你真的瞭解UIEvent、UITouch嗎?

来源:http://www.cnblogs.com/wujy/archive/2016/08/30/5820825.html
-Advertisement-
Play Games

一:首先查看一下關於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方法中獲取觸摸對象當前位置,並通過於與原始位置的差值計算出移動偏移量,再設置當前視圖的位置。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一:UIViewController模態跳轉 知識點1: a: 在官方文檔中,建議這兩者之間通過delegate實現交互。例如使用UIImagePickerController從系統相冊選取照片或者拍照,imagePickerController和彈出它的VC之間就通過UIImagePickerCo ...
  • 廢話不多說,先看上效果,由於動畫錄製的時候幀率限制,只能將動畫放慢了進行錄製,更容易看到效果 這是點擊開始之後代碼 由於動畫使由多個動畫組成,所以第一個動畫完畢之後自動再次開始一個動畫 先解釋一下動畫執行過程 第一步是通過CABasicAnimation 對照片進行縮放 第二步是通過CAKeyfra ...
  • 很久沒有總結,回頭看了一下過期的賬號,記錄的內容少之又少。最近有一些時間,想好好總結記錄一下。 由於很久沒有記錄,想寫的東西很多又很雜,想了一下,一篇一篇羅列知識點和經驗,還不如寫一個系列,記錄一個應用的開發流程和經歷。 主線就是一個應用的構建和開發過程,期間再針對部分節點進行分析和探討。 這篇的標 ...
  • 一:首先瞭解一下生命周期圖 二:UIViewController 生命周期介紹 1.通過alloc init 分配記憶體,初始化controller. 2.loadView loadView方法預設實現[super loadView] 如果在初始化controller時指定了xib文件名,就會根據傳入 ...
  • 一:首先查看一下關於UIGestureRecognizer的定義 UIGestureRecognizer是一個抽象類,定義了所有手勢的基本行為,使用它的子類才能處理具體的手勢 知識點1:關於UIGestureRecognizer的子類如下(下麵這些才是我們平常會直接運用到的類): 實例如下: 二:關 ...
  • 1.1 重新規劃android的項目結構 重新規劃android的目錄結構分兩步: 1.建立AndroidLib類庫,將與業務無關的邏輯轉移到AndroidLib。 acitivity存放的是跟業務無關的Activity基類 cache包存放的是緩存數據和圖片相關的處理 net包存放的是網路底層封裝 ...
  • 在項目中總是需要緩存一些網路請求數據以減輕伺服器壓力,業內也有許多優秀的開源的解決方案。通常的緩存方案都是由記憶體緩存和磁碟緩存組成的,記憶體緩存速度快容量小,磁碟緩存容量大速度慢可持久化。 1、PINCache概述 PINCache 是 Pinterest 的程式員在 Tumblr 的 TMCache ...
  • SQLite3是iOS內嵌的資料庫,SQLite3在存儲和檢索大量數據方面非常有效,它使得不必將每個對象都加到記憶體中。還能夠對數據進行負責的聚合,與使用對象執行這些操作相比,獲得結果的速度更快。 SQLite3使用SQL結構化查詢語言,SQL是與關係資料庫交互的標準語言。 SQLite3的使用: 1 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...