iOS學習——(轉)UIResponder詳解

来源:https://www.cnblogs.com/mukekeheart/archive/2018/02/23/8462314.html
-Advertisement-
Play Games

本文轉載自:ios開發 之 UIResponder詳解 我們知道UIResponder是所有視圖View的基類,在iOS中UIResponder類是專門用來響應用戶的操作處理各種事件的,包括觸摸事件(Touch Events)、運動事件(Motion Events)、遠程式控制制事件(Remote Co ...


本文轉載自:ios開發 之 UIResponder詳解 

  我們知道UIResponder是所有視圖View的基類,在iOS中UIResponder類是專門用來響應用戶的操作處理各種事件的,包括觸摸事件(Touch Events)、運動事件(Motion Events)、遠程式控制制事件(Remote Control Events)。我們知道UIApplication、UIView、UIViewController這幾個類是直接繼承自UIResponder,所以這些類都可以響應事件。當然我們自定義的繼承自UIView的View以及自定義的繼承自UIViewController的控制器都可以響應事件。本文將詳細介紹UIResponder類。

一 UIResponder.h文件註釋版

  首先,我們對UIResponder.h文件進行了研究和解釋,並對各模塊進行了註釋添加,方便我們在閱讀和學習時候的理解。在學習了UIView、NSObject的.h文件之後,我們發現這些基類的.h文件的組織架構基本類似,最初是定義該類中需要用到的一些枚舉類型數據,然後對相應的協議進行定義,接著就是對本類進行定義,一些基本的屬性和方法的定義,最後就是對本類做各種功能性的分類。

  1 //
  2 //  UIResponder.h
  3 
  4 #import <Foundation/Foundation.h>
  5 #import <UIKit/UIKitDefines.h>
  6 #import <UIKit/UIEvent.h>
  7 
  8 NS_ASSUME_NONNULL_BEGIN
  9 
 10 @class UIPress;
 11 @class UIPressesEvent;
 12 
 13 #pragma mark - UIResponderStandardEditActions協議定義
 14 
 15 @protocol UIResponderStandardEditActions <NSObject>
 16 @optional
 17 /** 剪切事件 */
 18 - (void)cut:(nullable id)sender NS_AVAILABLE_IOS(3_0);
 19 /** 複製事件 */
 20 - (void)copy:(nullable id)sender NS_AVAILABLE_IOS(3_0);
 21 /** 粘貼事件 */
 22 - (void)paste:(nullable id)sender NS_AVAILABLE_IOS(3_0);
 23 /** 選擇事件 */
 24 - (void)select:(nullable id)sender NS_AVAILABLE_IOS(3_0);
 25 /** 全選事件 */
 26 - (void)selectAll:(nullable id)sender NS_AVAILABLE_IOS(3_0);
 27 /** 刪除事件 */
 28 - (void)delete:(nullable id)sender NS_AVAILABLE_IOS(3_2);
 29 /** 從左到右寫入字元串(居左) */
 30 - (void)makeTextWritingDirectionLeftToRight:(nullable id)sender NS_AVAILABLE_IOS(5_0);
 31 /** 從右到左寫入字元串(居右) */
 32 - (void)makeTextWritingDirectionRightToLeft:(nullable id)sender NS_AVAILABLE_IOS(5_0);
 33 /** 切換字體為黑體(粗體) */
 34 - (void)toggleBoldface:(nullable id)sender NS_AVAILABLE_IOS(6_0);
 35 /** 切換字體為斜體 */
 36 - (void)toggleItalics:(nullable id)sender NS_AVAILABLE_IOS(6_0);
 37 /** 給文字添加下劃線 */
 38 - (void)toggleUnderline:(nullable id)sender NS_AVAILABLE_IOS(6_0);
 39 
 40 /** 增加字體大小 */
 41 - (void)increaseSize:(nullable id)sender NS_AVAILABLE_IOS(7_0);
 42 /** 減小字體大小 */
 43 - (void)decreaseSize:(nullable id)sender NS_AVAILABLE_IOS(7_0);
 44 
 45 @end
 46 
 47 #pragma mark - UIResponder類定義
 48 
 49 NS_CLASS_AVAILABLE_IOS(2_0) @interface UIResponder : NSObject <UIResponderStandardEditActions>
 50 
 51 #pragma mark - 響應者相關方法
 52 
 53 /** 獲取下一個響應者 */
 54 #if UIKIT_DEFINE_AS_PROPERTIES
 55 @property(nonatomic, readonly, nullable) UIResponder *nextResponder;
 56 #else
 57 - (nullable UIResponder *)nextResponder;
 58 #endif
 59 
 60 /** 是否允許成為第一響應者。預設返回NO */
 61 #if UIKIT_DEFINE_AS_PROPERTIES
 62 @property(nonatomic, readonly) BOOL canBecomeFirstResponder;
 63 #else
 64 - (BOOL)canBecomeFirstResponder;
 65 #endif
 66 /** 設置成為第一響應者 */
 67 - (BOOL)becomeFirstResponder;
 68 
 69 /** 是否允許放棄第一響應者。預設返回YES */
 70 #if UIKIT_DEFINE_AS_PROPERTIES
 71 @property(nonatomic, readonly) BOOL canResignFirstResponder;
 72 #else
 73 - (BOOL)canResignFirstResponder;
 74 #endif
 75 /** 設置放棄第一響應者 */
 76 - (BOOL)resignFirstResponder;
 77 
 78 /** 判斷對象是否是第一響應者 */
 79 #if UIKIT_DEFINE_AS_PROPERTIES
 80 @property(nonatomic, readonly) BOOL isFirstResponder;
 81 #else
 82 - (BOOL)isFirstResponder;
 83 #endif
 84 
 85 #pragma mark - 觸摸相關方法,一般用於響應屏幕觸摸
 86 /** 手指按下時響應 */
 87 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
 88 /** 手指移動時響應 */
 89 - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
 90 /** 手指抬起時響應 */
 91 - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
 92 /** 取消(意外中斷, 如:電話, 系統警告窗等) */
 93 - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
 94 /** 3DTouch響應(iOS9.1後使用) */
 95 - (void)touchesEstimatedPropertiesUpdated:(NSSet<UITouch *> *)touches NS_AVAILABLE_IOS(9_1);
 96 
 97 #pragma mark - 深按相關方法,一般用於遙控器按鍵響應
 98 /** 手指按壓開始時響應 */
 99 - (void)pressesBegan:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
100 /** 手指按壓位置移動時響應 */
101 - (void)pressesChanged:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
102 /** 手指抬起接受按壓時響應 */
103 - (void)pressesEnded:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
104 /** 按壓取消(意外中斷, 如:電話, 系統警告窗等) */
105 - (void)pressesCancelled:(NSSet<UIPress *> *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
106 
107 #pragma mark - 加速相關方法,一般用於搖一搖、運動事件監聽等
108 /** 開始加速 */
109 - (void)motionBegan:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
110 /** 結束加速 */
111 - (void)motionEnded:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
112 /** 加速取消(意外中斷, 如:電話, 系統警告窗等) */
113 - (void)motionCancelled:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
114 
115 /** 遠程式控制制事件 */
116 - (void)remoteControlReceivedWithEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(4_0);
117 
118 /** 返回UIMenuController需要顯示的控制項(如:複製,粘貼等) */
119 - (BOOL)canPerformAction:(SEL)action withSender:(nullable id)sender NS_AVAILABLE_IOS(3_0);
120 
121 /** 返迴響應的操作目標對象 */
122 - (nullable id)targetForAction:(SEL)action withSender:(nullable id)sender NS_AVAILABLE_IOS(7_0);
123 
124 /** 獲取響應鏈就近共用撤消管理 */
125 @property(nullable, nonatomic,readonly) NSUndoManager *undoManager NS_AVAILABLE_IOS(3_0);
126 
127 @end
128 
129 /** 快捷主鍵枚舉 */
130 typedef NS_OPTIONS(NSInteger, UIKeyModifierFlags) {
131     UIKeyModifierAlphaShift     = 1 << 16,  //!< Alpha+Shift鍵.
132     UIKeyModifierShift          = 1 << 17,  //!< Shift鍵.
133     UIKeyModifierControl        = 1 << 18,  //!< Control鍵.
134     UIKeyModifierAlternate      = 1 << 19,  //!< Alt鍵.
135     UIKeyModifierCommand        = 1 << 20,  //!< Command鍵.
136     UIKeyModifierNumericPad     = 1 << 21,  //!< Num鍵.
137 } NS_ENUM_AVAILABLE_IOS(7_0);
138 
139 #pragma mark - 快捷鍵對象
140 
141 NS_CLASS_AVAILABLE_IOS(7_0) @interface UIKeyCommand : NSObject <NSCopying, NSSecureCoding>
142 
143 /** 初始化對象 */
144 - (instancetype)init NS_DESIGNATED_INITIALIZER;
145 /** 初始化對象 */
146 - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
147 
148 /** 獲取快捷輔鍵(如快捷命令【Command+A】中的 A 鍵) */
149 @property (nonatomic,readonly) NSString *input;
150 /** 獲取快捷主鍵(如快捷命令【Command+A】中的 Command 鍵) */
151 @property (nonatomic,readonly) UIKeyModifierFlags modifierFlags;
152 /** 顯示給用戶的快捷鍵標題 */
153 @property (nullable,nonatomic,copy) NSString *discoverabilityTitle NS_AVAILABLE_IOS(9_0);
154 
155 /** 創建一個快捷鍵命令 */
156 + (UIKeyCommand *)keyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)modifierFlags action:(SEL)action;
157 
158 /** 創建一個快捷鍵命令 */
159 + (UIKeyCommand *)keyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)modifierFlags action:(SEL)action discoverabilityTitle:(NSString *)discoverabilityTitle NS_AVAILABLE_IOS(9_0);
160 
161 @end
162 
163 #pragma mark - 響應快捷命令
164 
165 @interface UIResponder (UIResponderKeyCommands)
166 /** 返回快捷鍵命令數組 */
167 @property (nullable,nonatomic,readonly) NSArray<UIKeyCommand *> *keyCommands NS_AVAILABLE_IOS(7_0);
168 @end
169 
170 @class UIInputViewController;
171 @class UITextInputMode;
172 @class UITextInputAssistantItem;
173 
174 #pragma mark - 輸入視圖
175 
176 @interface UIResponder (UIResponderInputViewAdditions)
177 
178 /** 鍵盤輸入視圖(系統預設的,可以自定義) */
179 @property (nullable, nonatomic, readonly, strong) __kindof UIView *inputView NS_AVAILABLE_IOS(3_2);
180 /** 彈出鍵盤時附帶的視圖 */
181 @property (nullable, nonatomic, readonly, strong) __kindof UIView *inputAccessoryView NS_AVAILABLE_IOS(3_2);
182 
183 /** 輸入助手配置鍵盤的快捷方式欄時使用 */
184 @property (nonnull, nonatomic, readonly, strong) UITextInputAssistantItem *inputAssistantItem NS_AVAILABLE_IOS(9_0) __TVOS_PROHIBITED __WATCHOS_PROHIBITED;
185 
186 /** 鍵盤輸入視圖控制器 */
187 @property (nullable, nonatomic, readonly, strong) UIInputViewController *inputViewController NS_AVAILABLE_IOS(8_0);
188 /** 彈出鍵盤時附帶的視圖的視圖控制器 */
189 @property (nullable, nonatomic, readonly, strong) UIInputViewController *inputAccessoryViewController NS_AVAILABLE_IOS(8_0);
190 
191 /** 文本輸入模式 */
192 @property (nullable, nonatomic, readonly, strong) UITextInputMode *textInputMode NS_AVAILABLE_IOS(7_0);
193 
194 /** 文本輸入模式標識 */
195 @property (nullable, nonatomic, readonly, strong) NSString *textInputContextIdentifier NS_AVAILABLE_IOS(7_0);
196 /** 根據設置的標識清除指定的文本輸入模式 */
197 + (void)clearTextInputContextIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(7_0);
198 
199 /** 重新刷新鍵盤輸入視圖 */
200 - (void)reloadInputViews NS_AVAILABLE_IOS(3_2);
201 
202 @end
203 
204 /** 特殊快捷輔鍵定義 */
205 UIKIT_EXTERN NSString *const UIKeyInputUpArrow         NS_AVAILABLE_IOS(7_0); //!< 上按鍵.
206 UIKIT_EXTERN NSString *const UIKeyInputDownArrow       NS_AVAILABLE_IOS(7_0); //!< 下按鍵.
207 UIKIT_EXTERN NSString *const UIKeyInputLeftArrow       NS_AVAILABLE_IOS(7_0); //!< 左按鍵.
208 UIKIT_EXTERN NSString *const UIKeyInputRightArrow      NS_AVAILABLE_IOS(7_0); //!< 右按鍵
209 UIKIT_EXTERN NSString *const UIKeyInputEscape          NS_AVAILABLE_IOS(7_0); //!< Esc按鍵.
210 
211 #pragma mark - 響應者活動
212 
213 @interface UIResponder (ActivityContinuation)
214 /** 用戶活動 */
215 @property (nullable, nonatomic, strong) NSUserActivity *userActivity NS_AVAILABLE_IOS(8_0);
216 /** 更新用戶活動 */
217 - (void)updateUserActivityState:(NSUserActivity *)activity NS_AVAILABLE_IOS(8_0);
218 /** 恢復用戶活動 */
219 - (void)restoreUserActivityState:(NSUserActivity *)activity NS_AVAILABLE_IOS(8_0);
220 @end
221 
222 NS_ASSUME_NONNULL_END

 二 UIResponder的使用

