UIControl을 선택 하위 클래스 화하는 방법은 무엇입니까?
나는 공통 UIButton
될 그런 것입니다. UIControl
직접 하위 클래스 를 만들고 나만의 매우 특별한 컨트롤을 만들고 싶습니다.
그러나 어떤 언어가 재정의하는 어떤 메서드도 호출되지 않습니다. 오리-액션 물건이 작동하고 오리지날 액션 메시지를받습니다. 그러나 내 UIControl
하위 클래스 내에서 터치 좌표를 잡아야하는데, 그렇게하는 유일한 방법은 다음 사람을 재정의하는 것입니다.
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
NSLog(@"begin touch track");
return YES;
}
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
NSLog(@"continue touch track");
return YES;
}
그것들은 호출되지 않았고 UIControl
초기화되고 UIView
, initWithFrame:
.
난 항상 사용할 수있는 모든 예제는 사용 UIButton
또는 UISlider
서브 클래스를위한 기지로,하지만 나는 가까이 가고 싶어 UIControl
그 소스이기 때문에 내가 원하는 것을 위해 : 빠른 속도와 지연되지 않은 터치 좌표를.
나는이 질문이 고대라는 뜻의 문제가 있다고 생각했습니다.
컨트롤이 전혀 어떤 하위 뷰가있는 경우 beginTrackingWithTouch
, touchesBegan
그 파단 터치 이벤트를 삼키는 있기 때문에, 등 호출하지 않을 수 있습니다.
당신이 핸들 접촉에 그 파단을 원하지 않는 경우 ,은 설정할 수 당신 있습니다 userInteractionEnabled
에 NO
파단이 단지를 통해 이벤트를 전달하므로. 다음 그런 touchesBegan/touchesEnded
거기에서 모든 터치를 재정의 하고 관리 할 수 있습니다 .
도움이 되셨기를 바랍니다.
빠른
이것들은 하위 클래스를 만드는 두 가지 이상의 방법 UIControl
입니다. 상위 뷰가 터치 이벤트에 반응하거나 컨트롤에서 다른 데이터를 가져와야하는 경우 일반적으로 (1) 대상 또는 (2) 재정의 된 터치 이벤트가있는 대리자 패턴을 사용하여 수행됩니다. (3) 제스처 인식기로 동일한 작업을 수행하는 방법 보여줍니다. 다음 애니메이션처럼 작동합니다.
다음 방법 중 하나만 선택하면됩니다.
방법 1 : 대상 추가
UIControl
서브 클래스는 이미 내장 목표에 대한 지원을하고있다. 당신은 부모에게 많은 데이터를 필요로하는 경우,이 아마 당신이 원하는 방법입니다.
MyCustomControl.swift
import UIKit
class MyCustomControl: UIControl {
// You don't need to do anything special in the control for targets to work.
}
ViewController.swift
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var myCustomControl: MyCustomControl!
@IBOutlet weak var trackingBeganLabel: UILabel!
@IBOutlet weak var trackingEndedLabel: UILabel!
@IBOutlet weak var xLabel: UILabel!
@IBOutlet weak var yLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Add the targets
// Whenever the given even occurs, the action method will be called
myCustomControl.addTarget(self, action: #selector(touchedDown), forControlEvents: UIControlEvents.TouchDown)
myCustomControl.addTarget(self, action: #selector(didDragInsideControl(_:withEvent:)),
forControlEvents: UIControlEvents.TouchDragInside)
myCustomControl.addTarget(self, action: #selector(touchedUpInside), forControlEvents: UIControlEvents.TouchUpInside)
}
// MARK: - target action methods
func touchedDown() {
trackingBeganLabel.text = "Tracking began"
}
func touchedUpInside() {
trackingEndedLabel.text = "Tracking ended"
}
func didDragInsideControl(control: MyCustomControl, withEvent event: UIEvent) {
if let touch = event.touchesForView(control)?.first {
let location = touch.locationInView(control)
xLabel.text = "x: \(location.x)"
yLabel.text = "y: \(location.y)"
}
}
}
메모
- 조치 메소드 이름에는 특별한 것이 없습니다. 나는 그들에게 무엇이든 부를 수 있었다. 한 곳과 똑같이 메소드 이름의 철을 추가합니다. 개체 충돌이 발생합니다.
- 두 개의 개의 콜론은
didDragInsideControl:withEvent:
두 개의 매개 변수가didDragInsideControl
메소드 에 전달됨을 의미합니다 . 콜론을 추가하는 것을 잊었거나 올바른 수의 변수가없는 경우 충돌이 발생합니다. - 이벤트에 대한 도움을 주신이 답변 에 감사드립니다
TouchDragInside
.
다른 데이터 전달
사용자 지정 컨트롤에 값이있는 경우
class MyCustomControl: UIControl {
var someValue = "hello"
}
대상 작업 메서드에서 액세스하려는 경우 컨트롤에 대한 참조를 많은 수 있습니다. 대상을 접근 할 때 작업 메소드 콜론을 추가합니다. 예를 들면 :
myCustomControl.addTarget(self, action: #selector(touchedDown), forControlEvents: UIControlEvents.TouchDown)
touchedDown:
콜론이없고 콜론 이 점에 유의하십시오 touchedDown
. 콜론은 매개 변수가 조치 메소드로 전달됨을 의미합니다. 조치 메소드에서 매개 변수가 서브 UIControl
클래스에 대한 참조임을 지정 합니다. 이 참조를 사용하면 컨트롤에서 데이터를 사용할 수 있습니다.
func touchedDown(control: MyCustomControl) {
trackingBeganLabel.text = "Tracking began"
// now you have access to the public properties and methods of your control
print(control.someValue)
}
방법 2 : 패턴 수단 및 터치 이벤트 재정의
서브 클래 싱 UIControl
은 다음 메소드에 대한 액세스를 제공합니다.
beginTrackingWithTouch
손가락이 컨트롤의 범위 내에서 처음 터치 다운 될 때 호출됩니다.continueTrackingWithTouch
손가락이 컨트롤을 가로 질러 그리고 심지어 컨트롤의 경계를 반복적으로 호출합니다.endTrackingWithTouch
손가락이 화면에서 뗄 때 호출됩니다.
터치 이벤트에 대한 특별한 제어가 필요하거나 부모와 관련된 데이터 통신이 많은 경우이 방법이 대상을 추가하는 것보다 더 많은 일 수 있습니다.
방법은 다음과 가변합니다.
MyCustomControl.swift
import UIKit
// These are out self-defined rules for how we will communicate with other classes
protocol ViewControllerCommunicationDelegate: class {
func myTrackingBegan()
func myTrackingContinuing(location: CGPoint)
func myTrackingEnded()
}
class MyCustomControl: UIControl {
// whichever class wants to be notified of the touch events must set the delegate to itself
weak var delegate: ViewControllerCommunicationDelegate?
override func beginTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) -> Bool {
// notify the delegate (i.e. the view controller)
delegate?.myTrackingBegan()
// returning true means that future events (like continueTrackingWithTouch and endTrackingWithTouch) will continue to be fired
return true
}
override func continueTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) -> Bool {
// get the touch location in our custom control's own coordinate system
let point = touch.locationInView(self)
// Update the delegate (i.e. the view controller) with the new coordinate point
delegate?.myTrackingContinuing(point)
// returning true means that future events will continue to be fired
return true
}
override func endTrackingWithTouch(touch: UITouch?, withEvent event: UIEvent?) {
// notify the delegate (i.e. the view controller)
delegate?.myTrackingEnded()
}
}
ViewController.swift
이것이 뷰 컨트롤러가 델리게이트로 설정되고 사용자 지정 컨트롤의 터치 이벤트에 응답하는 방법입니다.
import UIKit
class ViewController: UIViewController, ViewControllerCommunicationDelegate {
@IBOutlet weak var myCustomControl: MyCustomControl!
@IBOutlet weak var trackingBeganLabel: UILabel!
@IBOutlet weak var trackingEndedLabel: UILabel!
@IBOutlet weak var xLabel: UILabel!
@IBOutlet weak var yLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
myCustomControl.delegate = self
}
func myTrackingBegan() {
trackingBeganLabel.text = "Tracking began"
}
func myTrackingContinuing(location: CGPoint) {
xLabel.text = "x: \(location.x)"
yLabel.text = "y: \(location.y)"
}
func myTrackingEnded() {
trackingEndedLabel.text = "Tracking ended"
}
}
메모
- 대리자 패턴에 대한 자세한 내용은 이 답변을 참조하십시오 .
오직 사용되는 경우에는 대리 튼 사용되는 경우에는 없습니다.
print
가 어떻게 호출 이벤트되는지 보여주기 위해 방금 성명을 추가 할 수있었습니다 . 이 경우 코드는 다음과 같이 단순화됩니다.import UIKit class MyCustomControl: UIControl { override func beginTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) -> Bool { print("Began tracking") return true } override func continueTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) -> Bool { let point = touch.locationInView(self) print("x: \(point.x), y: \(point.y)") return true } override func endTrackingWithTouch(touch: UITouch?, withEvent event: UIEvent?) { print("Ended tracking") } }
방법 3 : 제스처 인식기 사용
제스처 인식기 추가는 모든보기에서 수행 할 수 UIControl
있습니다. 상단의 예와 결과를 얻을 수 UIPanGestureRecognizer
있습니다. 그런 다음 이벤트가 발생했을 때 다양한 상태를 테스트하여 무슨 일이 일어나고 있는지 확인할 수 있습니다.
MyCustomControl.swift
import UIKit
class MyCustomControl: UIControl {
// nothing special is required in the control to make it work
}
ViewController.swift
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var myCustomControl: MyCustomControl!
@IBOutlet weak var trackingBeganLabel: UILabel!
@IBOutlet weak var trackingEndedLabel: UILabel!
@IBOutlet weak var xLabel: UILabel!
@IBOutlet weak var yLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// add gesture recognizer
let gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(gestureRecognized(_:)))
myCustomControl.addGestureRecognizer(gestureRecognizer)
}
// gesture recognizer action method
func gestureRecognized(gesture: UIPanGestureRecognizer) {
if gesture.state == UIGestureRecognizerState.Began {
trackingBeganLabel.text = "Tracking began"
} else if gesture.state == UIGestureRecognizerState.Changed {
let location = gesture.locationInView(myCustomControl)
xLabel.text = "x: \(location.x)"
yLabel.text = "y: \(location.y)"
} else if gesture.state == UIGestureRecognizerState.Ended {
trackingEndedLabel.text = "Tracking ended"
}
}
}
메모
- .NET의 작업 메서드 이름 뒤에 콜론을 추가하는 것을 잊지 마십시오
action: "gestureRecognized:"
. 콜론은 매개 변수가 전달되고 있음을 의미합니다. - 컨트롤에서 데이터를 가져와야하는 경우 위의 방법 2에서와 같이 대리자 패턴을 구현할 수 있습니다.
나는이 문제에 대한 해결책을 찾기 위해 길고 열심히 노력해 왔으며 하나도 없다고 생각합니다. 그러나, 문서의 더 정밀한 점검에 나는 그것이 오해 할 수 있다고 생각 begintrackingWithTouch:withEvent:
하고 continueTrackingWithTouch:withEvent:
전혀 호출로되어있다 ...
UIControl
문서는 말한다 :
UIControl
두 가지 기본 이유로 서브 클래스 를 확장 할 수 있습니다 .특정 이벤트의 대상에 대한 작업 메시지 전달을 관찰하거나 수정하려면이 작업을 수행하려면을 재정의
sendAction:to:forEvent:
하고 전달 된 선택기, 대상 개체 또는 "Note"비트 마스크를 평가 한 다음 필요에 따라 진행합니다.이, 재정 다음 방법 중 하나 또는 모두를 수행하려면 (하이라이트 모양을 변경, 예를 들어) 사용자 추적 동작을 제공하려면
beginTrackingWithTouch:withEvent:
,continueTrackingWithTouch:withEvent:
,endTrackingWithTouch:withEvent:
.
내 관점에서 그다지 명확하지 않은 이것의 중요한 부분은 UIControl
하위 클래스 를 확장하고 싶을 수도 있다는 것 UIControl
입니다. 직접 확장하고 싶지 않을 수도 있습니다. 그것은 가능성이 있습니다 beginTrackingWithTouch:withEvent:
및 continuetrackingWithTouch:withEvent:
터치에 대한 응답으로 호출 얻을 것을 생각하지 않습니다 UIControl
직접 서브 클래스가 너무 그들에게 전화 해야하는 자신의 서브 클래스가 추적을 모니터링 할 수 있습니다.
그래서 이것에 대한 나의 해결책 은 다음과 같이 재정의 touchesBegan:withEvent:
하고 touchesMoved:withEvent:
거기에서 호출하는 것입니다. 멀티 터치는이 컨트롤에 대해 활성화되어 있지 않으며 터치가 종료되고 취소 된 이벤트를 터치하는 것에 대해서는 신경 쓰지 않지만 완전하고 철저하게하려면이를 구현해야합니다.
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
[super touchesBegan:touches withEvent:event];
// Get the only touch (multipleTouchEnabled is NO)
UITouch* touch = [touches anyObject];
// Track the touch
[self beginTrackingWithTouch:touch withEvent:event];
}
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
[super touchesMoved:touches withEvent:event];
// Get the only touch (multipleTouchEnabled is NO)
UITouch* touch = [touches anyObject];
// Track the touch
[self continueTrackingWithTouch:touch withEvent:event];
}
UIControlEvent*
컨트롤과 관련된 모든 메시지를 보내야합니다.이 메시지 sendActionsForControlEvents:
는 슈퍼 메서드에서 호출 될 수 있지만 테스트하지는 않았습니다.
touchesBegan / touchesEnded / touchesMoved에 [수퍼] 호출을 추가하는 것을 잊었다 고 생각합니다. 같은 방법
(BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
(BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
다음과 같이 touchesBegan / touchesEnded를 재정의하면 작동하지 않습니다.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(@"Touches Began");
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(@"Touches Moved");
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(@"Touches Ended");
}
그러나! 방법이 다음과 같으면 모두 잘 작동합니다.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
NSLog(@"Touches Began");
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
NSLog(@"Touches Moved");
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
NSLog(@"Touches Ended");
}
내가 자주 사용하는 가장 간단한 방법은 UIControl을 확장하는 것이지만 상속 된 addTarget 메서드를 사용하여 다양한 이벤트에 대한 콜백을 수신하는 것입니다. 핵심은 실제 이벤트에 대한 자세한 정보 (예 : 이벤트가 발생한 위치)를 찾을 수 있도록 보낸 사람과 이벤트를 모두 수신하는 것입니다.
따라서 간단히 UIControl을 하위 클래스로 만든 다음 init 메서드에서 (nibs를 사용하는 경우 initWithCoder도 설정되어 있는지 확인하십시오) 다음을 추가하십시오.
[self addTarget:self action:@selector(buttonPressed:forEvent:) forControlEvents:UIControlEventTouchUpInside];
물론 UIControlEventAllTouchEvents를 포함한 모든 표준 제어 이벤트를 선택할 수 있습니다. 선택자는 두 개의 개체를 전달받습니다. 첫 번째는 컨트롤입니다. 두 번째는 이벤트에 대한 정보입니다. 다음은 사용자가 왼쪽과 오른쪽을 눌렀는지 여부에 따라 터치 이벤트를 사용하여 버튼을 전환하는 예입니다.
- (IBAction)buttonPressed:(id)sender forEvent:(UIEvent *)event
{
if (sender == self.someControl)
{
UITouch* touch = [[event allTouches] anyObject];
CGPoint p = [touch locationInView:self.someControl];
if (p.x < self. someControl.frame.size.width / 2.0)
{
// left side touch
}
else
{
// right side touch
}
}
}
물론 이것은 매우 단순한 제어를위한 것이며 충분한 기능을 제공하지 않는 지점에 도달 할 수 있지만, 이것은 지금까지 사용자 정의 제어에 대한 내 모든 목적을 위해 작동했으며 일반적으로 동일한 것에 관심이 있기 때문에 사용하기 매우 쉽습니다. UIControl이 이미 지원하는 제어 이벤트 (터치 업, 드래그 등)
사용자 지정 컨트롤의 코드 샘플 : Custom UISwitch (참고 : buttonPressed : forEvent : 선택기에 등록되지 않지만 위 코드에서 확인할 수 있음)
beginTrackingWithTouch
및에 응답하지 않는 UIControl에 문제가 발생했습니다 continueTrackingWithTouch
.
내 문제는 initWithFrame:CGRectMake()
프레임을 작게 만들었을 때 (반응하는 영역) 작동하는 지점이 몇 점 밖에 없다는 것입니다. 나는 프레임을 컨트롤과 같은 크기로 만든 다음 컨트롤의 아무 곳이나 누르면 응답했습니다.
Obj C
2014 https://www.objc.io/issues/13-architecture/behaviors/ 에서 관련 기사를 찾았습니다 .
흥미로운 점은 IB를 사용하고 이벤트 처리 로직을 지정된 객체 (비헤이비어라고 부름)에 캡슐화하여 뷰 / 뷰 컨트롤러에서 로직을 제거하여 더 가볍게 만드는 것입니다.
- Github 샘플 케이스 : https://github.com/krzysztofzablocki/BehavioursExample
참고 URL : https://stackoverflow.com/questions/1245248/how-to-correctly-subclass-uicontrol
'ProgramingTip' 카테고리의 다른 글
Subversion 대신 Perforce를 사용하면 어떤 이점이 있습니까? (0) | 2020.11.17 |
---|---|
xpath : 값에 노드가 포함 된 주어진 속성이있는 노드를 찾습니다. (0) | 2020.11.17 |
Java의 공유에서 새 개체 만들기 (0) | 2020.11.17 |
MATLAB에서 함수 인수의 이름 / 값 쌍을 처리하는 방법 (0) | 2020.11.17 |
R Markdown을 HTML로 변환하는 방법? (0) | 2020.11.17 |