一:首先查看一下關於UIEvent的定義 UIEvent是代表iOS系統中的一個事件,一個事件包含一個或多個的UITouch;UIEvent分為三類:UIEventTypeTouches觸摸事件(通過觸摸、手勢進行觸發,例如手指點擊、縮放)、UIEventTypeMotion運動事件,通過加速器進行 ...
一:首先查看一下關於UIEvent的定義
//事件類型 typedef NS_ENUM(NSInteger, UIEventType) { UIEventTypeTouches, UIEventTypeMotion, UIEventTypeRemoteControl, }; // 觸摸事件的類型 typedef NS_ENUM(NSInteger, UIEventSubtype) { UIEventSubtypeNone = 0, //搖晃 UIEventSubtypeMotionShake = 1, //播放 UIEventSubtypeRemoteControlPlay = 100, //暫停 UIEventSubtypeRemoteControlPause = 101, //停止 UIEventSubtypeRemoteControlStop = 102, //播放和暫停切換 UIEventSubtypeRemoteControlTogglePlayPause = 103, //下一首 UIEventSubtypeRemoteControlNextTrack = 104, //上一首 UIEventSubtypeRemoteControlPreviousTrack = 105, //開始後退 UIEventSubtypeRemoteControlBeginSeekingBackward = 106, //結束後退 UIEventSubtypeRemoteControlEndSeekingBackward = 107, //開始快進 UIEventSubtypeRemoteControlBeginSeekingForward = 108, //結束快進 UIEventSubtypeRemoteControlEndSeekingForward = 109, }; NS_CLASS_AVAILABLE_IOS(2_0) @interface UIEvent : NSObject //事件類型 @property(nonatomic,readonly) UIEventType type NS_AVAILABLE_IOS(3_0); // 觸摸事件的類型 @property(nonatomic,readonly) UIEventSubtype subtype NS_AVAILABLE_IOS(3_0); //事件的時間戳 @property(nonatomic,readonly) NSTimeInterval timestamp; //所有的觸摸 - (nullable NSSet <UITouch *> *)allTouches; //獲得UIWindow的觸摸 - (nullable NSSet <UITouch *> *)touchesForWindow:(UIWindow *)window; //獲得UIView的觸摸 - (nullable NSSet <UITouch *> *)touchesForView:(UIView *)view; //獲得事件中特定手勢的觸摸 - (nullable NSSet <UITouch *> *)touchesForGestureRecognizer:(UIGestureRecognizer *)gesture NS_AVAILABLE_IOS(3_2); //會將丟失的觸摸放到一個新的 UIEvent 數組中,你可以用 coalescedTouchesForTouch(_:) 方法來訪問 - (nullable NSArray <UITouch *> *)coalescedTouchesForTouch:(UITouch *)touch NS_AVAILABLE_IOS(9_0); //輔助UITouch的觸摸,預測發生了一系列主要的觸摸事件。這些預測可能不完全匹配的觸摸的真正的行為,因為它的移動,所以他們應該被解釋為一個估計。 - (nullable NSArray <UITouch *> *)predictedTouchesForTouch:(UITouch *)touch NS_AVAILABLE_IOS(9_0); @end
UIEvent是代表iOS系統中的一個事件,一個事件包含一個或多個的UITouch;UIEvent分為三類:UIEventTypeTouches觸摸事件(通過觸摸、手勢進行觸發,例如手指點擊、縮放)、UIEventTypeMotion運動事件,通過加速器進行觸發(例如手機晃動)、UIEventTypeRemoteControl遠程式控制制事件通過其他遠程設備觸發(例如耳機控制按鈕);
知識點1:在iOS中並不是所有的類都能處理接收並事件,只有繼承自UIResponder類的對象才能處理事件,(如我們常用的UIView、UIViewController、UIApplication都繼承自UIResponder,它們都能接收並處理事件)。在UIResponder中定義了上面三類事件相關的處理方法:
觸摸事件:
(void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event; 一根或多根手指開始觸摸屏幕時執行; (void)touchesMoved:(NSSet )touches withEvent:(UIEvent )event; 一根或多根手指在屏幕上移動時執行,註意此方法在移動過程中會重覆調用; (void)touchesEnded:(NSSet )touches withEvent:(UIEvent )event; 一根或多根手指觸摸結束離開屏幕時執行; (void)touchesCancelled:(NSSet )touches withEvent:(UIEvent )event; 觸摸意外取消時執行(例如正在觸摸時打入電話);
運動事件:
(void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event NS_AVAILABLE_IOS(3_0); 運動開始時執行; (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event NS_AVAILABLE_IOS(3_0); 運動結束後執行; (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event NS_AVAILABLE_IOS(3_0); 運動被意外取消時執行;
遠程式控制制事件:
(void)remoteControlReceivedWithEvent:(UIEvent *)event NS_AVAILABLE_IOS(4_0); 接收到遠程式控制制消息時執行;
知識點2:iOS「搖一搖」功能的實現(運動事件的運用)
- (void)viewDidLoad { [super viewDidLoad]; // 設置允許搖一搖功能 [UIApplication sharedApplication].applicationSupportsShakeToEdit = YES; // 並讓自己成為第一響應者 [self becomeFirstResponder]; return; } //搖一搖相關方法 - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event { NSLog(@"開始搖動"); return; } - (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event { NSLog(@"取消搖動"); return; } - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event { if (event.subtype == UIEventSubtypeMotionShake) { // 判斷是否是搖動結束 NSLog(@"搖動結束"); } return; }
另外:監聽運動事件前提,監聽對象必須成為第一響應者;在模擬器中運行時,可以通過「Hardware」-「Shake Gesture」來測試「搖一搖」功能
知識點3:一個搖動隨機圖片顯示的實例(運動事件的運用)
KCImageView.m #import "KCImageView.h" #define kImageCount 3 @implementation KCImageView - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.image = [self getImage]; } return self; } #pragma mark 設置控制項可以成為第一響應者 - (BOOL)canBecomeFirstResponder{ return YES; } #pragma mark 運動開始 - (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event{ //這裡只處理搖晃事件 if (motion == UIEventSubtypeMotionShake) { self.image = [self getImage]; } } #pragma mark 運動結束 - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event{ } #pragma mark 隨機取得圖片 - (UIImage *)getImage{ int index = arc4random() % kImageCount; NSString *imageName = [NSString stringWithFormat:@"avatar%i.png",index]; UIImage *image = [UIImage imageNamed:imageName]; return image; } @end KCShakeViewController.m #import "KCShakeViewController.h" #import "KCImageView.h" @interface KCShakeViewController (){ KCImageView *_imageView; } @end @implementation KCShakeViewController - (void)viewDidLoad { [super viewDidLoad]; } #pragma mark 視圖顯示時讓控制項變成第一響應者 - (void)viewDidAppear:(BOOL)animated{ _imageView = [[KCImageView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame]; _imageView.userInteractionEnabled = true; [self.view addSubview:_imageView]; [_imageView becomeFirstResponder]; } #pragma mark 視圖不顯示時註銷控制項第一響應者的身份 - (void)viewDidDisappear:(BOOL)animated{ [_imageView resignFirstResponder]; } @end
知識點4:遠程式控制制事件的運用
iOS遠程式控制制事件,是通過其他遠程設備觸發的(比如耳機控制按鈕),iOS遠程式控制制事件相關的只有-(void)remoteControlReceivedWithEvent:(UIEvent *)event;監聽遠程式控制制事件的前提:啟動遠程事件接收,調用[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];UI控制項同樣要求必須成為第一響應者【使用參考運動事件】,但如果是視圖控制器或UIApplication,就沒有要求成為第一響應者,應用程式必須是 當前音頻額控制者,目前iOS7給我們的遠程式控制制許可權僅限於音頻控制;
一個關於音樂遠程式控制制實例:
#import "ViewController.h" @interface ViewController (){ UIButton *_playButton; BOOL _isPlaying; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; [self initLayout]; } - (BOOL)canBecomeFirstResponder{ return NO; } - (void)viewDidAppear:(BOOL)animated{ [super viewDidAppear:animated]; NSURL *url = [NSURL URLWithString:@"http://stream.jewishmusicstream.com:8000"]; _player = [[AVPlayer alloc] initWithURL:url]; } #pragma mark 遠程式控制制事件 - (void)remoteControlReceivedWithEvent:(UIEvent *)event{ if(event.type == UIEventTypeRemoteControl){ switch (event.subtype) { case UIEventSubtypeRemoteControlPlay: [_player play]; _isPlaying = true; break; case UIEventSubtypeRemoteControlTogglePlayPause: [self btnClick:_playButton]; break; case UIEventSubtypeRemoteControlNextTrack: NSLog(@"Next..."); break; case UIEventSubtypeRemoteControlPreviousTrack: NSLog(@"Previous..."); break; case UIEventSubtypeRemoteControlBeginSeekingForward: NSLog(@"Begin seek forward..."); break; case UIEventSubtypeRemoteControlEndSeekingForward: NSLog(@"End seek forward..."); break; case UIEventSubtypeRemoteControlBeginSeekingBackward: NSLog(@"Begin seek backward..."); break; case UIEventSubtypeRemoteControlEndSeekingBackward: NSLog(@"End seek backward..."); break; default: break; } [self changeUIState]; } } #pragma mark 界面佈局 - (void)initLayout{ //專輯封面 UIImage *image = [UIImage imageNamed:@"wxl.jpg"]; CGRect *frame = [UIScreen mainScreen].applicationFrame; UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame]; imageView.image = image; imageView.contentMode = UIViewContentModeScaleAspectFill; [self.view addSubview:imageView]; //播放控制面板 UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 480, 320, 88)]; view.backgroundColor = [UIColor lightGrayColor]; view.alpha = 0.9; [self.view addSubview:view]; //添加播放按鈕 _playButton = [UIButton buttonWithType:UIButtonTypeCustom]; _playButton.bounds = CGRectMake(0, 0, 50, 50); CGFloat playBtnX = view.frame.size.width/2; CGFloat playBtnY = view.frame.size.height/2; _playButton.center = CGPointMake(playBtnX, playBtnY); [self changeUIState]; [_playButton addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside]; [view addSubview:_playButton]; } #pragma mark 界面狀態 - (void)changeUIState{ if(_isPlaying){ UIImage *pauseImage = [UIImage imageNamed:@"playing_btn_pause_n.png"]; UIImage *pauseImageH = [UIImage imageNamed:@"playing_btn_pause_h.png"]; [_playButton setImage:pauseImage forState:UIControlStateNormal]; [_playButton setImage:pauseImageH forState:UIControlStateHighlighted]; }else{ UIImage *playImage = [UIImage imageNamed:@"playing_btn_play_n.png"]; UIImage *playImageH = [UIImage imageNamed:@"playing_btn_play_h.png"]; [_playButton setImage:playImage forState:UIControlStateNormal]; [_playButton setImage:playImageH forState:UIControlStateHighlighted]; } } - (void)btnClick:(UIButton *)btn{ if (_isPlaying) { [_player pause]; }else{ [_player play]; } _isPlaying =! _isPlaying; [self changeUIState]; } @end
二:首先查看一下關於UITouch的定義
//觸摸事件在屏幕上有一個周期 typedef NS_ENUM(NSInteger, UITouchPhase) { UITouchPhaseBegan, //開始觸摸 UITouchPhaseMoved, //移動 UITouchPhaseStationary, //停留 UITouchPhaseEnded, //觸摸結束 UITouchPhaseCancelled, //觸摸中斷 }; //檢測是否支持3DTouch typedef NS_ENUM(NSInteger, UIForceTouchCapability) { UIForceTouchCapabilityUnknown = 0, //3D Touch檢測失敗 UIForceTouchCapabilityUnavailable = 1, //3D Touch不可用 UIForceTouchCapabilityAvailable = 2 //3D Touch可用 }; NS_CLASS_AVAILABLE_IOS(2_0) @interface UITouch : NSObject //觸摸產生或變化的時間戳 只讀 @property(nonatomic,readonly) NSTimeInterval timestamp; //觸摸周期內的各個狀態 @property(nonatomic,readonly) UITouchPhase phase; //短時間內點擊的次數 只讀 @property(nonatomic,readonly) NSUInteger tapCount; //獲取手指與屏幕的接觸半徑 IOS8以後可用 只讀 @property(nonatomic,readonly) CGFloat majorRadius NS_AVAILABLE_IOS(8_0); //獲取手指與屏幕的接觸半徑的誤差 IOS8以後可用 只讀 @property(nonatomic,readonly) CGFloat majorRadiusTolerance NS_AVAILABLE_IOS(8_0); //觸摸時所在的視窗 只讀 @property(nullable,nonatomic,readonly,strong) UIWindow *window; //觸摸時所在視圖 @property(nullable,nonatomic,readonly,strong) UIView *view; //獲取觸摸手勢 @property(nullable,nonatomic,readonly,copy) NSArray <UIGestureRecognizer *> *gestureRecognizers NS_AVAILABLE_IOS(3_2); //取得在指定視圖的位置 // 返回值表示觸摸在view上的位置 // 這裡返回的位置是針對view的坐標系的(以view的左上角為原點(0,0)) // 調用時傳入的view參數為nil的話,返回的是觸摸點在UIWindow的位置 - (CGPoint)locationInView:(nullable UIView *)view; //該方法記錄了前一個觸摸點的位置 - (CGPoint)previousLocationInView:(nullable UIView *)view; //獲取觸摸壓力值,一般的壓力感應值為1.0 IOS9 只讀 @property(nonatomic,readonly) CGFloat force NS_AVAILABLE_IOS(9_0); //獲取最大觸摸壓力值 @property(nonatomic,readonly) CGFloat maximumPossibleForce NS_AVAILABLE_IOS(9_0); @end
知識點1:觸摸時,圖片移動(實例)
- (void)viewDidLoad { UIImageView *image = [[UIImageView alloc] initWithFrame:CGRectMake(20.0, 50.0, 45.0, 45.0)]; image.image = [UIImage imageNamed:@"baby.png"]; image.tag = 100; [self.view addSubview:image]; [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; UIImageView *view1 = (UIImageView*)[self.view viewWithTag:100]; CGPoint point = [touch locationInView:self.view]; CGRect frame = view1.frame; frame.origin = point; view1.frame = frame; }
知識點2:創建可以拖動的視圖
CGPoint originalLocation; -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; originalLocation = [touch locationInView:self.view]; } -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; CGPoint currentLocation = [touch locationInView:self.view]; CGRect frame = self.view.frame; frame.origin.x += currentLocation.x-originalLocation.x; frame.origin.y += currentLocation.y-originalLocation.y; self.view.frame = frame; }
這裡先在touchesBegan中通過[touch locationInView:self.view]獲取手指觸摸在當前視圖上的位置,用CGPoint變數記錄,然後在手指移動事件touchesMoved方法中獲取觸摸對象當前位置,並通過於與原始位置的差值計算出移動偏移量,再設置當前視圖的位置。