2.1 通過響應者鏈查找視圖的視圖控制器

   通過響應鏈查找視圖控制器,nextResponder獲取下一個響應者,響應者順序為:

 

/**
 *  查找視圖的視圖控制器
 *
 *  @param view 視圖
 *
 *  @return 返回視圖的控制器
 */
- (UIViewController *)getControllerFromView:(UIView *)view {
    // 遍歷響應者鏈。返回第一個找到視圖控制器
    UIResponder *responder = view;
    while ((responder = [responder nextResponder])){
        if ([responder isKindOfClass: [UIViewController class]]){
            return (UIViewController *)responder;
        }
    }
    // 如果沒有找到則返回nil
    return nil;
}

2.2 設置與取消第一響應者

  UIView預設不允許設置為第一響應者,因此設置UIView為第一響應者需要重寫canBecomeFirstResponder方法並返回YES。 設置為第一響應者後,對象則可以接受遠程式控制制事件進行處理(如耳機線控)。UITextField、UITextView成為第一響應者後會彈出輸入鍵盤,取消第一響應者則會隱藏輸入鍵盤。 

 1 //  ZMFirstResponderView.m
 2 
 3 #import "ZMFirstResponderView.h"
 4 
 5 @implementation ZMFirstResponderView
 6 
 7 /** 演示設置為第一響應者 */
 8 - (void)setBecomeFirstResponder {
 9     // 判斷對象是否已經是第一響應者
10     if ([self isFirstResponder]) {
11         return;
12     }
13     // 判斷對象是否允許成為第一響應者
14     if ([self canBecomeFirstResponder]) {
15         // 設置成為第一響應者
16         [self becomeFirstResponder];
17     }
18 }
19 
20 /** 演示放棄第一響應者 */
21 - (void)setResignFirstResponder {
22     // 判斷對象是否不是第一響應者
23     if (![self isFirstResponder]) {
24         return;
25     }
26     // 判斷對象是否允許放棄第一響應者
27     if ([self canResignFirstResponder]) {
28         // 設置放棄第一響應者
29         [self resignFirstResponder];
30     }
31 }
32 
33 /** 重寫方法,允許對象成為第一響應者 */
34 - (BOOL)canBecomeFirstResponder {
35     return YES;
36 }
37 
38 @end

