UiView的一些代理 一.<NSCoding>
我們會在以下這些場合用到NSCoding:
1. XIB/Storyboard
我們在創(chuàng)建一個UIView的subclass的時候,會注意到,如果我們希望在初始化這個view的時候就要做一些事情,不但要覆蓋掉initWithFrame:方法,也要覆蓋initWithCoder:方法。
如果這個view是我們用代碼創(chuàng)建的,那么會調(diào)用到initWithFrame:方法,但如果我們是XIB則會調(diào)用initWithCoder:這個方法。
XIB或是Storyboard在我們編寫程序的期間是XML格式的檔案,當(dāng)我們編譯App的時候,XCode會將其編譯成binary格式的data,分別是NIB與storyboardc,而這些data其實就是序列化過的Objective-C View對象。
將data轉(zhuǎn)成view,這個流程正是NSCoding protocol,我們從UIView的interface中可以看到UIView實現(xiàn)了NSCoding protocol。
2. NSUserDefaults
在我們的App中,如果我們要儲存一些偏好設(shè)定,那么最好的選擇就是NSUserDefaults。操作NSUserDefaults和操作NSDictionary差不多,只要指定特定的key,就可以將設(shè)定值存入NSUserDefaults中。
NSUserDefaults支持NSString、NSArray、NSDictionary、NSData以及int、doubel、float等類型的數(shù)據(jù)。
但如果是我們自定義的Class,就無法存入NSUserDefaults中,我們會先通過NSCoding轉(zhuǎn)成NSData后存入,在取出的時候也要多做一次unarchive(反序列化)。
二.<UIAppearance和UIAppearanceContainer>
iOS5及其以后提供了一個比較強(qiáng)大的工具UIAppearance,我們通過UIAppearance設(shè)置一些UI的全局效果,這樣就可以很方便的實現(xiàn)UI的自定義效果又能最簡單的實現(xiàn)統(tǒng)一界面風(fēng)格,它提供如下兩個方法。
+ (id)appearance
這個方法是統(tǒng)一全部改,比如你設(shè)置UINavBar的tintColor,你可以這樣寫:[[UINavigationBar appearance] setTintColor:myColor];
+ (id)appearanceWhenContainedIn:(Class <>)ContainerClass,...
這個方法可設(shè)置某個類的改變:例如:設(shè)置UIBarButtonItem 在UINavigationBar、UIPopoverController、UITabbar中的效果。就可以這樣寫
[[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], [UIPopoverController class],[UITabbar class] nil] setTintColor:myPopoverNavBarColor];
請注意*使用appearance設(shè)置UI效果最好采用全局的設(shè)置,在所有界面初始化前開始設(shè)置,否則可能失效。
具體UI外觀修改如下:
1.修改導(dǎo)航欄背景
代碼如下:
UINavigationBar * appearance = [UINavigationBar appearance];
UIImage *navBackgroundImg =[UIImage imageNamed:@"navBg.png”];
[appearance setBackgroundImage:navBackgroundImg forBarMetrics:UIBarMetricsDefault];
2.標(biāo)簽欄(UITabbar)
代碼如下:
UITabBar *appearance = [UITabBar appearance];
//設(shè)置背景圖片
[appearance setBackgroundImage:[UIImage imageNamed:@"tabbar_bg.png"]];
//門置選擇item的背景圖片
UIImage * selectionIndicatorImage =[[UIImage imageNamed:@"tabbar_slider"]resizableImageWithCapInsets:UIEdgeInsetsMake(4, 0, 0, 0)] ;
[appearance setSelectionIndicatorImage:selectionIndicatorImage];
3.分段控件(UISegmentControl)
代碼如下:
UISegmentedControl *appearance = [UISegmentedControl appearance];
//Segmenteg正常背景
[appearance setBackgroundImage:[UIImage imageNamed:@"Segmente.png"]
forState:UIControlStateNormal
barMetrics:UIBarMetricsDefault];
//Segmente選中背景
[appearance setBackgroundImage:[UIImage imageNamed:@"Segmente_a.png"]
forState:UIControlStateSelected
barMetrics:UIBarMetricsDefault];
//Segmente左右都未選中時的分割線
//BarMetrics表示navigation bar的狀態(tài),UIBarMetricsDefault 表示portrait狀態(tài)(44pixel height),UIBarMetricsLandscapePhone 表示landscape狀態(tài)(32pixel height)
[appearance setDividerImage:[UIImage imageNamed:@"Segmente_line.png"]
forLeftSegmentState:UIControlStateNormal
rightSegmentState:UIControlStateNormal
barMetrics:UIBarMetricsDefault];
[appearance setDividerImage:[UIImage imageNamed:@"Segmente_line.png"]
forLeftSegmentState:UIControlStateSelected
rightSegmentState:UIControlStateNormal
barMetrics:UIBarMetricsDefault];
[appearance setDividerImage:[UIImage imageNamed:@"Segmente_line.png"]
forLeftSegmentState:UIControlStateNormal
rightSegmentState:UIControlStateSelected
barMetrics:UIBarMetricsDefault];
//字體
NSDictionary *textAttributes1 = @{UITextAttributeFont: [UIFont systemFontOfSize:18],
UITextAttributeTextColor: [UIColor blueColor],
UITextAttributeTextShadowColor: [UIColor whiteColor],
UITextAttributeTextShadowOffset: [NSValue valueWithCGSize:CGSizeMake(1, 1)]};
[appearance setTitleTextAttributes:textAttributes1 forState:1];
NSDictionary *textAttributes2 = @{UITextAttributeFont: [UIFont systemFontOfSize:18],
UITextAttributeTextColor: [UIColor whiteColor],
UITextAttributeTextShadowColor: [UIColor blackColor],
UITextAttributeTextShadowOffset: [NSValue valueWithCGSize:CGSizeMake(1, 1)]};
[appearance setTitleTextAttributes:textAttributes2 forState:0];
4.UIBarbutton
注意:UIBarbutton有l(wèi)eftBarButton,rightBarButton和backBarButton,其中backBarButton由于帶有箭頭,需要單獨(dú)設(shè)置。
barButton背景設(shè)置是ios6.0及以后的,而backbutton是ios5.0及以后的,這里要注意!
代碼如下:
//修改導(dǎo)航條上的UIBarButtonItem
UIBarButtonItem *appearance = [UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil];
//設(shè)置導(dǎo)航欄的字體包括backBarButton和leftBarButton,rightBarButton的字體
NSDictionary *textAttributes = @{UITextAttributeFont: [UIFont systemFontOfSize:18],
UITextAttributeTextColor: [UIColor blueColor],
UITextAttributeTextShadowColor: [UIColor whiteColor],
UITextAttributeTextShadowOffset: [NSValue valueWithCGSize:CGSizeMake(1, 1)]};
[appearance setTitleTextAttributes:textAttributes forState:1];//forState為0時為下正常狀態(tài),為1時為點(diǎn)擊狀態(tài)。
//修改leftBarButton,rightBarButton背景效果
[appearance setBackgroundImage:[UIImage imageNamed:@"navBarButton.png"]
forState:UIControlStateNormal
style:UIBarButtonItemStyleBordered
barMetrics:UIBarMetricsDefault];
[appearance setBackgroundImage:[UIImage imageNamed:@"navBarButton_a.png"]
forState:UIControlStateHighlighted
style:UIBarButtonItemStyleBordered
barMetrics:UIBarMetricsDefault];
//backBarButton需要單獨(dú)設(shè)置背景效果。只能在ios6.0以后才能用
[appearance setBackButtonBackgroundImage:[UIImage imageNamed:@"nav_bg.png"]
forState:0
barMetrics:UIBarMetricsDefault];
[appearance setBackButtonBackgroundImage:[UIImage imageNamed:@"work.png"]
forState:1
barMetrics:UIBarMetricsDefault];
[appearance setBackButtonTitlePositionAdjustment:UIOffsetMake(2, -1)
forBarMetrics:UIBarMetricsDefault];
5.工具欄(UIToolbar)
UIToolbar *appearance = [UIToolbar appearance];
//樣式和背景二選一即可,看需求了
//樣式(黑色半透明,不透明等)設(shè)置
[appearance setBarStyle:UIBarStyleBlackTranslucent];
//背景設(shè)置
[appearance setBackgroundImage:[UIImage imageNamed:@"toolbarBg.png"]
forToolbarPosition:UIToolbarPositionAny
barMetrics:UIBarMetricsDefault];
三.<DynamicItem>
所有的UIView類都是一個DynamicItem對象,都可以實現(xiàn)UIDynamic動畫。UIDynamic,隸屬于UIKit框架,可以認(rèn)為是一種物理引擎,能模擬和仿真現(xiàn)實生活中的物理現(xiàn)象重力、彈性碰撞等現(xiàn)象。
UIDynamic使用步驟
創(chuàng)建一個物理仿真器(順便設(shè)置仿真范圍)
創(chuàng)建相應(yīng)的物理仿真行為(順便添加物理仿真元素)
將物理仿真行為添加到物理仿真器中,開始仿真。
任何遵守了UIDynamicItem協(xié)議的對象都可以作為物理仿真元素。(UIView)
UIView默認(rèn)已經(jīng)遵守了UIDynamicItem協(xié)議,因此任何UI控件都能做物理仿真
UICollectionViewLayoutAttributes類默認(rèn)也遵守UIDynamicItem協(xié)議。
物理仿真行為UIDynamic提供了以下幾種物理仿真行為UIGravityBehavior:
重力行為UICollisionBehavior:
碰撞行為UISnapBehavior:
捕捉行為UIPushBehavior:
推動行為UIAttachmentBehavior:
附著行為UIDynamicItemBehavior:
動力元素行為物理仿真器它可以讓物理仿真元素執(zhí)行物理仿真行為,它是UIDynamicAnimator類型的對象。
UIDynamicAnimator的初始化- (instancetype)initWithReferenceView:(UIView *)view;view參數(shù):是一個參照視圖,表示物理仿真的范圍UIDynamicAnimator的常見方法
//添加1個物理仿真行為- (void)addBehavior:(UIDynamicBehavior *)behavior;//移除1個物理仿真行為- (void)removeBehavior:(UIDynamicBehavior *)behavior;
//移除之前添加過的所有物理仿真行為- (void)removeAllBehaviors;UIDynamicAnimator的常見屬性//參照視圖@property (nonatomic, readonly) UIView* referenceView;
//添加到物理仿真器中的所有物理仿真行為@property (nonatomic, readonly, copy) NSArray* behaviors;
//是否正在進(jìn)行物理仿真@property (nonatomic, readonly, getter = isRunning) BOOL running;
//代理對象(能監(jiān)聽物理仿真器的仿真過程,比如開始和結(jié)束)@property (nonatomic, assign) iddelegate;
四.<UITraitEnvironment>
為了表征 Size Classes,Apple 在 iOS 8 中引入了一個新的類--UITraitCollection。這個類封裝了像水平和豎直方向的 Size Class 等信息。iOS 8 的 UIKit 中大多數(shù) UI 的基礎(chǔ)類 (包括 UIScreen,UIWindow,UIViewController 和 UIView) 都實現(xiàn)了 UITraitEnvironment 這個接口,通過其中的 traitCollection 這個屬性,我們可以拿到對應(yīng)的 UITraitCollection 對象,從而得知當(dāng)前的 Size Class,并進(jìn)一步確定界面的布局。
和 UIKit 中的響應(yīng)者鏈正好相反,traitCollection 將會在 view hierarchy 中自上而下地進(jìn)行傳遞。對于沒有指定 traitCollection 的 UI 部件,將使用其父節(jié)點(diǎn)的 traitCollection。這在布局包含 childViewController 的界面的時候會相當(dāng)有用。在 UITraitEnvironment 這個接口中另一個非常有用的是 -traitCollectionDidChange:。在 traitCollection 發(fā)生變化時,這個方法將被調(diào)用。在實際操作時,我們往往會在 ViewController 中重寫 -traitCollectionDidChange: 或者 -willTransitionToTraitCollection:withTransitionCoordinator: 方法 (對于 ViewController 來說的話,后者也許是更好的選擇,因為提供了轉(zhuǎn)場上下文方便進(jìn)行動畫;但是對于普通的 View 來說就只有前面一個方法了),然后在其中對當(dāng)前的 traitCollection 進(jìn)行判斷,并進(jìn)行重新布局以及動畫。
五.<UICoordinateSpace protocol>
iOS 8之前,window和screen的坐標(biāo)是不變的。它們不會進(jìn)行旋轉(zhuǎn),而是一直處于豎屏?xí)r的坐標(biāo)體系。我們是在View Controller旋轉(zhuǎn)方法中做對應(yīng)的處理。
iOS8之后,像上文描述的那樣,window和screen的坐標(biāo)會隨著app旋轉(zhuǎn)而改變。也不總是這樣的情況,因為View Controller仍然決定了App在當(dāng)前視圖下支持哪幾個方向。 在某些時候,我們需要一些修正之后的frame(比如需要存儲touch事件在屏幕上的坐標(biāo)),這個時候新的UICoordinateSpace就登場了。
UICoordinateSpace協(xié)議的接口如上,使用這些接口我們可以從當(dāng)前視圖的坐標(biāo)轉(zhuǎn)化到在screen的坐標(biāo)體系當(dāng)中,像這樣:[myView convertPoint:point toCoordinateSpace:myView.window.screen.fixedCoordinateSpace];這里提到了fixedCoordinateSpace,在iOS 8上UIScreen提供了兩個新的屬性,如下:@property (readonly) idcoordinateSpace NS_AVAILABLE_IOS(8_0);@property (readonly) idfixedCoordinateSpace NS_AVAILABLE_IOS(8_0);
coordinateSpace值的是當(dāng)前screen旋轉(zhuǎn)之后的坐標(biāo)體系,fixedCoordinateSpace指的是修正之后的坐標(biāo)體系。我們同樣可以把screen的坐標(biāo)轉(zhuǎn)換到當(dāng)前視圖的坐標(biāo)體系,如下:
[myView.window.screen.fixedCoordinateSpace convertPoint:point? ? ? toCoordinateSpace:myView];
舉個例子來說,在iPhone 5的屏幕大小上(320 * 568),myView在豎屏方向的frame是CGRectMake(50, 50, 200, 200),myView的Super View是window。有如下輸出結(jié)果:
CGPoint point = [myView convertPoint:CGPointMake(0, 0) toCoordinateSpace:[UIScreen mainScreen].fixedCoordinateSpace];
NSLog(@"fixedCoordinateSpace %@", NSStringFromCGPoint(point)); // {50, 50}
point = [myView convertPoint:CGPointMake(0, 0) toCoordinateSpace:[UIScreen mainScreen].coordinateSpace];
NSLog(@"coordinateSpace %@", NSStringFromCGPoint(point)); // {50, 50}
此時我們把設(shè)備順時針旋轉(zhuǎn)90度,在這兩種不同的坐標(biāo)體系下分別輸出的結(jié)果是:
NSLog(@"fixedCoordinateSpace %@", NSStringFromCGPoint(point)); // {50, 518}
NSLog(@"coordinateSpace %@", NSStringFromCGPoint(point)); // {50, 50}
六.<UIFocusItem>
該協(xié)議用于描述第三方類,允許在一個項目中申報其參與焦點(diǎn)系統(tǒng)的能力。
UIView的對象符合UIFocusItem協(xié)議能夠參與焦點(diǎn)系統(tǒng);此外,只能集中UIFocusItem對象。
即使一個對象符合UIFocusItem不是目前focusable,它可能仍然關(guān)注系統(tǒng)產(chǎn)生影響。例如,項目設(shè)置成不可定焦,但這完全掩蓋其他物品,可能防止focusable其他物品,因為他們對用戶是不可見的。另外,因為UIFocusItem符合UIFocusEnvironment,仍可能影響項目設(shè)置成不可定焦的關(guān)注行為他們包含的商品,或者對焦點(diǎn)更新。是在iOS10以后新增加的類目。
七.<CALayerDelegate>
CALayer的基本操作.
CALayer簡介:
CALayer我們又稱為層,在每個UIView內(nèi)部都有一個layer的屬性,UIView之所以能夠顯示,就是因為它里面有l(wèi)ayer層,才具有顯示的功能,我們通過操作CALayer對象,可以很方便地調(diào)整UIView的一些外觀屬性,例如可以給UIView設(shè)置陰影,圓角,邊框等等...
操作layer改變UIView外觀.
2.1 設(shè)置陰影
//默認(rèn)圖層是有陰影的, 只不過是透明的。1為不透明,0為透明_RedView.layer.shadowOpacity = 1;//設(shè)置陰影的偏移量self.imageV.layer.shadowOffset = CGSizeMake(-30, -10);//設(shè)置陰影的模糊程度self.imageV.layer.shadowRadius = 10;//設(shè)置陰影的圓角_RedView.layer.shadowRadius =10;//設(shè)置陰影的顏色,把UIKit轉(zhuǎn)換成CoreGraphics框架,用.CG開頭_RedView.layer.shadowColor = [UIColor blueColor].CGColor;
2.2.設(shè)置邊框
設(shè)置圖層邊框,在圖層中使用CoreGraphics的CGColorRef//設(shè)置邊框的顏色_RedView.layer.borderColor = [UIColor whiteColor].CGColor;//設(shè)置邊框的寬度_RedView.layer.borderWidth = 2;
2.3.設(shè)置圓角
圖層的圓角半徑,圓角半徑為寬度的一半, 就是一個圓_RedView.layer.cornerRadius = 50;
操作layer改變UIImageView的外觀.
3.1 設(shè)置陰影
//UIView本身就自帶陰影效果,它是透明._imageView.layer.shadowOpacity = 1;//設(shè)置陰影的偏移量_imageView.layer.shadowOffset = CGSizeMake(-30, -10);//設(shè)置陰影的模糊程度_imageView.layer.shadowRadius = 10;//設(shè)置陰影的顏色_imageView.layer.shadowColor = [UIColor blueColor].CGColor;
3.2 設(shè)置圖形邊框
//設(shè)置邊框?qū)挾萠imageView.layer.borderWidth = 2;//設(shè)置邊框顏色_imageView.layer.borderColor = [UIColor whiteColor].CGColor;
3.3 設(shè)置圖片的圓角半徑
//我們設(shè)置的所有l(wèi)ayer的屬性只作用在根層上,根層設(shè)置為圓形后,其上面的圖片并不會改變,因此需要裁剪。_imageView.layer.cornerRadius = 50;//裁剪,超出裁剪區(qū)域的部分全部裁剪掉_imageView.layer.masksToBounds = YES;
注意:UIImageView當(dāng)中Image并不是直接添加在根層上面的.而是添加在layer當(dāng)中的contents層里.我們設(shè)置層的所有屬性它只作用在根層上面.對contents里面的東西并不起作用.所以我們看不到圖片有圓角的效果.想要讓圖片有圓角的效果.可以把masksToBounds這個屬性設(shè)為YES.把就會把超過根層以外的東西都給裁剪掉.
layer的 CATransform3D屬性.
只有旋轉(zhuǎn)的時候才可以看出3D的效果.
//x,y,z 分別代表x,y,z軸. //旋轉(zhuǎn)CATransform3DMakeRotation(M_PI, 1, 0, 0);//平移CATransform3DMakeTranslation(x,y,z)//縮放CATransform3DMakeScale(x,y,z);//可以通過KVC的方式進(jìn)行設(shè)置屬性.//但是CATransform3DMakeRotation的值是一個結(jié)構(gòu)體, 所以要把結(jié)構(gòu)轉(zhuǎn)成對象.NSValue *value = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 1, 0, 0)];[_imageView.layer setValue:value forKeyPath:@"transform.scale"];
什么時候用KVC?當(dāng)需要做一些快速縮放,平移,二維的旋轉(zhuǎn)時用KVC.比如: [_imageView.layer setValue:@0.5 forKeyPath:@"transform.scale"];快速的進(jìn)行縮放.后面forKeyPath屬性值不是亂寫的.蘋果文檔當(dāng)中給了相關(guān)的屬性.

