1:iOS SEL的簡單總結 SEL就是對方法的一種包裝。包裝的SEL類型數據它對應相應的方法地址,找到方法地址就可以調用方法 a.方法的存儲位置 在記憶體中每個類的方法都存儲在類對象中 每個方法都有一個與之對應的SEL類型的數據 根據一個SEL數據就可以找到對應的方法地址,進而調用方法 SEL類型的 ...
1:iOS SEL的簡單總結
SEL就是對方法的一種包裝。包裝的SEL類型數據它對應相應的方法地址,找到方法地址就可以調用方法
a.方法的存儲位置
在記憶體中每個類的方法都存儲在類對象中
每個方法都有一個與之對應的SEL類型的數據
根據一個SEL數據就可以找到對應的方法地址,進而調用方法
SEL類型的定義: typedef struct objc_selector *SEL
b.SEL對象的創建
SEL s1 = @selector(test1); // 將test1方法包裝成SEL對象
SEL s2 = NSSelectorFromString(@"test1"); // 將一個字元串方法轉換成為SEL對象
c.SEL對象的其他用法
// 將SEL對象轉換為NSString對象
NSString *str = NSStringFromSelector(@selector(test));
實例:
Person *p = [Person new]; // 調用對象p的test方法 [p performSelector:@selector(test)]; [person performSelector:@selector(test2:) withObject:@"傳入參數"];
Person類代碼: #import "Person.h" @implementation Person - (void)test { NSLog(@"無參數的對象方法"); } - (void)test2:(NSString *)str { NSLog(@"帶有參數的方法%@",str); } @end
d:在數組中的運用
// 對一個數組array的每個元素執行一次test方法 [array makeObjectsPerformSelector:@selector(test)]; [array makeObjectsPerformSelector:@selector(test) withObject:@"aaa"]; //對一個數組array進行排序 [array sortedArrayUsingSelector:@selector(compare:)];
e:關於Sel傳參運用(target - action設計模式)
在Button中我們經常用下麵進行事件增加代碼:
- (void)addTarget:(nullable id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents;
實例:
#import <UIKit/UIKit.h> @interface TapView : UIView //目標 @property(weak,nonatomic)id target; //行為 @property(assign,nonatomic)SEL action; //自定義方法 -(void)addCustomtarget:(id)target andAction:(SEL)action; @end
#import "TapView.h" @implementation TapView //自定義方法 -(void)addCustomtarget:(id)target andAction:(SEL)action{ _action = action; _target = target; } -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ //當視圖點擊的時候,target去執行action的方法並把自己傳過去. //首先代理不能是空,而且代理(代理是對象!)的類中有方法並且能傳出過來. if (nil != _target && [[_target class] instancesRespondToSelector:_action]) { [_target performSelector:_action withObject:self]; } }
另一個實例:
#import <Foundation/Foundation.h> #import <objc/runtime.h> #import "Debug.h" // not given; just an assert @interface NSObject (Extras) // Enforce the rule that the selector used must return void. - (void) performVoidReturnSelector:(SEL)aSelector withObject:(id)object; - (void) performVoidReturnSelector:(SEL)aSelector; @end @implementation NSObject (Extras) // Apparently the reason the regular performSelect gives a compile time warning is that the system doesn't know the return type. I'm going to (a) make sure that the return type is void, and (b) disable this warning // See http://stackoverflow.com/questions/7017281/performselector-may-cause-a-leak-because-its-selector-is-unknown - (void) checkSelector:(SEL)aSelector { // See http://stackoverflow.com/questions/14602854/objective-c-is-there-a-way-to-check-a-selector-return-value Method m = class_getInstanceMethod([self class], aSelector); char type[128]; method_getReturnType(m, type, sizeof(type)); NSString *message = [[NSString alloc] initWithFormat:@"NSObject+Extras.performVoidReturnSelector: %@.%@ selector (type: %s)", [self class], NSStringFromSelector(aSelector), type]; NSLog(@"%@", message); if (type[0] != 'v') { message = [[NSString alloc] initWithFormat:@"%@ was not void", message]; [Debug assertTrue:FALSE withMessage:message]; } } - (void) performVoidReturnSelector:(SEL)aSelector withObject:(id)object { [self checkSelector:aSelector]; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" // Since the selector (aSelector) is returning void, it doesn't make sense to try to obtain the return result of performSelector. In fact, if we do, it crashes the app. [self performSelector: aSelector withObject: object]; #pragma clang diagnostic pop } - (void) performVoidReturnSelector:(SEL)aSelector { [self checkSelector:aSelector]; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [self performSelector: aSelector]; #pragma clang diagnostic pop } @end
2:代理模式實例
#import <UIKit/UIKit.h> @protocol TouchViewDelegate <NSObject> -(void)changeViewColor:(UIColor *)color; @end @interface TouchView : UIView //聲明一個代理,這個代理遵守TouchViewDelegate協議, @property(nonatomic,assign)id<TouchViewDelegate> delegate; @end
#import "TouchView.h" @implementation TouchView //我們還是用touchesBegandian -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ if (nil != self.delegate && [self.delegate respondsToSelector:@selector(changeViewColor:)]) { //我們傳一個顏色到我們的ViewController去. [self.delegate changeViewColor:[UIColor brownColor]]; } } @end
運用代碼: #import "ViewController.h" #import "TouchView.h" //在這裡ViewController 要遵守協議.... @interface ViewController ()<TouchViewDelegate> @property(nonatomic,strong)TouchView *touchView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.touchView = [[TouchView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)]; self.touchView.backgroundColor = [UIColor redColor]; //指定touchView的代理為ViewController.即為本身~ self.touchView.delegate =self; [self.view addSubview: self.touchView]; } -(void)changeViewColor:(UIColor *)color{ //現在參數color是有值的,這是因為在TouchView那個頁面傳過來的. self.touchView.backgroundColor = color; } @end
3:關於Bolck運用
#import <UIKit/UIKit.h> //給block改名成MyBlock typedef void(^MyBlock)(NSString *); @interface OtherViewController : UIViewController //ARC:語義設置使用strong即可 @property(nonatomic,strong)MyBlock block; @end
#import "OtherViewController.h" @interface OtherViewController () @property(nonatomic,strong)UITextField *textField; @end @implementation OtherViewController - (void)viewDidLoad { [super viewDidLoad]; self.textField = [[UITextField alloc]initWithFrame:CGRectMake(0, 100, 414, 40)]; self.textField.backgroundColor = [UIColor lightGrayColor]; self.view.backgroundColor = [UIColor whiteColor]; [self.view addSubview: self.textField]; } -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ self.block(_textField.text); [self.navigationController popViewControllerAnimated:YES]; } @end
運用代碼: OtherViewController *otherVC = [[OtherViewController alloc]init]; __weak typeof(self)weak = self; //給block賦值~ otherVC.block = ^(NSString *string){ weak.label.text = string; }; [self.navigationController pushViewController:otherVC animated:YES];
4:UIButton的幾種觸發方式
a、UIControlEventTouchDown
指滑鼠左鍵按下(註:只是“按下”)的動作
b、UIControlEventTouchDownRepeat
指滑鼠左鍵連續多次重覆按下(註:只是“按下”)的動作,比如,滑鼠連續雙擊、三擊、……、多次連擊。
說明:多次重覆按下時,事件序列是這樣的:
UIControlEventTouchDown ->
(UIControlEventTouchUpInside) ->
UIControlEventTouchDown ->
UIControlEventTouchDownRepeat ->
(UIControlEventTouchUpInside) ->
UIControlEventTouchDown ->
UIControlEventTouchDownRepeat ->
(UIControlEventTouchUpInside) ->
......
除了第一次按下外,後面每次摁下都是一個UIControlEventTouchDown事件,然後緊跟一個UIControlEventTouchDownRepeat事件。
c、UIControlEventTouchDragInside
指按下滑鼠,然後在控制項邊界範圍內拖動。
d、UIControlEventTouchDragOutside
與UIControlEventTouchDragInside不同的是,拖動時,滑鼠位於控制項邊界範圍之外。
但首先得有個UIControlEventTouchDown事件,然後接一個UIControlEventTouchDragInside事件,再接一個UIControlEventTouchDragExit事件,這時,滑鼠已經位於控制項外了,繼續拖動就是UIControlEventTouchDragOutside事件了。
具體操作是:在控制項裡面按下滑鼠,然後拖動到控制項之外。
e、UIControlEventTouchDragEnter
指拖動動作中,從控制項邊界外到內時產生的事件。
f、UIControlEventTouchDragExit
指拖動動作中,從控制項邊界內到外時產生的事件。
g、UIControlEventTouchUpInside
指滑鼠在控制項範圍內抬起,前提先得按下,即UIControlEventTouchDown或UIControlEventTouchDownRepeat事件。
h、UIControlEventTouchUpOutside
指滑鼠在控制項邊界範圍外抬起,前提先得按下,然後拖動到控制項外,即
UIControlEventTouchDown ->
UIControlEventTouchDragInside(n 個) ->
UIControlEventTouchDragExit ->
UIControlEventTouchDragOutside(n 個)
時間序列,再然後就是抬起滑鼠,產生UIControlEventTouchUpOutside事件。
5:讓編譯器對一些警告閉嘴
a:方法棄用告警
#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" //會報警告的方法,比如SEL [TestFlight setDeviceIdentifier:[[UIDevice currentDevice] uniqueIdentifier]]; #pragma clang diagnostic pop
b:未使用變數
#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-variable" int a; #pragma clang diagnostic pop
6:一個六邊形,並只在六邊形裡面有點擊效果
#import <UIKit/UIKit.h> //六邊形Button @interface HexagonButton : UIView NS_ASSUME_NONNULL_BEGIN typedef void (^HexagonButtonBlock)(); @property (nonatomic, strong) UIBezierPath *path; @property (nonatomic, strong) CAShapeLayer *maskLayer; @property (nonatomic, strong) HexagonButtonBlock block; //點擊事件 //添加點擊事件 NS_ASSUME_NONNULL_END @end
#import "HexagonButton.h" @implementation HexagonButton - (instancetype) initWithFrame:(CGRect)frame { if ([super initWithFrame:frame]) { self.backgroundColor = [UIColor brownColor]; self.userInteractionEnabled = YES; //添加單擊手勢 UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(click:)]; [self addGestureRecognizer:tap]; } return self; } - (void) layoutSubviews { [super layoutSubviews]; CGFloat SIZE = self.frame.size.width; // step 1: 生成六邊形路徑 CGFloat longSide = SIZE * 0.5 * cosf(M_PI * 30 / 180); CGFloat shortSide = SIZE * 0.5 * sin(M_PI * 30 / 180); CGFloat k = SIZE * 0.5 - longSide; //路徑整體下移,保證六邊形路徑位於圖形中間 _path = [UIBezierPath bezierPath]; [_path moveToPoint:CGPointMake(0, longSide + k)]; [_path addLineToPoint:CGPointMake(shortSide, + k)]; [_path addLineToPoint:CGPointMake(shortSide + shortSide + shortSide, k)]; [_path addLineToPoint:CGPointMake(SIZE, longSide + k)]; [_path addLineToPoint:CGPointMake(shortSide * 3, longSide * 2 + k)]; [_path addLineToPoint:CGPointMake(shortSide, longSide * 2 + k)]; [_path closePath]; // step 2: 根據路徑生成蒙板 _maskLayer = [CAShapeLayer layer]; // _maskLayer.position = self.center; _maskLayer.path = [_path CGPath]; // step 3: 添加蒙版 self.layer.mask = _maskLayer; self.backgroundColor = [UIColor orangeColor]; } //點擊事件 - (void) click:(UITapGestureRecognizer *) tap { if (_block) { _block(); } } - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { //如果點擊的區域在所創建的路徑範圍內 if (CGPathContainsPoint(_path.CGPath, NULL, point, NO)) { return [super hitTest:point withEvent:event]; } return nil; } @end
//創建六變形按鈕 HexagonButton * hexagonButton = [[HexagonButton alloc] initWithFrame:CGRectMake(20, 20, 100, 100)]; hexagonButton.center = self.view.center; hexagonButton.block = ^(){ NSLog(@"六邊形區域被點擊"); }; [self.view addSubview:hexagonButton];
7:self.navigationController.viewControllers修改
var controllerArr = self.navigationController?.viewControllers//獲取Controller數組 controllerArr?.removeAll()//移除controllerArr中保存的歷史路徑 //重新添加新的路徑 controllerArr?.append(self.navigationController?.viewControllers[0]) controllerArr?.append(C) controllerArr?.append(B) //這時歷史路徑為(root -> c -> b) //將組建好的新的跳轉路徑 set進self.navigationController里 self.navigationController?.setViewControllers(controllerArr!, animated: true)//直接寫入,完成跳轉B頁面的同時修改了之前的跳轉路徑
主要解決那些亂七八糟的轉轉,不按順序來的問題;