2.3 觸摸相關方法,一般用於響應屏幕觸摸

/** 手指按下時響應 */
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
    [super touchesBegan:touches withEvent:event];
    NSLog(@"--->手指按下時響應");
}

/** 手指移動時響應 */
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
    [super touchesMoved:touches withEvent:event];
    NSLog(@"--->手指移動時響應");
}

/** 手指抬起時響應 */
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
    [super touchesEnded:touches withEvent:event];
    NSLog(@"--->手指抬起時響應");
}

/** 觸摸取消(意外中斷, 如:電話, Home鍵退出等) */
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
    [super touchesCancelled:touches withEvent:event];
    NSLog(@"--->取消觸摸響應");
}

2.4 加速相關方法,一般用於搖一搖、運動事件監聽等

/** 開始加速 */
- (void)motionBegan:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0) {
    [super motionBegan:motion withEvent:event];
    NSLog(@"--->開始加速");
}

/** 結束加速 */
- (void)motionEnded:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0) {
    [super motionEnded:motion withEvent:event];
    NSLog(@"--->結束加速");
}

/** 加速取消(意外中斷, 如:電話, Home鍵退出等) */
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0) {
    [super motionCancelled:motion withEvent:event];
    NSLog(@"--->加速取消");
}

2.5 遠程式控制制方法,一般用於耳機線控

