UIKit 視圖樹模型html
一、視圖樹模型ios
計算機圖形其實是一個視圖樹模型,每一個視圖都有一個本地座標系。
每一個本地座標系的組成部分是:原點在父座標系中的位置,每一個基在父座標系中的位置,由此就能夠根據向量的本地位置求出相對於父座標系的位置,最終求出向量全局位置。
咱們要分清全局座標系,父座標系,本地座標系三種概念,分清基、向量的座標、向量的位置三種概念。app
二、控件監聽事件iphone
觀察者模式在這裏獲得充分體現。ide
UIView和UIControll均可以監聽用戶事件,可是UIView實現監聽事件要經過在子類中覆寫- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event等方法,或者藉助於UIGestureRecognizer。而UIControll類則爲線程提供了一些方便的接口,好比addTarget方法。函數
監聽普通事件:oop
[button addTarget:self action:@selector(loadImageWithMultiThread) forControlEvents:UIControlEventTouchUpInside];佈局
監聽手勢事件:動畫
UITapGestureRecognizer響應順序是怎麼樣的
一個scrollview上有幾個按鈕
在scrollview上add 了一個單擊事件ui
singletap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)];
[singletap setNumberOfTapsRequired:1];
[scrollview addGestureRecognizer:singletap];
這樣點擊按鈕,不會響應按鈕的事件,而會直接跳到handleSingleTap去了,緣由在於單擊事件應該先被直接單擊的superview處理,若是沒有處理才傳遞到subview去處理。
怎麼才能讓按鈕響應單擊事件?
使用UIGestureRecognizerDelegate的一個方法判斷點擊的是哪一個view,肯定是否響應事件。
singletap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)]; [singletap setNumberOfTapsRequired:1]; singletap.delegate = self; [scrollview addGestureRecognizer:singletap]; //讓scrollview而不是上面的button來相應觸摸事件 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { if(touch.view != scrollview){ return NO; }else return YES; }
再一次舉例
UITapGestureRecognizer *singleFingerTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)]; [self.view addGestureRecognizer:singleFingerTap]; [singleFingerTap release]; - (void)handleSingleTap:(UITapGestureRecognizer *)recognizer { CGPoint location = [recognizer locationInView:[recognizer.view superview]]; //Do stuff here... }
三、添加視圖
[[[UIApplication sharedApplication] keyWindow] addSubview:myNewView];
怎樣讓視圖顯示在最外層?這樣就把視圖顯示在最外層,一個相似action sheet或者alert view的效果就是在window上添加一層灰色透明視圖做爲根節點,再添加想要的視圖。
嚐嚐使用這個方法覆蓋一個HUD風格的透明圖形
becomeKeyWindow 和 – resignKeyWindow ,線程調用這兩個方法,控制一個window實例是否成爲用於轉發用戶消息的那個窗口
Core Animation 動畫
一、實現動畫
對於不須要和用戶交互的動畫,要實現動畫只須要調用UIView的一個類方法便可,沒必要涉及CALayer和用戶觸摸事件
UIViewAnimationOptions options = UIViewAnimationCurveLinear | UIViewAnimationOptionAllowUserInteraction; [UIView animateWithDuration:0.2 delay:0.0 options:options animations:^ { highlightView.alpha = 1.0; } completion: ^(BOOL finished){
// 動畫結束時的處理
}];
可是對於交互類型的動畫,則須要手勢識別器,實現視圖跟隨觸摸點移動,或者覆寫須要覆寫touchesBegan&touchesMoved:配合Core Graphics中的各類函數實現自定義繪圖。
二、CALayer樹
線程會利用CALayer類(或者其子類)封裝的代碼來進行界面繪圖。UIView繼承自UIResponder,主要特色是能夠告訴線程如何響應觸摸事件,而CALayer是告訴線程如何進行實際的繪圖。
UIView的實例中維護着一個CALayer實例的樹模型,至關於CALayer樹的管理器,訪問UIView的跟繪圖和跟座標有關的屬性,例如frame,bounds等等,實際上內部都是在訪問它所包含的CALayer的相關屬性。
UIView有個layer屬性,能夠返回它的主CALayer實例,UIView有一個layerClass方法,返回主layer所使用的類,UIView的子類,能夠經過重載這個方法,來讓UIView使用不一樣的CALayer來顯示
- (class) layerClass { return ([CAEAGLLayer class]); }
UIView的CALayer相似UIView的子View樹形結構,也能夠向它的layer上添加子layer,來完成某些特殊的表示。例以下面的代碼會在目標View上敷上一層黑色的透明薄膜。
grayCover = [[CALayer alloc] init]; grayCover.backgroundColor = [[[UIColor blackColor] colorWithAlphaComponent:0.2] CGColor]; [self.layer addSubLayer: grayCover];
UIView的layer樹形在系統內部,被系統維護着三份copy
第一份,邏輯樹,就是代碼裏能夠操縱的,例如更改layer的屬性等等就在這一份。
第二份,動畫樹,這是一箇中間層,系統正在這一層上更改屬性,進行各類渲染操做。
第三份,顯示樹,這棵樹的內容是當前正被顯示在屏幕上的內容。
這三棵樹的邏輯結構都是同樣的,區別只有各自的屬性。
三、CATrainsition
一個CATrainsition類的實例能夠引用一個動畫過程。能夠經過設置CATrainsition實例的屬性,定義將要如何進行動畫。
CATransition是CAAnimation的子類,經過靜態方法animation獲取能夠引用動畫過程的實例。
自定義在window上添加模態視圖的全翻頁效果:
CATransition *animation = [CATransition animation]; animation.duration = 1.0; animation.timingFunction = UIViewAnimationCurveEaseInOut; animation.type = @"pageCurl"; //animation.type = kCATransitionPush; animation.subtype = kCATransitionFromLeft; [self.view.window.layer addAnimation:animation forKey:nil]; [self presentViewController:myNextViewController animated:NO completion:nil];
修改導航控制器推入動畫
CATransition *transition = [CATransition animation]; transition.duration = 1; transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; transition.type = kCATransitionPush; transition.subtype = kCATransitionFromTop; transition.delegate = self; [self.navigationController.view.layer addAnimation:transition forKey:nil]; self.navigationController.navigationBarHidden = NO; [self.navigationController pushViewController:viewController animated:NO];
修改導航控制器彈出的動畫
CATransition *transition = [CATransition animation]; transition.duration =0.4; transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; transition.type = kCATransitionReveal; //transition.subtype = kCATransitionFromBottom; transition.delegate = self; [self.navigationController.view.layer addAnimation:transition forKey:nil]; self.navigationController.navigationBarHidden = NO; [self.navigationController popViewControllerAnimated:NO];
Core Graphics 繪圖
iPhone的繪圖操做是在UIView類的drawRect方法中完成的,因此若是咱們要想在一個UIView中繪圖,須要寫一個擴展UIView 的類,並重寫drawRect方法,在這裏進行繪圖操做,線程會自動調用此方法進行繪圖。
好比,你想繪製一個方塊,你須要寫一個類來擴展UIView並在drawRect方法中填入以下代碼:
- (void)drawRect:(CGRect)rect { // Drawing code. //得到處理的上下文 CGContextRef context = UIGraphicsGetCurrentContext(); //設置線條樣式 CGContextSetLineCap(context, kCGLineCapSquare); //設置線條粗細寬度 CGContextSetLineWidth(context, 1.0); //設置顏色 CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0); //開始一個起始路徑 CGContextBeginPath(context); //起始點設置爲(0,0):注意這是上下文對應區域中的相對座標, CGContextMoveToPoint(context, 0, 0); //設置下一個座標點 CGContextAddLineToPoint(context, 100, 100); //設置下一個座標點 CGContextAddLineToPoint(context, 0, 150); //設置下一個座標點 CGContextAddLineToPoint(context, 50, 180); //鏈接上面定義的座標點 CGContextStrokePath(context); }
不過有時不會直接調用在context上添加線段的addLine方法,逐點填寫絕對座標,而是先得到上下文context,在定義一個路徑path,調用addLine方法定義好一個path的所有位置,而後把路徑添加到上下文裏
class MyView: UIView { var _path = CGPathCreateMutable() override func drawRect(rect: CGRect) { UIColor.redColor().set() var context = UIGraphicsGetCurrentContext() CGContextSetLineWidth(context, 5) CGContextAddPath(context, _path) CGContextStrokePath(context) } override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) { let p = (touches as NSSet).anyObject()?.locationInView(self) CGPathMoveToPoint(path, nil, p!.x, p!.y) } override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) { let p = (touches as NSSet).anyObject()?.locationInView(self) CGPathAddLineToPoint(_path, nil, p!.x, p!.y) setNeedsDisplay() } func drawTwoLines(point:CGPoint, lineJoin:CGLineJoin) { let context = UIGraphicsGetCurrentContext() CGContextSetLineJoin(context, lineJoin) CGContextSetLineWidth(context, 5) CGContextMoveToPoint(context, point.x-100, point.y+50) CGContextAddLineToPoint(context, point.x, point.y) CGContextAddLineToPoint(context, point.x+100, point.y+50) CGContextStrokePath(context) } }
再說明一下重繪,重繪操做仍然在drawRect方法中完成,可是蘋果不建議直接調用drawRect方法,若是你強直直接調用此方法,固然是沒有效果的。在UIView中,重寫drawRect: (CGRect) aRect方法能夠本身定義想要畫的圖案,可是此方法通常狀況下只會畫一次,也就是說這個drawRect方法通常狀況下只會被調用一次。drawRect是在Controller->loadView, Controller->viewDidLoad 兩方法以後調用的.
蘋果要求咱們調用UIView類中的setNeedsDisplay方法,則程序會自動調用drawRect方法進行重繪,即[self setNeedsDisplay]方法便可。
注意
一、若是在UIView初始化時沒有設置rect大小,將直接致使drawRect不被自動調用。
二、經過設置contentMode屬性值爲UIViewContentModeRedraw。那麼將在每次設置或更改frame的時候自動調用drawRect:。
三、直接調用setNeedsDisplay,或者setNeedsDisplayInRect:觸發drawRect:,可是有個前提條件是rect不能爲0.
四、若使用UIView繪圖,只能在drawRect:方法中獲取相應的contextRef並繪圖。若是在其餘方法中獲取將獲取到一個invalidate的ref而且不能用於畫圖。drawRect:方法不能手動顯示調用,必須經過調用setNeedsDisplay 或者 setNeedsDisplayInRect ,讓系統自動調該方法。
五、若使用calayer繪圖,只能在drawInContext: 中繪製,或者在delegate中的相應方法繪製。一樣也是調用setNeedDisplay等間接調用以上方法。
六、若要實時畫圖,不能使用gestureRecognizer,只能使用touchbegan等方法來掉用setNeedsDisplay實時刷新屏幕
Interface Builder
筆者傾向於使用純代碼方式來實現界面佈局,這樣更容易實現自定義UI控件和更容易維護修改。不過對於簡單的界面,也可使用蘋果爲咱們提供的圖形化的方式來構建。
一、若是經過加載nib文件的方式加載controller的視圖,當線程調用 initWithNibName: 來初始化controller.view時,若是nibName能夠找到,線程會根據nib文件初始化控制器,須要注意的是不管經過什麼方式(除了story board)初始化控制器,最終都會調用UIViewController或者其子類中的initWithNibName方法,因此在這裏初始化就夠了。
二、若是經過story board生成控制器,那麼會調用instantiateViewControllerWithIdentifier建立對象,而後用initWithCdoer初始化控制器。
- (id)initWithCoder:(NSCoder*)coder{ if (self =[super initWithcoder:coder]) { //初始化控制器的變量 } return self; }
三、使用interface builder實現控件監聽事件,須要配合代碼中的IBOutlet和IBAction關鍵字,並創建Interface Builder對象和代碼的關聯。
素材資源
要創建一個像那麼回事兒的界面,不能不關注圖片資源,雖然這看起來像是美工的活。
一、分辨率
iPhone4/4s:主屏幕像素:960x640,開發中屏幕尺寸爲 480x320
iPhone5/5s:主屏幕像素:1136x640,開發中屏幕尺寸爲 568x320
iPhone6/6s:主屏幕像素:1334x750,開發中屏幕尺寸爲 667x375
iPhone6p:主屏幕像素:1920*1080,開發中屏幕尺寸爲 736x414
必定要注意開發是的最小刻度值和並不等於實際像素的長度。
素材準備:
● Icon.png – 像素57×57 iPhone iOS 6應用圖標
● Icon-120.png – 像素120 x 120 iPhone4顯示屏 iOS 7應用圖標
● Icon-Small.png – 像素29×29 iPhone 系統設置和搜索結果圖標
● Icon-Small@2x.png – 像素58×58 iPhone Retina顯示屏 系統設置和搜索結果圖標
● Default.png 480x320 iPhone啓動畫面圖片
● Default@2x.png 960x640 iPhone四、4s的啓動畫面圖片
● Default-568h@2x.png 1136x640 iPhone5啓動畫面圖片
詳見
程序中須要注意bounds和nativeBounds的區別。iphone5的bounds.size.height是568.
實用界面設置
UIApplication(或者其子類)的對象主要告訴線程如何等待和處理事件。
這個類中定義了不少有用的代碼,可供線程執行。
1.設置icon上的數字圖標 [UIApplication sharedApplication].applicationIconBadgeNumber = 4; 2.設置搖動手勢的時候,是否支持redo,undo操做 [UIApplication sharedApplication].applicationSupportsShakeToEdit =YES; 3.判斷程序運行狀態 if([UIApplication sharedApplication].applicationState ==UIApplicationStateInactive){ NSLog(@"程序在運行狀態"); } 4.阻止屏幕變暗進入休眠狀態 [UIApplication sharedApplication].idleTimerDisabled =YES; 慎重使用本功能,由於很是耗電。 5.顯示聯網狀態 [UIApplication sharedApplication].networkActivityIndicatorVisible =YES; 6.在map上顯示一個地址 NSString* addressText =@"1 Infinite Loop, Cupertino, CA 95014"; addressText = [addressText stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding]; NSString* urlText = [NSString stringWithFormat:@"http://maps.google.com/maps?q=%@", addressText]; [[UIApplication sharedApplication]openURL:[NSURL URLWithString:urlText]]; 7.發送電子郵件 NSString *recipients =@"mailto:first@example.com?cc=second@example.com,third@example.com&subject=Hello from California!"; NSString *body =@"&body=It is raining in sunny California!"; NSString *email = [NSString stringWithFormat:@"%@%@", recipients, body]; email = [email stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; [[UIApplication sharedApplication]openURL:[NSURL URLWithString:email]]; 8.打電話到一個號碼 [[UIApplication sharedApplication]openURL:[NSURL URLWithString:@"tel://8004664411"]]; 9.發送短信 [[UIApplication sharedApplication]openURL:[NSURL URLWithString:@"sms://466453"]]; 10.打開一個網址 [[UIApplication sharedApplication]openURL:[NSURL URLWithString:@"http://itunesconnect.apple.com"]]; AppDelegate類 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 當因爲其它方法打開應用程序(如URL指定或者鏈接),通知委託啓動完畢 - (void)applicationWillTerminate:(UIApplication *)application 通知委託,應用程序將在關閉 退出,請作一些清理工做。 - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application 通知委託,應用程序收到了爲來自系統的內存不足警告。-(void)applicationSignificantTimeChange:(UIApplication *)application 通知委託系統時間發生改變(主要是指時間屬性,而不是具體的時間值) 打開URL - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url 打開指定的URL 控制狀態欄方位變化 – application:willChangeStatusBarOrientation:duration: 設備方向將要發生改變 – application:didChangeStatusBarOrientation: 活動狀態改變 - (void)applicationWillResignActive:(UIApplication *)application 通知委託應用程序將進入非活動狀態,在此期間,應用程序不接收消息或事件。-(void)applicationDidBecomeActive:(UIApplication *)application 通知委託應用程序進入活動狀態,請恢復數據 若是咱們但願得到appdelegate實例,沒必要經過import類的方式,只須要: UIApplicationDelegate* delegate = [[UIApplication sharedApplication] delegate];
實用控件和方法
不少控件和方法都是直接在keywindow上添加視圖實現,它們每每是以模態的方式呈現,適用於註冊、登陸等等場景。
一、UIViewController:
每一個控制器實例都有下面兩個方法,用於在keywindow上添加或刪除一個模態視圖,經常使用於登陸界面、發微博界面等等。
– presentViewController:animated:completion: 彈出,出現一個新視圖,能夠帶動畫效果,完成後能夠作相應的執行函數常常爲nil
– dismissViewControllerAnimated:completion:退出一個新視圖,能夠帶動畫效果,完成後能夠作相應的執行函數常常爲nil
二、action sheet
actionSheet = [[UIActionSheet alloc] initWithTitle:title delegate:nil cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:@"相冊", @"拍照",nil]; picker = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 50, 320, 220)]; picker.delegate = self; picker.dataSource = self; picker.showsSelectionIndicator = YES; [actionSheet addSubview:picker]; [actionSheet showInView:self.view];
參數說明:
title:視圖標題
delegate:設置代理
cancelButtonTitle:取消按鈕的標題
destructiveButtonTitle:特殊標記的按鈕的標題
otherButtonTitles:其餘按鈕的標題
動做回調方法爲:
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{ UIAlertView *alertView; switch (buttonIndex) { case 0: alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"點擊相冊事件" delegate:self cancelButtonTitle:nil otherButtonTitles: nil]; [alertView show]; break; case 1: alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"以點擊拍照事件" delegate:self cancelButtonTitle:nil otherButtonTitles: nil]; [alertView show]; break; default: break; } }
三、alert view
在action sheet中,動做回調方法中就建立了alert view實例。