연습: Xamarin.iOS에서 Touch 사용
이 연습에서는 다양한 종류의 터치 이벤트에 응답하는 코드를 작성하는 방법을 보여 줍니다. 각 예제는 별도의 화면에 포함됩니다.
- 터치 샘플 – 터치 이벤트에 응답하는 방법.
- Gesture Recognizer 샘플 – 기본 제공 제스처 인식기를 사용하는 방법
- 사용자 지정 제스처 인식기 샘플 – 사용자 지정 제스처 인식기를 빌드하는 방법입니다.
각 섹션에는 코드를 처음부터 작성하는 지침이 포함되어 있습니다.
아래 지침에 따라 스토리보드에 코드를 추가하고 iOS에서 사용할 수 있는 다양한 유형의 터치 이벤트에 대해 알아봅니다.
터치 샘플
이 샘플에서는 일부 터치 API를 보여 줍니다. 터치 이벤트를 구현하는 데 필요한 코드를 추가하려면 다음 단계를 수행합니다.
프로젝트 Touch_Start 엽니다. 먼저 프로젝트를 실행하여 모든 것이 괜찮은지 확인하고 터치 샘플 단추를 터치합니다. 다음과 유사한 화면이 표시됩니다(단추는 작동하지 않지만).
파일 TouchViewController.cs 편집하고 클래스에 다음 두 개의 인스턴스 변수를
TouchViewController
추가합니다.#region Private Variables private bool imageHighlighted = false; private bool touchStartedInside; #endregion
TouchesBegan
아래 코드와 같이 메서드를 구현합니다.public override void TouchesBegan(NSSet touches, UIEvent evt) { base.TouchesBegan(touches, evt); // If Multitouch is enabled, report the number of fingers down TouchStatus.Text = string.Format ("Number of fingers {0}", touches.Count); // Get the current touch UITouch touch = touches.AnyObject as UITouch; if (touch != null) { // Check to see if any of the images have been touched if (TouchImage.Frame.Contains(touch.LocationInView(TouchView))) { // Fist image touched TouchImage.Image = UIImage.FromBundle("TouchMe_Touched.png"); TouchStatus.Text = "Touches Began"; } else if (touch.TapCount == 2 && DoubleTouchImage.Frame.Contains(touch.LocationInView(TouchView))) { // Second image double-tapped, toggle bitmap if (imageHighlighted) { DoubleTouchImage.Image = UIImage.FromBundle("DoubleTapMe.png"); TouchStatus.Text = "Double-Tapped Off"; } else { DoubleTouchImage.Image = UIImage.FromBundle("DoubleTapMe_Highlighted.png"); TouchStatus.Text = "Double-Tapped On"; } imageHighlighted = !imageHighlighted; } else if (DragImage.Frame.Contains(touch.LocationInView(View))) { // Third image touched, prepare to drag touchStartedInside = true; } } }
이 메서드는 개체에 대해
UITouch
검사 작동하며, 개체가 있는 경우 터치가 발생한 위치에 따라 몇 가지 작업을 수행합니다.- TouchImage 내부 – 레이블에 텍스트를
Touches Began
표시하고 이미지를 변경합니다. - DoubleTouchImage 내부 – 제스처가 두 번 탭된 경우 표시되는 이미지를 변경합니다.
- DragImage 내부 – 터치가 시작되었음을 나타내는 플래그를 설정합니다. 이 메서드
TouchesMoved
는 다음 단계에서 볼 수 있듯이 이 플래그를 사용하여 화면 주위로 이동해야 하는지DragImage
여부를 결정합니다.
위의 코드는 개별 터치만 처리하며 사용자가 화면에서 손가락을 이동하는 경우에도 동작이 없습니다. 이동에 응답하려면 아래 코드와 같이 구현
TouchesMoved
합니다.public override void TouchesMoved(NSSet touches, UIEvent evt) { base.TouchesMoved(touches, evt); // get the touch UITouch touch = touches.AnyObject as UITouch; if (touch != null) { //==== IMAGE TOUCH if (TouchImage.Frame.Contains(touch.LocationInView(TouchView))) { TouchStatus.Text = "Touches Moved"; } //==== IMAGE DRAG // check to see if the touch started in the drag me image if (touchStartedInside) { // move the shape float offsetX = touch.PreviousLocationInView(View).X - touch.LocationInView(View).X; float offsetY = touch.PreviousLocationInView(View).Y - touch.LocationInView(View).Y; DragImage.Frame = new RectangleF(new PointF(DragImage.Frame.X - offsetX, DragImage.Frame.Y - offsetY), DragImage.Frame.Size); } } }
이 메서드는 개체를
UITouch
가져오고 검사 터치가 발생한 위치를 확인합니다. 터치가 발생한TouchImage
경우 터치 이동 텍스트가 화면에 표시됩니다.true이면
touchStartedInside
사용자가 손가락을 대DragImage
고 이동하는 것을 알 수 있습니다. 사용자가 화면을 손가락으로 이동하면 코드가 이동합니다DragImage
.- TouchImage 내부 – 레이블에 텍스트를
사용자가 화면에서 손가락을 떼거나 iOS에서 터치 이벤트를 취소할 때 사례를 처리해야 합니다. 이를 위해 아래와 같이 구현
TouchesEnded
합니다TouchesCancelled
.public override void TouchesCancelled(NSSet touches, UIEvent evt) { base.TouchesCancelled(touches, evt); // reset our tracking flags touchStartedInside = false; TouchImage.Image = UIImage.FromBundle("TouchMe.png"); TouchStatus.Text = ""; } public override void TouchesEnded(NSSet touches, UIEvent evt) { base.TouchesEnded(touches, evt); // get the touch UITouch touch = touches.AnyObject as UITouch; if (touch != null) { //==== IMAGE TOUCH if (TouchImage.Frame.Contains(touch.LocationInView(TouchView))) { TouchImage.Image = UIImage.FromBundle("TouchMe.png"); TouchStatus.Text = "Touches Ended"; } } // reset our tracking flags touchStartedInside = false; }
이러한 두 방법 모두 플래그를 false로
touchStartedInside
다시 설정합니다.TouchesEnded
화면에도 표시됩니다TouchesEnded
.이 시점에서 터치 샘플 화면이 완료됩니다. 다음 스크린샷과 같이 각 이미지와 상호 작용할 때 화면이 어떻게 변경되는지 확인합니다.
제스처 인식기 샘플
이전 섹션에서는 터치 이벤트를 사용하여 화면 주위에 개체를 끄는 방법을 보여 줍니다. 이 섹션에서는 터치 이벤트를 제거하고 다음 제스처 인식기를 사용하는 방법을 보여 줍니다.
UIPanGestureRecognizer
화면 주위에 이미지를 끌기 위한 것입니다.UITapGestureRecognizer
화면의 두 번 탭에 응답하는 것입니다.
제스처 인식기를 구현하려면 다음 단계를 수행합니다.
파일 GestureViewController.cs 편집하고 다음 인스턴스 변수를 추가합니다.
#region Private Variables private bool imageHighlighted = false; private RectangleF originalImageFrame = RectangleF.Empty; #endregion
이미지의 이전 위치를 추적하려면 이 인스턴스 변수가 필요합니다. 이동 제스처 인식기 화면에서 이미지를 다시 그리는 데 필요한 오프셋을 계산 하는 값을 사용
originalImageFrame
합니다.컨트롤러에 다음 메서드를 추가합니다.
private void WireUpDragGestureRecognizer() { // Create a new tap gesture UIPanGestureRecognizer gesture = new UIPanGestureRecognizer(); // Wire up the event handler (have to use a selector) gesture.AddTarget(() => HandleDrag(gesture)); // to be defined // Add the gesture recognizer to the view DragImage.AddGestureRecognizer(gesture); }
이 코드는 인스턴스를
UIPanGestureRecognizer
인스턴스화하고 뷰에 추가합니다. 메서드의HandleDrag
형태로 제스처에 대상을 할당합니다. 이 메서드는 다음 단계에서 제공됩니다.HandleDrag를 구현하려면 컨트롤러에 다음 코드를 추가합니다.
private void HandleDrag(UIPanGestureRecognizer recognizer) { // If it's just began, cache the location of the image if (recognizer.State == UIGestureRecognizerState.Began) { originalImageFrame = DragImage.Frame; } // Move the image if the gesture is valid if (recognizer.State != (UIGestureRecognizerState.Cancelled | UIGestureRecognizerState.Failed | UIGestureRecognizerState.Possible)) { // Move the image by adding the offset to the object's frame PointF offset = recognizer.TranslationInView(DragImage); RectangleF newFrame = originalImageFrame; newFrame.Offset(offset.X, offset.Y); DragImage.Frame = newFrame; } }
위의 코드는 먼저 제스처 인식기의 상태를 검사 다음 화면 주위로 이미지를 이동합니다. 이 코드를 사용하면 컨트롤러에서 이제 화면 주위에서 하나의 이미지 끌기를 지원할 수 있습니다.
DoubleTouchImage에 표시되는 이미지를 변경하는 A
UITapGestureRecognizer
를 추가합니다. 컨트롤러에 다음 메서드를GestureViewController
추가합니다.private void WireUpTapGestureRecognizer() { // Create a new tap gesture UITapGestureRecognizer tapGesture = null; // Report touch Action action = () => { TouchStatus.Text = string.Format("Image touched at: {0}",tapGesture.LocationOfTouch(0, DoubleTouchImage)); // Toggle the image if (imageHighlighted) { DoubleTouchImage.Image = UIImage.FromBundle("DoubleTapMe.png"); } else { DoubleTouchImage.Image = UIImage.FromBundle("DoubleTapMe_Highlighted.png"); } imageHighlighted = !imageHighlighted; }; tapGesture = new UITapGestureRecognizer(action); // Configure it tapGesture.NumberOfTapsRequired = 2; // Add the gesture recognizer to the view DoubleTouchImage.AddGestureRecognizer(tapGesture); }
이 코드는 해당 코드
UIPanGestureRecognizer
와 매우 유사하지만 대상에 대리자를 사용하는 대신 사용Action
중입니다.마지막으로 해야 할 일은 방금 추가한 메서드를 호출하도록 수정
ViewDidLoad
하는 것입니다. ViewDidLoad를 변경하여 다음 코드와 유사하게 변경합니다.public override void ViewDidLoad() { base.ViewDidLoad(); Title = "Gesture Recognizers"; // Save initial state originalImageFrame = DragImage.Frame; WireUpTapGestureRecognizer(); WireUpDragGestureRecognizer(); }
의 값을
originalImageFrame
초기화합니다.애플리케이션을 실행하고 두 이미지와 상호 작용합니다. 다음 스크린샷은 이러한 상호 작용의 한 예입니다.
사용자 지정 제스처 인식기
이 섹션에서는 이전 섹션의 개념을 적용하여 사용자 지정 제스처 인식기를 빌드합니다. 사용자 지정 제스처 인식기는 서브클래스를 UIGestureRecognizer
수행하고 사용자가 화면에서 "V"를 그린 다음 비트맵을 토글할 때 인식합니다. 다음 스크린샷은 이 화면의 예입니다.
사용자 지정 제스처 인식기를 만들려면 다음 단계를 수행합니다.
프로젝트에
CheckmarkGestureRecognizer
새 클래스를 추가하고 다음 코드와 같이 표시합니다.using System; using CoreGraphics; using Foundation; using UIKit; namespace Touch { public class CheckmarkGestureRecognizer : UIGestureRecognizer { #region Private Variables private CGPoint midpoint = CGPoint.Empty; private bool strokeUp = false; #endregion #region Override Methods /// <summary> /// Called when the touches end or the recognizer state fails /// </summary> public override void Reset() { base.Reset(); strokeUp = false; midpoint = CGPoint.Empty; } /// <summary> /// Is called when the fingers touch the screen. /// </summary> public override void TouchesBegan(NSSet touches, UIEvent evt) { base.TouchesBegan(touches, evt); // we want one and only one finger if (touches.Count != 1) { base.State = UIGestureRecognizerState.Failed; } Console.WriteLine(base.State.ToString()); } /// <summary> /// Called when the touches are cancelled due to a phone call, etc. /// </summary> public override void TouchesCancelled(NSSet touches, UIEvent evt) { base.TouchesCancelled(touches, evt); // we fail the recognizer so that there isn't unexpected behavior // if the application comes back into view base.State = UIGestureRecognizerState.Failed; } /// <summary> /// Called when the fingers lift off the screen /// </summary> public override void TouchesEnded(NSSet touches, UIEvent evt) { base.TouchesEnded(touches, evt); // if (base.State == UIGestureRecognizerState.Possible && strokeUp) { base.State = UIGestureRecognizerState.Recognized; } Console.WriteLine(base.State.ToString()); } /// <summary> /// Called when the fingers move /// </summary> public override void TouchesMoved(NSSet touches, UIEvent evt) { base.TouchesMoved(touches, evt); // if we haven't already failed if (base.State != UIGestureRecognizerState.Failed) { // get the current and previous touch point CGPoint newPoint = (touches.AnyObject as UITouch).LocationInView(View); CGPoint previousPoint = (touches.AnyObject as UITouch).PreviousLocationInView(View); // if we're not already on the upstroke if (!strokeUp) { // if we're moving down, just continue to set the midpoint at // whatever point we're at. when we start to stroke up, it'll stick // as the last point before we upticked if (newPoint.X >= previousPoint.X && newPoint.Y >= previousPoint.Y) { midpoint = newPoint; } // if we're stroking up (moving right x and up y [y axis is flipped]) else if (newPoint.X >= previousPoint.X && newPoint.Y <= previousPoint.Y) { strokeUp = true; } // otherwise, we fail the recognizer else { base.State = UIGestureRecognizerState.Failed; } } } Console.WriteLine(base.State.ToString()); } #endregion } }
Reset 메서드는 속성이
State
둘 중 하나Recognized
또는Ended
.로 변경되면 호출됩니다. 사용자 지정 제스처 인식기에서 설정된 내부 상태를 다시 설정해야 합니다. 이제 다음 번에 사용자가 애플리케이션과 상호 작용할 때 클래스가 새로 시작되고 제스처를 다시 인식할 준비가 될 수 있습니다.이제 사용자 지정 제스처 인식기(
CheckmarkGestureRecognizer
)를 정의했으므로 CustomGestureViewController.cs 파일을 편집하고 다음 두 개의 인스턴스 변수를 추가합니다.#region Private Variables private bool isChecked = false; private CheckmarkGestureRecognizer checkmarkGesture; #endregion
제스처 인식기를 인스턴스화하고 구성하려면 컨트롤러에 다음 메서드를 추가합니다.
private void WireUpCheckmarkGestureRecognizer() { // Create the recognizer checkmarkGesture = new CheckmarkGestureRecognizer(); // Wire up the event handler checkmarkGesture.AddTarget(() => { if (checkmarkGesture.State == (UIGestureRecognizerState.Recognized | UIGestureRecognizerState.Ended)) { if (isChecked) { CheckboxImage.Image = UIImage.FromBundle("CheckBox_Unchecked.png"); } else { CheckboxImage.Image = UIImage.FromBundle("CheckBox_Checked.png"); } isChecked = !isChecked; } }); // Add the gesture recognizer to the view View.AddGestureRecognizer(checkmarkGesture); }
다음 코드 조각과 같이 호출
WireUpCheckmarkGestureRecognizer
되도록 편집ViewDidLoad
합니다.public override void ViewDidLoad() { base.ViewDidLoad(); // Wire up the gesture recognizer WireUpCheckmarkGestureRecognizer(); }
애플리케이션을 실행하고 화면에 "V"를 그려 봅니다. 다음 스크린샷과 같이 변경 내용이 표시되는 이미지가 표시됩니다.
위의 세 섹션에서는 터치 이벤트, 기본 제공 제스처 인식기 또는 사용자 지정 제스처 인식기를 사용하여 iOS에서 터치 이벤트에 응답하는 다양한 방법을 보여 줍니다.