耳機線控要註意三點要素:

  1. 啟動接受遠程事件:[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
  2. 設置成為第一響應者(UIViewController,AppDelegate中不需要設置)
  3. 獲取音頻的控制權
//  ZMAudioView.m

#import "ZMAudioView.h"
#import <AVFoundation/AVFoundation.h>

@implementation ZMAudioView

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // 1 啟動接受遠程事件
        [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
        //2  設置成為第一響應者
        [self becomeFirstResponder];
        // 3 播放一段靜音文件,使APP獲取音頻的控制權
        NSURL *audioURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"mute_60s" ofType:@"mp3"]];
        AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioURL error:nil];
        [audioPlayer play];
    }
    return self;
}

/** 允許對象成為第一響應者 */
- (BOOL)canBecomeFirstResponder {
    return YES;
}

/** 遠程式控制制事件響應 */
- (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent {
    NSLog(@"--->耳機線控響應");
}

- (void)dealloc {
    // 停止接受遠程事件
    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];
    // 放棄第一響應者
    [self resignFirstResponder];
}

@end

2.6 在UILabel中實現長按菜單(複製、粘貼等)

為UILabel添加長按菜單需要註意幾點:

  1. 啟用用戶交互:self.userInteractionEnabled = YES;
  2. 在顯示菜單之前設置對象成為第一響應者(UIViewController,AppDelegate中不需要設置)
  3. 返回菜單需要顯示的按鈕,並重寫實現對應方法
  4. 註冊長按手勢,顯示菜單