forKeyPath屬性值
自定義CALayer.
2.1 如何自定義Layer.
自定義CALayer的方式創(chuàng)建UIView的方式非常相似.
CALayer *layer = [CALayer layer];layer.frame = CGRectMake(50, 50, 100, 100);layer.backgroundColor = [UIColor redColor].CGColor;[self.view.layer addSublayer:layer];給layer設(shè)置圖片.layer.contents = (id)[UIImage imageNamed:@"icon"].CGImage;
2.2 關(guān)于CALayer的疑惑?
為什么要使用CGImageRef、CGColorRef?
CALayer定義在QuartzCore框架中.CGImageRef、CGColorRef兩種數(shù)據(jù)類型定義在CoreGraphics框架中.UIColor、UIImage定義在UIKit框架中.QuartzCore框架和CoreGraphics框架是可以跨平臺使用的,在iOS和Mac OSX上都能使用.但是UIKit框架只能在iOS中使用.所以為了保證可移植性,QuartzCore不能使用UIImage、UIColor,只能使用CGImageRef、CGColorRef.
UIView和CALayer都能夠顯示東西,該怎樣選擇?
對比CALayer,UIView多了一個事件處理的功能。也就是說,CALayer不能處理用戶的觸摸事件,而UIView可以,但是CALayer的性能會高一些,因為它少了事件處理的功能,更加輕量級如果顯示出來的東西需要跟用戶進(jìn)行交互的話,用UIView;如果不需要跟用戶進(jìn)行交互,用UIView或者CALayer都可以我們平常開發(fā)中一般用UIView.
CALayer的兩個重要屬性position和anchorPoint
position和anchorPoint是CAlayer的兩個屬性.我們以前修改一個控件的位置都是能過Frame的方式進(jìn)行修改.現(xiàn)在利用CALayer的position和anchorPoint屬性也能夠修改控件的位置.
這兩個屬性是配合使用的.position:它是用來設(shè)置當(dāng)前的layer在父控件當(dāng)中的位置的.所以它的坐標(biāo)原點(diǎn).以父控件的左上角為(0.0)點(diǎn).anchorPoint:它是決點(diǎn)CALayer身上哪一個點(diǎn)會在position屬性所指的位置anchorPoint是以當(dāng)前的layer左上角為原點(diǎn)(0.0),它的取值范圍是0~1,默認(rèn)位置在中間也就是(0.5,0.5).anchorPoint又稱錨點(diǎn).就是把錨點(diǎn)定到position所指的位置.兩者結(jié)合使用.想要修改某個控件的位置,我們可以設(shè)置它的position點(diǎn).設(shè)置完畢后.layer身上的anchorPoint會自動定到position所在的位置.