//  ZMMenuLabel.m

#import "ZMMenuLabel.h"

@implementation ZMMenuLabel

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // 啟用用戶交互
        self.userInteractionEnabled = YES;
        // 添加長按手勢
        UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressMenu:)];
        longPressGesture.minimumPressDuration = 0.2;
        [self addGestureRecognizer:longPressGesture];
    }
    return self;
}

/** 允許對象成為第一響應者 */
- (BOOL)canBecomeFirstResponder {
    return YES;
}

/** 長按響應 */
- (void)longPressMenu:(UILongPressGestureRecognizer *)sender {
    if (sender.state == UIGestureRecognizerStateBegan) {
        // 設置成為第一響應者
        [self becomeFirstResponder];
        // 顯示菜單
        UIMenuController *menuCtrl = [UIMenuController sharedMenuController];
        [menuCtrl setTargetRect:self.frame inView:self.superview];
        [menuCtrl setMenuVisible:YES animated:YES];
    }
}

/** 返回需要顯示的菜單按鈕 */
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    // 只顯示覆制、粘貼按鈕
    if (action == @selector(copy:) || action == @selector(paste:)) {
        return YES;
    }
    return NO;
}

/** 實現複製方法 */
- (void)copy:(id)sender {
    UIPasteboard *paste = [UIPasteboard generalPasteboard];
    paste.string = self.text;
}

/** 實現粘貼方法 */
- (void)paste:(id)sender {
    UIPasteboard *paste = [UIPasteboard generalPasteboard];
    self.text = paste.string;
}

@end

2.7 使用NSUndoManager實現畫板撤銷/重做功能