position和anchorpint圖示
隱式動畫.
4.1 什么是隱式動畫?
了解什么是隱式動畫前,要先了解什么是根層和非根層.根層:UIView內(nèi)部自動關(guān)聯(lián)著的那個layer我們稱它是根層.非根層:自己手動創(chuàng)建的層,稱為非根層.
隱式動畫就是當(dāng)對非根層的部分屬性進(jìn)行修改時, 它會自動的產(chǎn)生一些動畫的效果.我們稱這個默認(rèn)產(chǎn)生的動畫為隱式動畫.這些屬性稱為Animatable Properties(可動畫屬性)。也就是 手動創(chuàng)建的CALayer對象,都存在著隱式動畫
列舉常見的Animatable Properties:
bounds:CALayer的寬度和高度,修改時產(chǎn)生縮放動畫。
backgroundColor:背景顏色,修改時產(chǎn)生背景顏色漸變動畫效果。
position:CALayer的位置,修改時產(chǎn)生平移動畫
例:
如何取消隱式動畫?首先要了解動畫底層是怎么做的.動畫的底層是包裝成一個事務(wù)來進(jìn)行的.什么是事務(wù)?很多操作綁定在一起,當(dāng)這些操作執(zhí)行完畢后,才去執(zhí)行下一個操作.
因此我們自己開啟事務(wù),并在事物中設(shè)置沒有動畫就會隱藏動畫了
//開啟事務(wù)[CATransaction begin];//設(shè)置事務(wù)沒有動畫[CATransaction setDisableActions:YES];//設(shè)置動畫執(zhí)行的時長[CATransaction setAnimationDuration:2];//這其中修改屬性就沒有動畫了//提交事務(wù)[CATransaction commit];