實現撤銷/重做註意以下幾點:

  1. 在調用方法時需要添加註冊一個對應的撤銷方法
  2. 撤銷/ 重做只需要調用undoManager中的相應方法即可
  3. 如果需要多個動作一起撤銷則需要標記分組
  1 /** ==============ZMDrawingBoardView.h文件=================== */
  2 
  3 #import <UIKit/UIKit.h>
  4 
  5 /** 畫板View */
  6 @interface ZMDrawingBoardView : UIView
  7 
  8 @end
  9 
 10 
 11 /** 劃線Model */
 12 @interface ZMLineModel : NSObject
 13 
 14 @property (nonatomic) CGPoint begin;
 15 @property (nonatomic) CGPoint end;
 16 
 17 @end
 18 
 19 
 20 /** ==============ZMDrawingBoardView.m文件=================== */
 21 
 22 #import "ZMDrawingBoardView.h"
 23 
 24 /** 畫板View */
 25 @interface ZMDrawingBoardView ()
 26 
 27 @property (nonatomic, strong) ZMLineModel *currentLine;
 28 @property (nonatomic, strong) NSMutableArray<ZMLineModel *> *toucheArray;
 29 
 30 @end
 31 
 32 @implementation ZMDrawingBoardView
 33 
 34 
 35 - (instancetype)initWithFrame:(CGRect)frame
 36 {
 37     self = [super initWithFrame:frame];
 38     if (self) {
 39         [self initSubView];
 40         self.backgroundColor = [UIColor whiteColor];
 41         self.toucheArray = [NSMutableArray array];
 42     }
 43     return self;
 44 }
 45 
 46 /** 繪製畫板 */
 47 - (void)drawRect:(CGRect)rect {
 48     // 獲得上下文
 49     CGContextRef context = UIGraphicsGetCurrentContext();
 50     // 設置樣式
 51     CGContextSetLineCap(context, kCGLineCapSquare);
 52     // 設置寬度
 53     CGContextSetLineWidth(context, 5.0);
 54     // 設置顏色
 55     CGContextSetStrokeColorWithColor(context, [[UIColor redColor] CGColor]);
 56     
 57     for (ZMLineModel *line in self.toucheArray) {
 58         // 開始繪製
 59         CGContextBeginPath(context);
 60         // 移動畫筆到起點
 61         CGContextMoveToPoint(context, line.begin.x, line.begin.y);
 62         // 添加下一點
 63         CGContextAddLineToPoint(context, line.end.x, line.end.y);
 64         // 繪製完成
 65         CGContextStrokePath(context);
 66     }
 67 }
 68 
 69 /** 劃線開始 */
 70 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
 71 {
 72     // 標記開始撤銷分組
 73     [self.undoManager beginUndoGrouping];
 74     
 75     for (UITouch *touch in touches) {
 76         // 記錄起始點
 77         CGPoint locTouch = [touch locationInView:self];
 78         _currentLine = [[ZMLineModel alloc] init];
 79         _currentLine.begin = locTouch;
 80       

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

-Advertisement-
Play Games
更多相關文章
  • Scores表 併列 不併列 ...
  • 在master資料庫中,SQL Server提供系統擴展的存儲過程,其中有一些存儲過程的命名以xp_開頭,用於處理操作系統的文件。 一,判斷文件是否存在 存儲過程sys.xp_fileexist 用於判斷文件是否存在,參數是文件(file)的路徑或目錄的路徑: 該存儲過程返回的結果集有一行數據,三個 ...
  • 眾所周知,網頁不光需要靠互聯網接入訪問才能提供各種形式的服務,而且連網頁自身的各種設計元素也需要在有網接入的情況上才能獲得。 但藉助於HTML5的離線特性,我們可以通過把各種類型的資源都儲存在Web應用的緩存里來解決這個問題。需要被緩存的文件在一個叫做Mainfest的文件中聲明。文件一旦被緩存,S ...
  • 什麼鬼!單例居然失效了,一個地方設置值,另個地方居然取不到,這怎麼可能?沒道理啊!排查半天,發現這兩就不在一個進程里,才恍然大悟…… 什麼是進程 按照操作系統中的描述:進程一般指一個執行單元,在 PC 和移動設備上指一個程式或者一個應用。 為什麼要使用多進程 我們都知道,系統為 APP 每個進程分配 ...
  • 本文將介紹在Android平臺上實現和演示 "迪士尼動畫基本準則" 。 項目開源,GitHub: "https://github.com/vhow/animation" 說明: 演示動畫原則的想法源自 "Animation Principles for the Web" 本項目所有的動畫都是通過 " ...
  • ...
  • 定位技術在現在的移動設備上是必不可少的,許多app都會使用定位功能。 通常定位方式有兩種:GPS定位;網路定位。 Android系統對這兩種定位方式都提供了相應的API支持,但是因為google的網路服務在中國不可訪問,所以在中國很少用到Android原生定位API,而是使用一些國內第三方公司的SD ...
  • 倒計時的功能,比如說:發送簡訊驗證碼倒計時。 界面佈局: 當我們不需要使用倒計時功能的時候,一定要要調用cancel()方法取消掉,不然它還會在我們頁面銷毀的時候繼續執行的,很有可能會導致記憶體泄漏的問題 代碼分析 mHandler裡面的代碼: 在創建構造函數之前會創建一個內部的Handler對象,主 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...