UIView的常見屬性html
UIView的常見屬性java
- (void)addSubview:(UIView *)view;
linux
添加一個子控件viewios
- (void)removeFromSuperview;程序員
從父控件中移除web
- (UIView *)viewWithTag:(NSInteger)tag;面試
根據一個tag標識找出對應的控件(通常都是子控件)算法
transform屬性sql
利用transform屬性能夠修改控件的位移(位置)、縮放、旋轉數據庫
建立一個transform屬性
CGAffineTransform CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty) ;
CGAffineTransform CGAffineTransformMakeScale(CGFloat sx, CGFloat sy);
CGAffineTransform CGAffineTransformMakeRotation(CGFloat angle)
(注意:angle是弧度制,並非角度制)
在某個transform的基礎上進行疊加
CGAffineTransform CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty);
CGAffineTransform CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy);
CGAffineTransform CGAffineTransformRotate(CGAffineTransform t, CGFloat angle);
清空以前設置的transform屬性
view.transform = CGAffineTransformIdentity;
1筆記
1. IBAction的參數
========================================
- (IBAction)left:(UIButton *)button
1> 在OC中,絕大多數的控件監聽方法的第一個參數就是控件自己
2> 默認連線時的參數類型是id
3> 若是要在監聽方法中,方便控件的使用,能夠在連線時或者連線後,修改監聽方法的參數類型
2. 修改對象的結構體成員
========================================
在OC中,不容許直接修改「對象」的「結構體屬性」的「成員」,可是容許修改「對象」的「結構體屬性」
修改結構體屬性的成員方法以下:
1> 使用臨時變量記錄對象的結構體屬性
2> 修改臨時變量的屬性
3> 將臨時變量從新設置給對象的結構體屬性
3. 在程序開發中須要避免出現魔法數字(Magic Number)
========================================
使用枚舉類型,能夠避免在程序中出現魔法數字
1> 枚舉類型實質上就是一個整數,其做用就是用來替代魔法數字
2> 枚舉類型中,指定了第一個整數以後,後面的數字會遞增
4. frame & bounds & center
========================================
1> frame能夠修改對象的位置和尺寸
2> bounds能夠修改對象的尺寸
3> center能夠修改對象的位置
5. 首尾式動畫
========================================
// beginAnimations表示此後的代碼要「參與到」動畫中
[UIView beginAnimations:nil context:nil];
// setAnimationDuration用來指定動畫持續時間
[UIView setAnimationDuration:2.0];
self.headImageView.bounds = rect;
......
// commitAnimations,將beginAnimation以後的全部動畫提交併生成動畫
[UIView commitAnimations];
6. transform屬性
========================================
在OC中,經過transform屬性能夠修改對象的平移、縮放比例和旋轉角度
經常使用的建立transform結構體方法分兩大類
1> 建立「基於控件初始位置」的形變
CGAffineTransformMakeTranslation
CGAffineTransformMakeScale
CGAffineTransformMakeRotation
2> 建立「基於transform參數」的形變
CGAffineTransformTranslate
CGAffineTransformScale
CGAffineTransformRotate
補充:
在OC中,全部跟角度相關的數值,都是弧度值,180° = M_PI
正數表示順時針旋轉
負數表示逆時針旋轉
提示:因爲transform屬性能夠基於控件的上一次的狀態進行疊加形變,例如,先旋轉再平移
所以在實際動畫開發中,當涉及位置、尺寸形變效果時,大多修改控件的transform屬性,
而不是frame、bounds、center
7. 使用代碼建立控件
========================================
在OC開發中,Storyboard中的全部操做均可以經過代碼實現,程序員必定要熟練掌握代碼佈局界面的能力!
使用代碼建立控件的步驟以下:
1> 使用控件對應類建立對象
2> 設置對象屬性:frame\color\text\image\backgroundImage……
3> [self.view addSubview:btn];將控件添加到視圖
設置控件監聽方法的示例代碼以下:
[btn addTarget:self action:@selector(click:) forControlEvents:UIControlEventTouchUpInside];
提示:
1> addTarget方法定義在UIControl類中,這意味着能夠給全部繼承自UIControl類的對象添加監聽方法
2> 監聽方法的第一個參數就是對象自己
3> 監聽方法的第二個參數是監聽控件的事件
8. viewDidLoad
========================================
viewDidLoad是視圖加載完成後調用的方法,一般在此方法中執行視圖控制器的初始化工做
在viewDidLoad方法中,必定不要忘記調用父類的方法實現!
[super viewDidLoad];
圖片瀏覽器
1. 界面分析
========================================
1> 須要讀取或修改屬性的控件須要設置屬性
// 序號標籤
// 圖片
// 圖片描述
// 左邊按鈕
// 右邊按鈕
2> 須要監聽響應事件的對象,須要添加監聽方法
// 左邊按鈕
// 右邊按鈕
2. 手碼懶加載建立控件的步驟
========================================
1> 定義控件屬性,注意:屬性必須是strong的,示例代碼以下:
@property (nonatomic, strong) UIImageView *icon;
2> 在屬性的getter方法中實現懶加載,示例代碼以下:
- (UIImageView *)icon
{
if (!_icon) {
// 計算位置參數
CGFloat imageW = 200;
CGFloat imageX = (320 - imageW) / 2;
CGFloat imageH = 200;
CGFloat imageY = 80;
// 實例化圖像視圖
_icon = [[UIImageView alloc] initWithFrame:CGRectMake(imageX, imageY, imageW, imageH)];
// 將圖像視圖添加到主視圖
[self.view addSubview:_icon];
}
return _icon;
}
使用懶加載的好處:
1> 沒必要將建立對象的代碼所有寫在viewDidLoad方法中,代碼的可讀性更強
2> 每一個控件的getter方法中分別負責各自的實例化處理,代碼彼此之間的獨立性強,鬆耦合
3. 使用Plist文件
========================================
使用Plist文件的目的:將數據與代碼分離
加載方法:
NSString *path = [[NSBundle mainBundle] pathForResource:@"ImageData" ofType:@"plist"];
_imageList = [NSArray arrayWithContentsOfFile:path];
提示:一般在方法中出現File字眼,一般須要傳遞文件的全路徑做爲參數
Tomcat
1. Images.xcassets中的素材
========================================
1> 只支持png格式的圖片
2> 圖片只支持[UIImage imageNamed]的方式實例化,可是不能從Bundle中加載
3> 在編譯時,Images.xcassets中的全部文件會被打包爲Assets.car的文件
2. UIImageView的序列幀動畫
========================================
// 0. 是否正在動畫
[self.tom isAnimating];
// 1. 設置圖片的數組
[self.tom setAnimationImages:arrayM];
// 2. 設置動畫時長,默認每秒播放30張圖片
[self.tom setAnimationDuration:arrayM.count * 0.075];
// 3. 設置動畫重複次數,默認爲0,無限循環
[self.tom setAnimationRepeatCount:1];
// 4. 開始動畫
[self.tom startAnimating];
// 5. 動畫播放完成後,清空動畫數組
[self.tom performSelector:@selector(setAnimationImages:) withObject:nil afterDelay:self.tom.animationDuration];
3. UIImage imageNamed
========================================
在圖片使用完成後,不會直接被釋放掉,具體釋放時間由系統決定,適用於圖片小,經常使用的圖像處理
若是要釋放快速釋放圖片,可使用[UIImage imageWithContentsOfFile:path]實例化圖像
4. 方法重構的策略
========================================
1> 將具備共性的代碼複製到一個新的方法
2> 根據不一樣的調用狀況,增長方法的參數
提示:在寫程序時不要着急重構,有時候把代碼先寫出來,更容易看清楚如何重構纔會更好!
5. Bundle中的圖片素材
========================================
往項目中拖拽素材時,一般選擇
1> Destination: 勾選
2> Folders:
選擇第一項:黃色文件夾
Xcode中分文件夾,Bundle中全部所在都在同一個文件夾下,所以,不能出現文件重名的狀況
特色:
*** 能夠直接使用[NSBundle mainBundle]做爲資源路徑,效率高!
*** 可使用[UIImage imageNamed:]加載圖像
選擇第二項:藍色文件夾
Xcode中分文件夾,Bundle中一樣分文件夾,所以,能夠出現文件重名的狀況
特色:
*** 須要在[NSBundle mainBundle]的基礎上拼接實際的路徑,效率較差!
*** 不能使用[UIImage imageNamed:]加載圖像
6. 文件管理
========================================
[NSFileManager defaultManager]
經常使用方法
1> 判斷文件是否存在
- (BOOL)fileExistsAtPath:(NSString *)path;
2> 將文件從源路徑複製到目標路徑
- (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error;
3> 刪除文件
- (BOOL)removeItemAtPath:(N
UIImageView幀動畫相關屬性和方法
@property(nonatomic,copy) NSArray *animationImages;
須要播放的序列幀圖片數組(裏面都是UIImage對象,會按順序顯示裏面的圖片)
@property(nonatomic) NSTimeInterval animationDuration;
幀動畫的持續時間
@property(nonatomic) NSInteger animationRepeatCount;
幀動畫的執行次數(默認是無限循環)
- (void)startAnimating;
開始執行幀動畫
- (void)stopAnimating;
中止執行幀動畫
- (BOOL)isAnimating;
是否正在執行幀動畫
方式一:有緩存(圖片所佔用的內存會一直停留在程序中)
+ (UIImage *)imageNamed:(NSString *)name;
name是圖片的文件名
方式二:無緩存(圖片所佔用的內存會在一些特定操做後被清除)
+ (UIImage *)imageWithContentsOfFile:(NSString *)path
- (id)initWithContentsOfFile:(NSString *)path;
path是圖片的全路徑
知識擴充
1. @property的參數說明
========================================
ARC是蘋果爲了簡化程序員對內存的管理,推出的一套內存管理機制
使用ARC機制,對象的申請和釋放工做會在運行時,由編譯器自動在代碼中添加retain和release
1> strong:強指針引用的對象,在生命週期內不會被系統釋放
在OC中,對象默認都是強指針
2> weak:弱指針引用的對象,系統會當即釋放
弱指針能夠指向其餘已經被強指針引用的對象
@property參數使用小結:
1> 控件用weak
2> 屬性對象用strong
3> 非對象類型用assign
4> 字符串NSString用copy
提示:在純手碼實現界面佈局時,若是經過懶加載處理界面控件,須要使用strong強指針
2. 運行循環
========================================
在iOS的應用程序中,應用程序啓動以後,系統即會建立一個運行循環監聽用戶的交互。
如下代碼其本質是在運行循環中註冊一個監聽事件
[button addTarget:self action:@selector(click:) forControlEvents:UIControlEventTouchUpInside];
當運行循環檢測到button的UIControlEventTouchUpInside事件時,會給視圖控制器(self)發送一個click消息。
應用管理的筆記
1. 開發前的思路
========================================
1> 從mainBundle中加載Plist
2> 按照plist中的數據數量先肯定各個appView的大小和位置
3> 使用代碼建立appView中的子控件,並顯示內容
2. 關於九宮格佈局的計算方法
========================================
關於如何計算界面的九宮格佈局,其實能夠有若干種方法,沒必要死記課堂的代碼,
要可以順利計算出每個小格子準確的座標,建議:
1> 先建立若干小的視圖
2> 找到本身理解比較容易的計算方法
3> 編寫循環建立九宮格佈局
要求:可以公用的常量儘可能給抽取出來,以便增長九宮格佈局的靈活性,儘可能保證作到:
1> 根據要顯示的數據自動調整小格子的位置和數量
2> 一旦調整了要顯示的列數,僅須要修改少許的數值便可作到
3. 關於UIButton的一些補充
========================================
3.1 按鈕的類型
在iOS的控件中,只有UIButton提供了類方法,能夠在實例化按鈕時指定按鈕的不一樣類型。
UIButtonTypeCustom和[[UIButton alloc] init]是等價的
3.2 修改按鈕字體
在UIButton中定義有兩個readonly的屬性:
1> titleLabel
2> imageView
@property中readonly表示不容許修改這兩個屬性的指針地址,可是能夠修改其屬性
注意:因爲按鈕的字體大小是全部狀態共用的,所以能夠經過
button.titleLabel.font= [UIFont systemFontOfSize:14.0];
修改按鈕標籤文本的字體大小
可是不能使用如下代碼設置按鈕標籤的文本內容
button.titleLabel.text = @"下載";
由於按鈕標籤的文本內容是跟按鈕的狀態向關的
4. 塊動畫
========================================
4.1 首尾式動畫
若是隻是修改控件的屬性,使用首尾式動畫仍是比較方便的,可是若是須要在動畫完成後作後續處理,就不是那麼方便了
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
// 修改屬性的動畫代碼
// ......
[UIView commitAnimations];
4.2 塊動畫
塊動畫相對來講比較靈活,尤其重要的是可以將動畫相關的代碼編寫在一塊兒,便於代碼的閱讀和理解
[UIView animateWithDuration:2.0 animations:^{
// 修改控件屬性動畫
label.alpha = 0.0;
} completion:^(BOOL finished) {
// 刪除控件
[label removeFromSuperview];
}];
5. 字典轉模型
========================================
5.1 字典轉模型的好處:
1> 下降代碼的耦合度
2> 全部字典轉模型部分的代碼統一集中在一到處理,下降代碼出錯的概率
3> 在程序中直接使用模型的屬性操做,提升編碼效率
模型應該提供一個能夠傳入字典參數的構造方法
- (instancetype)initWithDict:(NSDictionary *)dict;
+ (instancetype)xxxWithDict:(NSDictionary *)dict;
5.2 instancetype & id
1> instancetype在類型表示上,跟id同樣,能夠表示任何對象類型
2> instancetype只能用在返回值類型上,不能像id同樣用在參數類型上
3> instancetype比id多一個好處:編譯器會檢測instancetype的真實類型
5.3 在模型中添加readonly屬性
// 定義屬性時,會生成getter&setter方法,還會生成一個帶下劃線的成員變量
// 而若是是readonly屬性,則只會生成getter方法,同時沒有成員變量
@property (nonatomic, strong, readonly) UIImage *image;
@interface LFAppInfo()
{
UIImage *_imageABC;
}
- (UIImage *)image
{
if (!_imageABC) {
_imageABC = [UIImage imageNamed:self.icon];
}
return _imageABC;
}
在模型中合理地使用只讀屬性,能夠進一步下降代碼的耦合度。
5.4 使用數據模型的好處:
*** 調用方不用關心模型內部的任何處理細節!
6. XIB
========================================
Xib文件能夠用來描述某一塊局部的UI界面
XIB & Storyboard
相同點:
1> 都用來描述軟件界面
2> 都用Interface Builder工具來編輯
不一樣點
1> Xib是輕量級的,用來描述局部的UI界面
2> Storyboard是重量級的,用來描述整個軟件的多個界面,而且能展現多個界面之間的跳轉關係
7. View的封裝思路
========================================
1> 若是一個view內部的子控件比較多,通常會考慮自定義一個view,把它內部子控件的建立屏蔽起來,不讓外界關心
2> 外界能夠傳入對應的模型數據給view,view拿到模型數據後給內部的子控件設置對應的數據
快捷鍵
新建
shift + cmd + n 新建項目
cmd + n 新建文件
視圖
option + cmd + 回車 打開助理編輯器
cmd + 回車 顯示主窗口
cmd + 0 導航窗口
option + cmd + 0 工具窗口
在.m & .h之間切換 control + cmd + 上/下
按照瀏覽文件的先後順序切換 control + cmd + 左右
查看頭文件 control + cmd + j
切換到對應的函數 control + 6 支持智能輸入,注意輸入法
運行
cmd + r 運行
cmd + . 中止
cmd + b 編譯
cmd + shift + b 靜態內存分析編譯,能夠檢查程序結構上是否存在內存泄露
排版
control + i 將選中按鈕從新縮進
cmd + ] 向右增長縮進
cmd + [ 向左減小縮進
cmd + / 註釋/取消註釋,提示:取消註釋時,註釋雙斜線必須在行首
cmd + 向上 到文件開始位置
cmd + 向下 到文件末尾位置
UIButton的常見設置
- (void)setTitle:(NSString *)title forState:(UIControlState)state;
設置按鈕的文字
- (void)setTitleColor:(UIColor *)color forState:(UIControlState)state;
設置按鈕的文字顏色
- (void)setImage:(UIImage *)image forState:(UIControlState)state;
設置按鈕內部的小圖片
- (void)setBackgroundImage:(UIImage *)image forState:(UIControlState)state;
設置按鈕的背景圖片
設置按鈕的文字字體(須要拿到按鈕內部的label來設置)
btn.titleLabel.font = [UIFont systemFontOfSize:13];
- (NSString *)titleForState:(UIControlState)state;
得到按鈕的文字
- (UIColor *)titleColorForState:(UIControlState)state;
得到按鈕的文字顏色
- (UIImage *)imageForState:(UIControlState)state;
得到按鈕內部的小圖片
- (UIImage *)backgroundImageForState:(UIControlState)state;
得到按鈕的背景圖片
猜圖筆記
1. 搭建界面
1> 上半部分,固定的,能夠用Storyboard實現
2> 下半部分,根據題目變化,能夠考慮用代碼實現
2. 圖片的放大縮小
1> 放大過程
(1) 增長蒙版(遮罩),蒙版默認alpha = 0
(2) 將圖片移動到視圖的頂層
(3) 計算圖片的目標位置,將蒙版的alpha改成0.5,動畫顯示
2> 縮小過程
(1) 恢復圖片位置,動畫顯示
(2) 隱藏蒙版,將蒙版的默認alpha改成0
提示:若是按鈕的alpha = 0,則不能響應用戶交互
3. 加載數據,字典轉模型(KVC)
1> KVC (key value coding)鍵值編碼
KVC容許間接修改對象的屬性值,是cocoa的大招!
提示:使用setValuesForKeys要求類的屬性必須在字典中存在。類中的屬性能夠比字典中的鍵值多,可是不能少!
2> 下一題按鈕的交互
當到達最後一題時,禁用下一題按鈕。
4. 遊戲的交互部分實現
1> 增長答案視圖和備選答案視圖,簡化按鈕的佈局
利用九宮格算法動態添加按鈕
2> 點擊備選按鈕,文字到上面
3> 點擊答案按鈕,文字到下面
4> 答案的檢測
(1) 錯誤,須要提示用戶
(2) 正確,提示用戶,自動進入下一題
5. 提示功能
6. 首尾工做
1> 圖標
2> 啓動畫面
iTools 將手機中的應用程序導出到電腦上
搜索引擎: 瘋狂猜圖 ipa
KVC KVO
KVC key value Coding 鍵值編碼
KVO key value Observer 鍵值觀察
介紹UIScrollView
1.介紹UIScrollView
2.大圖展現(UIScrollView使用)
1.給scrollView添加內容
2.設置contentSize屬性(只能在代碼中設置)
3.UIScrollView重要屬性
1.contentOffset(經過按鈕改變其位移)
2.contentInset
注意:經過stroryboard設置當即生效,經過代碼設置需配合contentOffect手動位移
3.其餘屬性
4.喜馬拉雅
1.storyboard界面佈局(減小代碼)
2.經過最後按鈕的frame獲取contentSize的height(CGRectGetMaxY(frame))
3.storyboard設置contentInset(使用代碼設置還需設置contentOffset)
5.scrollView代理方法
1.要想成爲代理遵照協議(UIScrollViewDelegate)
1.聲明協議(通常協議名稱:控件名稱 + Delegate)
2.實現協議定義的接口方法
3.設置代理(UIViewController成爲scrollView的代理)
6.scrollView實現縮放功能
1.要想成爲代理遵照協議(UIScrollViewDelegate)
1.聲明協議(通常協議名稱:控件名稱 + Delegate)
2.實現協議定義的接口方法()
2.設置代理(UIViewController成爲scrollView的代理)
3.設置最大、最小縮放倍數(注:倍數相等時,沒法縮放)
7.圖片輪播功能
1.將內容添加到scrollView中(5張圖片)
2.設置scrollView的contentSize
3.設置翻頁屬性
4.添加UIPageControl控件
5.設置UIPageControl的頁碼 (利用contentSize計算頁碼)
6.增長自動滾動(NSTimer實現)
1.介紹UIScrollView的背景
2.介紹scrollView一些屬性(主要有三個)
1>.要想使用scrollView必須作兩件事
1.設置scrollView內容
2.設置contentSize
2>.其餘重要屬性
1.contentOffset(滾動位置)
2.contentInset(注意:在storyborad裏面設置效果不一樣)
3.喜馬拉雅項目
1>.分析頁面結構(scrollView的frame肯定)
2>.在storyboard拖控件
3>.重點scrollView(設置內容而且設置contentSize)
4>.調節scrollView的顯示位置
4.代理
1>代理思想兩個思想
1).監聽思想:B監聽A發生了什麼事情
2).通知思想:A發生了一些事情,要通知B去作
2>scrollView的代理使用
1).如何成爲代理(三步)
*聲明協議
*設置代理對象self.scrollView.delegate = self;
*實現協議方法
2).代理監聽scrollView的拖拽事件
3).用代理實現縮放
*成爲UIScrollView的代理()
*設置縮放對象(經過viewForZoomingInScrollView方法)
*設置縮放爲範圍(maximumZoomScale、minimumZoomScale)
1.圖片輪播(能夠採用兩種維度去分解工做:功能、MVC)
1.UI(分析UI如何實現storyboard、代碼建立)
1.scrollView(有兩件事)
2.圖片(代碼增長到scrollView)
3.UIPageControl(需設置總頁數、當前頁碼)
2.業務
1.拖動(整頁切換,UIScrollView的寬度爲一頁)
2.頁碼設置(當前是第幾頁)
3.自動滾動
4.優化(timer的機制:觸摸式移除,放開時再加進來)
2.智能猜圖擴展(代理)
1.UIAlertView的使用以及常見代理使用 (介紹UIWindow的level模式,level最高是UIWindowLevelStatusBar,能夠覆蓋status bar)
2.UIActionSheetView簡單使用以及代理使用(強調了危險操做,標紅顯示按鈕)
3.應用管理擴展(定義協議並使用)
1.定義協議(三步)
*定義protocol(兩種optional[代理對象可不實現]、required[代理對象必須實現])
*增長代理屬性(weak) @property (weak, nonatomic) id<LFAppInfoViewDelegate> delegate;
*給代理髮消息,調用代理的方法(須要判斷代理對象是否實現了該方法,不判斷調用後(編譯時不會)會報錯)
注意:定義協議的名稱命名[類名+Delegate]、協議方法的命名規範[方法名稱須要去掉前綴,而且將本身做爲參數]
2.使用代理(三步)
*聲明協議
*設置代理對象
*實現協議方法(本例是在代理對象[控制器] 添加一個UILabel)
iOS通知
一個完整的通知通常包含3個屬性:
- (NSString *)name; // 通知的名稱
- (id)object; // 通知發佈者(是誰要發佈通知)
- (NSDictionary *)userInfo; // 一些額外的信息(通知發佈者傳遞給通知接收者的信息內容)
初始化一個通知(NSNotification)對象
+ (instancetype)notificationWithName:(NSString *)aName object:(id)anObject;
+ (instancetype)notificationWithName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;
- (instancetype)initWithName:(NSString *)name object:(id)object userInfo:(NSDictionary *)userInfo;
發佈通知
通知中心(NSNotificationCenter)提供了相應的方法來幫助發佈通知
- (void)postNotification:(NSNotification *)notification;
發佈一個notification通知,可在notification對象中設置通知的名稱、通知發佈者、額外信息等
- (void)postNotificationName:(NSString *)aName object:(id)anObject;
發佈一個名稱爲aName的通知,anObject爲這個通知的發佈者
- (void)postNotificationName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo;
發佈一個名稱爲aName的通知,anObject爲這個通知的發佈者,aUserInfo爲額外信息
註冊通知監聽器
通知中心(NSNotificationCenter)提供了方法來註冊一個監聽通知的監聽器(Observer)
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(NSString *)aName object:(id)anObject;
observer:監聽器,即誰要接收這個通知
aSelector:收到通知後,回調監聽器的這個方法,而且把通知對象當作參數傳入
aName:通知的名稱。若是爲nil,那麼不管通知的名稱是什麼,監聽器都能收到這個通知
anObject:通知發佈者。若是爲anObject和aName都爲nil,監聽器都收到全部的通知
- (id)addObserverForName:(NSString *)name object:(id)obj queue:(NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block;
name:通知的名稱
obj:通知發佈者
block:收到對應的通知時,會回調這個block
queue:決定了block在哪一個操做隊列中執行,若是傳nil,默認在當前操做隊列中同步執行
取消註冊通知監聽器
通知中心不會保留(retain)監聽器對象,在通知中心註冊過的對象,必須在該對象釋放前取消註冊。不然,當相應的通知再次出現時,通知中心仍然會向該監聽器發送消息。由於相應的監聽器對象已經被釋放了,因此可能會致使應用崩潰
通知中心提供了相應的方法來取消註冊監聽器
- (void)removeObserver:(id)observer;
- (void)removeObserver:(id)observer name:(NSString *)aName object:(id)anObject;
通常在監聽器銷燬以前取消註冊(如在監聽器中加入下列代碼):
- (void)dealloc {
//[super dealloc]; 非ARC中須要調用此句
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
通知和代理的選擇
共同點
利用通知和代理都能完成對象之間的通訊
(好比A對象告訴D對象發生了什麼事情, A對象傳遞數據給D對象)
不一樣點
代理 : 一對一關係(1個對象只能告訴另1個對象發生了什麼事情)
通知 : 多對多關係(1個對象能告訴N個對象發生了什麼事情, 1個對象能得知N個對象發生了什麼事情)
鍵盤通知
咱們常常須要在鍵盤彈出或者隱藏的時候作一些特定的操做,所以須要監聽鍵盤的狀態
鍵盤狀態改變的時候,系統會發出一些特定的通知
UIKeyboardWillShowNotification // 鍵盤即將顯示
UIKeyboardDidShowNotification // 鍵盤顯示完畢
UIKeyboardWillHideNotification // 鍵盤即將隱藏
UIKeyboardDidHideNotification // 鍵盤隱藏完畢
UIKeyboardWillChangeFrameNotification // 鍵盤的位置尺寸即將發生改變
UIKeyboardDidChangeFrameNotification // 鍵盤的位置尺寸改變完畢
系統發出鍵盤通知時,會附帶一下跟鍵盤有關的額外信息(字典),字典常見的key以下:
UIKeyboardFrameBeginUserInfoKey // 鍵盤剛開始的frame
UIKeyboardFrameEndUserInfoKey // 鍵盤最終的frame(動畫執行完畢後)
UIKeyboardAnimationDurationUserInfoKey // 鍵盤動畫的時間
UIKeyboardAnimationCurveUserInfoKey // 鍵盤動畫的執行節奏(快慢)
UIDevice通知
UIDevice類提供了一個單粒對象,它表明着設備,經過它能夠得到一些設備相關的信息,好比電池電量值(batteryLevel)、電池狀態(batteryState)、設備的類型(model,好比iPod、iPhone等)、設備的系統(systemVersion)
經過[UIDevice currentDevice]能夠獲取這個單粒對象
UIDevice對象會不間斷地發佈一些通知,下列是UIDevice對象所發佈通知的名稱常量:
UIDeviceOrientationDidChangeNotification // 設備旋轉
UIDeviceBatteryStateDidChangeNotification // 電池狀態改變
UIDeviceBatteryLevelDidChangeNotification // 電池電量改變
UIDeviceProximityStateDidChangeNotification // 近距離傳感器(好比設備貼近了使用者的臉部)
建立頭部視圖
//建立頭部視圖
+(instancetype)headerViewWithTableView:(UITableView *)tableView{
return [[self alloc]initWithTableView:tableView];
}
-(instancetype)initWithTableView:(UITableView *)tableView{
static NSString * indentifier = @"header";
GYLHeaderView * headerView = [tableView dequeueReusableCellWithIdentifier:indentifier];
if (headerView == nil) {
headerView = [[GYLHeaderView alloc]initWithReuseIdentifier:indentifier];
}
return headerView;
}
//但凡在init中得到的frame都是 0
-(id)initWithReuseIdentifier:(NSString *)reuseIdentifier
{
if(self =[super initWithReuseIdentifier:reuseIdentifier])
{
//1.添加子控件
//1.1添加按鈕
UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom];
//添加按鈕的點擊事件
[btn addTarget:self action:@selector(btnOnClick:) forControlEvents:UIControlEventTouchDragInside];
//設置按鈕的背景圖片
[btn setBackgroundImage:[UIImage imageNamed:@"buddy_header_bg"] forState:UIControlStateNormal];
[btn setBackgroundImage:[UIImage imageNamed:@"buddy_header_bg_highlighted"] forState:UIControlStateHighlighted];
//設置按鈕的上的尖尖的圖片
[btn setImage:[UIImage imageNamed:@"buddy_header_arrow"] forState:UIControlStateNormal];
//設置按鈕的內容左對齊
btn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
//2.設置按鈕的內邊距,這樣按鈕的內容距離左邊就有必定的距離
btn.contentEdgeInsets = UIEdgeInsetsMake(0, 20, 0, 0);
//3.設置按鈕色標題和圖片之間的距離
btn.titleEdgeInsets = UIEdgeInsetsMake(0, 20, 0, 0);
// btn.imageEdgeInsets 這是設置圖片的距離
[btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
//設置btn中的圖片不填充整個imageView
btn.imageView.contentMode = UIViewContentModeCenter;
//超出的部分不要進行剪切
// btn.imageView.clipsToBounds = NO;
btn.imageView.layer.masksToBounds = NO;
[self addSubview:btn];
self.btn = btn ;
//1.2添加label
UILabel * label = [[UILabel alloc]init];
// label.backgroundColor = [UIColor grayColor];
//設置文本右對齊
label.textAlignment = NSTextAlignmentRight;
label.textColor = [UIColor grayColor];
[self addSubview:label];
self.label = label;
}
return self;
}
// 該方法在控件的frame被改變的時候就會調用
// 該方法通常用於調整子控件的位置
- (void)layoutSubviews
{
#warning 切記重寫layoutSubviews方法必定要調用父類的layoutSubviews
[super layoutSubviews];
// 1.設置按鈕的frame
self.btn.frame = self.bounds;
// 2.設置label的frame
CGFloat padding = 20;// 間隙
CGFloat labelY = 0;
CGFloat labelH = self.bounds.size.height;
CGFloat labelW = 150;
CGFloat labelX = self.bounds.size.width - padding - labelW;
self.label.frame = CGRectMake(labelX, labelY, labelW, labelH);
}
- (void)btnOnClick:(UIButton *)btn
{
NSLog(@"按鈕被點擊了");
// 1.修改組模型的isOpen屬性
// 修改模型數據數據
self.qqGroup.open = !self.qqGroup.isOpen;
// 2. 刷新表格(通知代理)
if ([self.delegate respondsToSelector:@selector(headerViewDidClickHeaderView:)]) {
[self.delegate headerViewDidClickHeaderView:self];
}
// 3.修改btn上圖片,讓圖片旋轉
// self.btn.imageView.transform = CGAffineTransformMakeRotation(M_PI_2);
// NSLog(@"%p %@", self, self.qqGroup.name);
}
#pragma mark - 當一個控件被添加到其它視圖上的時候會調用如下方法
// 已經被添加到父視圖上的時候會調用
- (void)didMoveToSuperview
{
// 在這個方法中就快要拿到最新的被添加到tableview上的頭部視圖修改它的圖片
if (self.qqGroup.isOpen) {
self.btn.imageView.transform = CGAffineTransformMakeRotation(M_PI_2);
}
}
// 即將被添加到父視圖上的時候會調用
- (void)willMoveToSuperview:(UIView *)newSuperview
{
// NSLog(@"willMoveToSuperview");
}
- (void)setQqGroup:(GYLGroupModel *)qqGroup
{
_qqGroup = qqGroup;
// 1.設置按鈕上的文字
[self.btn setTitle:_qqGroup.name forState:UIControlStateNormal];
// 2.設置在線人數
self.label.text = [NSString stringWithFormat:@"%@/%d", _qqGroup.online, (int)_qqGroup.friends.count];
}
UIApplication的特徵
UIApplication對象是應用程序的象徵
每個應用都有本身的UIApplication對象,並且是單例的
經過[UIApplication sharedApplication]能夠得到這個單例對象
一個iOS程序啓動後建立的第一個對象就是UIApplication對象
利用UIApplication對象,能進行一些應用級別的操做
…
設置應用程序圖標右上角的紅色提醒數字
@property(nonatomic) NSInteger applicationIconBadgeNumber;
設置聯網指示器的可見性
@property(nonatomic,getter=isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible;
從iOS7開始,系統提供了2種管理狀態欄的方式
經過UIViewController管理(每個UIViewController均可以擁有本身不一樣的狀態欄)
經過UIApplication管理(一個應用程序的狀態欄都由它統一管理)
在iOS7中,默認狀況下,狀態欄都是由UIViewController管理的,UIViewController實現下列方法就能夠輕鬆管理狀態欄的可見性和樣式
狀態欄的樣式
- (UIStatusBarStyle)preferredStatusBarStyle;
狀態欄的可見性
(BOOL)prefersStatusBarHidden;
…
UIApplication有個功能十分強大的openURL:方法
- (BOOL)openURL:(NSURL*)url;
openURL:方法的部分功能有
打電話
UIApplication *app = [UIApplication sharedApplication];
[app openURL:[NSURL URLWithString:@"tel://10086"]];
發短信
[app openURL:[NSURL URLWithString:@"sms://10086"]];
發郵件
[app openURL:[NSURL URLWithString:@"mailto://12345@qq.com"]];
打開一個網頁資源
[app openURL:[NSURL URLWithString:@"http://blog.csdn.net/guoyule2010"]];
打開其餘app程序
…
UIPickerView的常見屬性
一.UIPickerView
1.UIPickerView的常見屬性
// 數據源(用來告訴UIPickerView有多少列多少行)
@property(nonatomic,assign) id<UIPickerViewDataSource> dataSource;
// 代理(用來告訴UIPickerView每1列的每1行顯示什麼內容,監聽UIPickerView的選擇)
@property(nonatomic,assign) id<UIPickerViewDelegate> delegate;
// 是否要顯示選中的指示器
@property(nonatomic) BOOL showsSelectionIndicator;
// 一共有多少列
@property(nonatomic,readonly) NSInteger numberOfComponents;
2.UIPickerView的常見方法
// 從新刷新全部列
- (void)reloadAllComponents;
// 從新刷新第component列
- (void)reloadComponent:(NSInteger)component;
// 主動選中第component列的第row行
- (void)selectRow:(NSInteger)row inComponent:(NSInteger)component animated:(BOOL)animated;
// 得到第component列的當前選中的行號
- (NSInteger)selectedRowInComponent:(NSInteger)component;
3.數據源方法(UIPickerViewDataSource)
// 一共有多少列
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView;
// 第component列一共有多少行
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component;
4.代理方法(UIPickerViewDelegate)
// 第component列的寬度是多少
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component;
// 第component列的行高是多少
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component;
// 第component列第row行顯示什麼文字
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component;
// 第component列第row行顯示怎樣的view(內容)
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view;
// 選中了pickerView的第component列第row行
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component;
二.UIDatePicker
1.常見屬性
// datePicker的顯示模式
@property (nonatomic) UIDatePickerMode datePickerMode;
// 顯示的區域語言
@property (nonatomic, retain) NSLocale *locale;
2.監聽UIDatePicker的選擇
* 由於UIDatePicker繼承自UIControl,因此經過addTarget:...監聽
三.程序啓動的完整過程
1.main函數
2.UIApplicationMain
* 建立UIApplication對象
* 建立UIApplication的delegate對象
3.delegate對象開始處理(監聽)系統事件(沒有storyboard)
* 程序啓動完畢的時候, 就會調用代理的application:didFinishLaunchingWithOptions:方法
* 在application:didFinishLaunchingWithOptions:中建立UIWindow
* 建立和設置UIWindow的rootViewController
* 顯示窗口
3.根據Info.plist得到最主要storyboard的文件名,加載最主要的storyboard(有storyboard)
* 建立UIWindow
* 建立和設置UIWindow的rootViewController
* 顯示窗口
UIWindow
1.UIWindow
* 主窗口的概念
* 新建UIWindow
2.UIViewController
* 控制器的建立方式
* 控制器view的建立方式
* view的懶加載
* loadView、viewDidLoad、viewDidUnload、didReceiveMemoryWarning
3.UINavigationController
* 經過「設置」演示基本用途
* 經過非storyboard方式,感覺導航的做用
1> 建立導航控制器
2> 設置UIWindow的根控制器
3> push 1個、2個、3個 子控制器
4> 解釋push的原理(棧、導航控制器的管理過程)
5> 棧底、棧頂控制器的概念
6> 如何設置導航欄上面的內容、返回文字的設置
7> pop的方法使用
8> push和addChild、viewControllers和childViewController的關係
* 經過storyboard方式,感覺導航的做用
4.UIViewController的生命週期方法、AppDelegate的生命週期方法
如何建立一個控制器
控制器常見的建立方式有如下幾種
經過storyboard建立
直接建立
控制器常見的建立方式有如下幾種
經過storyboard建立
直接建立
GYLViewController *nj = [[GYLViewController alloc] init];
指定xib文件來建立
GYLViewController *nj = [[GYLViewController alloc] initWithNibName:@」GYLViewController" bundle:nil];
先加載storyboard文件(Test是storyboard的文件名)
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Test" bundle:nil];
接着初始化storyboard中的控制器
初始化「初始控制器」(箭頭所指的控制器)
GYLViewController *nj = [storyboard instantiateInitialViewController];
經過一個標識初始化對應的控制器
GYLViewController *nj = [storyboard instantiateViewControllerWithIdentifier:@」nj"];
控制器view的延遲加載
控制器的view是延遲加載的:用到時再加載
能夠用isViewLoaded方法判斷一個UIViewController的view是否已經被加載
控制器的view加載完畢就會調用viewDidLoad方法
多控制器
一個iOS的app不多隻由一個控制器組成,除非這個app極其簡單
當app中有多個控制器的時候,咱們就須要對這些控制器進行管理
有多個view時,能夠用一個大的view去管理1個或者多個小view
控制器也是如此,用1個控制器去管理其餘多個控制器
好比,用一個控制器A去管理3個控制器B、C、D
控制器A被稱爲控制器B、C、D的「父控制器」
控制器B、C、D的被稱爲控制器A的「子控制器」
爲了便於管理控制器,iOS提供了2個比較特殊的控制器
UINavigationController
UITabBarController
UINavigationController的簡單使用
UINavigationController以棧的形式保存子控制器
@property(nonatomic,copy) NSArray *viewControllers;
@property(nonatomic,readonly) NSArray *childViewControllers;
使用push方法能將某個控制器壓入棧
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated;
使用pop方法能夠移除控制器
將棧頂的控制器移除
- (UIViewController *)popViewControllerAnimated:(BOOL)animated;
回到指定的子控制器
- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated;
回到根控制器(棧底控制器)
- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated;
如何修改導航欄的內容
導航欄的內容由棧頂控制器的navigationItem屬性決定
UINavigationItem有如下屬性影響着導航欄的內容
左上角的返回按鈕
@property(nonatomic,retain) UIBarButtonItem *backBarButtonItem;
中間的標題視圖
@property(nonatomic,retain) UIView *titleView;
中間的標題文字
@property(nonatomic,copy) NSString *title;
左上角的視圖
@property(nonatomic,retain) UIBarButtonItem *leftBarButtonItem;
UIBarButtonItem *rightBarButtonItem 右上角的視圖
@property(nonatomic,retain) UIBarButtonItem *rightBarButtonItem;
Segue的屬性
每個Segue對象,都有3個屬性
惟一標識
@property (nonatomic, readonly) NSString *identifier;
來源控制器
@property (nonatomic, readonly) id sourceViewController;
目標控制器
@property (nonatomic, readonly) id destinationViewController;
Segue的類型
根據Segue的執行(跳轉)時刻,Segue能夠分爲2大類型
自動型:點擊某個控件後(好比按鈕),自動執行Segue,自動完成界面跳轉
手動型:須要經過寫代碼手動執行Segue,才能完成界面跳轉
自動型Segue
按住Control鍵,直接從控件拖線到目標控制器
點擊「登陸」按鈕後,就會自動跳轉到右邊的控制器
若是點擊某個控件後,不須要作任何判斷,必定要跳轉到下一個界面,建議使用「自動型Segue」
手動型Segue
按住Control鍵,歷來源控制器拖線到目標控制器
手動型的Segue須要設置一個標識(如右圖)
在恰當的時刻,使用perform方法執行對應的Segue
[self performSegueWithIdentifier:@"login2contacts" sender:nil];
// Segue必須由來源控制器來執行,也就是說,這個perform方法必須由來源控制器來調用
若是點擊某個控件後,須要作一些判斷,也就是說:知足必定條件後才跳轉到下一個界面,建議使用「手動型Segue」
performSegueWithIdentifier:sender:
利用performSegueWithIdentifier:方法能夠執行某個Segue,完成界面跳轉
接下來研究performSegueWithIdentifier:sender:方法的完整執行過程
[self performSegueWithIdentifier:@「login2contacts」 sender:nil];
// 這個self是來源控制器
根據identifier去storyboard中找到對應的線,新建UIStoryboardSegue對象
設置Segue對象的sourceViewController(來源控制器)
新建而且設置Segue對象的destinationViewController(目標控制器)
performSegueWithIdentifier:sender:
調用sourceViewController的下面方法,作一些跳轉前的準備工做而且傳入建立好的Segue對象
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;
// 這個sender是當初performSegueWithIdentifier:sender:中傳入的sender
調用Segue對象的- (void)perform;方法開始執行界面跳轉操做
取得sourceViewController所在的UINavigationController
調用UINavigationController的push方法將destinationViewController壓入棧中,完成跳轉
Sender參數的傳遞
[self performSegueWithIdentifier:@「login2contacts」 sender:@「jack」];
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;
控制器的數據傳遞
控制器之間的數據傳遞主要有2種狀況:順傳和逆傳
順傳
控制器的跳轉方向: A C
數據的傳遞方向 : A C
數據的傳遞方式 : 在A的prepareForSegue:sender:方法中根據segue參數取得destinationViewController, 也就是控制器C, 直接給控制器C傳遞數據
(要在C的viewDidLoad方法中取得數據,來賦值給界面上的UI控件)
逆傳
控制器的跳轉方向: A C
數據的傳遞方向 : C A
數據的傳遞方式 : 讓A成爲C的代理, 在C中調用A的代理方法,經過代理方法的參數傳遞數據給A
面試
1. 字符串
若是IDE沒有代碼自動補全功能,因此你應該記住下面的這些方法。
toCharArray() // 得到字符串對應的char數組
Arrays.sort() // 數組[排序](/tag/%e6%8e%92%e5%ba%8f "View all posts in 排序")
Arrays.toString(char[] a) // 數組轉成字符串
charAt(int x) // 得到某個索引處的字符
length() // 字符串長度
length // 數組大小
2. 鏈表
在Java中,鏈表的實現很是簡單,每一個節點Node都有一個值val和指向下個節點的連接next。
class Node {
int val;
Node next;
Node(int x) {
val = x;
next = null;
}
}
棧:
class Stack{
Node top;
public Node peek(){
if(top != null){
return top;
}
return null;
}
public Node pop(){
if(top == null){
return null;
}else{
Node temp = new Node(top.val);
top = top.next;
return temp;
}
}
public void push(Node n){
if(n != null){
n.next = top;
top = n;
}
}
}
隊列:
class Queue{
Node first, last;
public void enqueue(Node n){
if(first == null){
first = n;
last = first;
}else{
last.next = n;
last = n;
}
}
public Node dequeue(){
if(first == null){
return null;
}else{
Node temp = new Node(first.val);
first = first.next;
return temp;
}
}
}
3. 樹
這裏的樹一般是指二叉樹,每一個節點都包含一個左孩子節點和右孩子節點,像下面這樣:
class TreeNode{
int value;
TreeNode left;
TreeNode right;
}
下面是與樹相關的一些概念:
譯者注:完美二叉樹也隱約稱爲徹底二叉樹。完美二叉樹的一個例子是一我的在給定深度的祖先圖,由於每一個人都必定有兩個生父母。徹底二叉樹能夠當作是能夠有若干額外向左靠的葉子節點的完美二叉樹。疑問:完美二叉樹和滿二叉樹的區別?(參考:http://xlinux.nist.gov/dads/HTML/perfectBinaryTree.html)
4. 圖
圖相關的問題主要集中在深度優先搜索(depth first search)和廣度優先搜索(breath first search)。
下面是一個簡單的圖廣度優先搜索的實現。
1) 定義GraphNode
class GraphNode{
int val;
GraphNode next;
GraphNode[] neighbors;
boolean visited;
GraphNode(int x) {
val = x;
}
GraphNode(int x, GraphNode[] n){
val = x;
neighbors = n;
}
public String toString(){
return "value: "+ this.val;
}
}
2) 定義一個隊列Queue
class Queue{
GraphNode first, last;
public void enqueue(GraphNode n){
if(first == null){
first = n;
last = first;
}else{
last.next = n;
last = n;
}
}
public GraphNode dequeue(){
if(first == null){
return null;
}else{
GraphNode temp = new GraphNode(first.val, first.neighbors);
first = first.next;
return temp;
}
}
}
3) 用隊列Queue實現廣度優先搜索
public class GraphTest {
public static void main(String[] args) {
GraphNode n1 = new GraphNode(1);
GraphNode n2 = new GraphNode(2);
GraphNode n3 = new GraphNode(3);
GraphNode n4 = new GraphNode(4);
GraphNode n5 = new GraphNode(5);
n1.neighbors = new GraphNode[]{n2,n3,n5};
n2.neighbors = new GraphNode[]{n1,n4};
n3.neighbors = new GraphNode[]{n1,n4,n5};
n4.neighbors = new GraphNode[]{n2,n3,n5};
n5.neighbors = new GraphNode[]{n1,n3,n4};
breathFirstSearch(n1, 5);
}
public static void breathFirstSearch(GraphNode root, int x){
if(root.val == x)
System.out.println("find in root");
Queue queue = new Queue();
root.visited = true;
queue.enqueue(root);
while(queue.first != null){
GraphNode c = (GraphNode) queue.dequeue();
for(GraphNode n: c.neighbors){
if(!n.visited){
System.out.print(n + " ");
n.visited = true;
if(n.val == x)
System.out.println("Find "+n);
queue.enqueue(n);
}
}
}
}
}
Output:
value: 2 value: 3 value: 5 Find value: 5
value: 4
5. 排序
下面是不一樣排序算法的時間複雜度,你能夠去wiki看一下這些算法的基本思想。
Algorithm |
Average Time |
Worst Time |
Space |
冒泡排序 |
n^2 |
n^2 |
1 |
選擇排序 |
n^2 |
n^2 |
1 |
Counting Sort |
n+k |
n+k |
n+k |
Insertion sort |
n^2 |
n^2 |
|
Quick sort |
n log(n) |
n^2 |
|
Merge sort |
n log(n) |
n log(n) |
depends |
另外,這裏有一些實現/演示:: Counting sort、Mergesort、 Quicksort、 InsertionSort。
對程序員來講,遞歸應該是一個與生俱來的思想(a built-in thought),能夠經過一個簡單的例子來講明。
問題: 有n步臺階,一次只能上1步或2步,共有多少種走法。
步驟1:找到走完前n步臺階和前n-1步臺階之間的關係。
爲了走完n步臺階,只有兩種方法:從n-1步臺階爬1步走到或從n-2步臺階處爬2步走到。若是f(n)是爬到第n步臺階的方法數,那麼f(n) = f(n-1) + f(n-2)。
步驟2: 確保開始條件是正確的。
f(0) = 0;
f(1) = 1;
public static int f(int n){
if(n <= 2) return n;
int x = f(n-1) + f(n-2);
return x;
}
遞歸方法的時間複雜度是n的指數級,由於有不少冗餘的計算,以下:
f(5)
f(4) + f(3)
f(3) + f(2) + f(2) + f(1)
f(2) + f(1) + f(1) + f(0) + f(1) + f(0) + f(1)
f(1) + f(0) + f(1) + f(1) + f(0) + f(1) + f(0) + f(1)
直接的想法是將遞歸轉換爲迭代:
public static int f(int n) {
if (n <= 2){
return n;
}
int first = 1, second = 2;
int third = 0;
for (int i = 3; i <= n; i++) {
third = first + second;
first = second;
second = third;
}
return third;
}
對這個例子而言,迭代花費的時間更少,你可能也想看看Recursion vs Iteration。
7. 動態規劃
動態規劃是解決下面這些性質類問題的技術:
爬臺階問題徹底符合上面的四條性質,所以能夠用動態規劃法來解決。
public static int[] A = new int[100];
public static int f3(int n) {
if (n <= 2)
A[n]= n;
if(A[n] > 0)
return A[n];
else
A[n] = f3(n-1) + f3(n-2);//store results so only calculate once!
return A[n];
}
**8. 位操做
**
位操做符:
OR (|) |
AND (&) |
XOR (^) |
Left Shift (<<) |
Right Shift (>>) |
Not (~) |
1|0=1 |
1&0=0 |
1^0=1 |
0010<<2=1000 |
1100>>2=0011 |
~1=0 |
得到給定數字n的第i位:(i從0計數並從右邊開始)
public static boolean getBit(int num, int i){
int result = num & (1<<i);
if(result == 0){
return false;
}else{
return true;
}
例如,得到數字10的第2位:
i=1, n=10
1<<1= 10
1010&10=10
10 is not 0, so return true;
9. 機率問題
解決機率相關的問題一般須要很好的規劃瞭解問題(formatting the problem),這裏恰好有一個這類問題的簡單例子:
一個房間裏有50我的,那麼至少有兩我的生日相同的機率是多少?(忽略閏年的事實,也就是一年365天)
計算某些事情的機率不少時候均可以轉換成先計算其相對面。在這個例子裏,咱們能夠計算全部人生日都互不相同的機率,也就是:365/365 * 364/365 * 363/365 * … * (365-49)/365,這樣至少兩我的生日相同的機率就是1 – 這個值。
public static double caculateProbability(int n){
double x = 1;
for(int i=0; i<n; i++){
x *= (365.0-i)/365.0;
}
double pro = Math.round((1-x) * 100);
return pro/100;
calculateProbability(50) = 0.97
10. 排列組合
組合和排列的區別在於次序是否關鍵。
若是你有任何問題請在下面評論。
參考/推薦資料:
1. Binary tree
2. Introduction to Dynamic Programming
3. UTSA Dynamic Programming slides
數據存取
iOS應用數據存儲的經常使用方式
XML屬性列表(plist)歸檔
Preference(偏好設置)
NSKeyedArchiver歸檔(NSCoding)
SQLite3
Core Data
應用沙盒
每一個iOS應用都有本身的應用沙盒(應用沙盒就是文件系統目錄),與其餘文件系統隔離。應用必須待在本身的沙盒裏,其餘應用不能訪問該沙盒
應用沙盒的文件系統目錄,以下圖所示(假設應用的名稱叫Layer)
模擬器應用沙盒的根路徑在: (guoyule是用戶名, 8.1是模擬器版本)
/Users/guoyule/Library/Application Support/iPhone Simulator/8./1Applications
應用沙盒結構分析
應用程序包:(上圖中的Layer)包含了全部的資源文件和可執行文件
Documents:保存應用運行時生成的須要持久化的數據,iTunes同步設備時會備份該目錄。例如,遊戲應用可將遊戲存檔保存在該目錄
tmp:保存應用運行時所需的臨時數據,使用完畢後再將相應的文件從該目錄刪除。應用沒有運行時,系統也可能會清除該目錄下的文件。iTunes同步設備時不會備份該目錄
Library/Caches:保存應用運行時生成的須要持久化的數據,iTunes同步設備時不會備份該目錄。通常存儲體積大、不須要備份的非重要數據
Library/Preference:保存應用的全部偏好設置,iOS的Settings(設置)應用會在該目錄中查找應用的設置信息。iTunes同步設備時會備份該目錄
應用沙盒目錄的常見獲取方式
沙盒根目錄:NSString *home = NSHomeDirectory();
Documents:(2種方式)
利用沙盒根目錄拼接」Documents」字符串
NSString *home = NSHomeDirectory();
NSString *documents = [home stringByAppendingPathComponent:@"Documents"];
// 不建議採用,由於新版本的操做系統可能會修改目錄名
利用NSSearchPathForDirectoriesInDomains函數
// NSUserDomainMask 表明從用戶文件夾下找
// YES 表明展開路徑中的波浪字符「~」
NSArray *array = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, NO);
// 在iOS中,只有一個目錄跟傳入的參數匹配,因此這個集合裏面只有一個元素
NSString *documents = [array objectAtIndex:0];
tmp:NSString *tmp = NSTemporaryDirectory();
Library/Caches:(跟Documents相似的2種方法)
利用沙盒根目錄拼接」Caches」字符串
利用NSSearchPathForDirectoriesInDomains函數(將函數的第2個參數改成:NSCachesDirectory便可)
Library/Preference:經過NSUserDefaults類存取該目錄下的設置信息
屬性列表
屬性列表是一種XML格式的文件,拓展名爲plist
若是對象是NSString、NSDictionary、NSArray、NSData、NSNumber等類型,就可使用writeToFile:atomically:方法直接將對象寫到屬性列表文件中
屬性列表-歸檔NSDictionary
將一個NSDictionary對象歸檔到一個plist屬性列表中
// 將數據封裝成字典
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:@"郭玉樂" forKey:@"name"];
[dict setObject:@"18501956963" forKey:@"phone"];
[dict setObject:@"24" forKey:@"age"];
// 將字典持久化到Documents/stu.plist文件中
[dict writeToFile:path atomically:YES];
屬性列表-恢復NSDictionary
讀取屬性列表,恢復NSDictionary對象
// 讀取Documents/stu.plist的內容,實例化NSDictionary
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSLog(@"name:%@", [dict objectForKey:@"name"]);
NSLog(@"phone:%@", [dict objectForKey:@"phone"]);
NSLog(@"age:%@", [dict objectForKey:@"age"]);
偏好設置
不少iOS應用都支持偏好設置,好比保存用戶名、密碼、字體大小等設置,iOS提供了一套標準的解決方案來爲應用加入偏好設置功能
每一個應用都有個NSUserDefaults實例,經過它來存取偏好設置
好比,保存用戶名、字體大小、是否自動登陸
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:@"guoyule" forKey:@"username"];
[defaults setFloat:18.0f forKey:@"text_size"];
[defaults setBool:YES forKey:@"auto_login"];
讀取上次保存的設置
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *username = [defaults stringForKey:@"username"];
float textSize = [defaults floatForKey:@"text_size"];
BOOL autoLogin = [defaults boolForKey:@"auto_login"];
注意:UserDefaults設置數據時,不是當即寫入,而是根據時間戳定時地把緩存中的數據寫入本地磁盤。因此調用了set方法以後數據有可能尚未寫入磁盤應用程序就終止了。出現以上問題,能夠經過調用synchornize方法強制寫入
[defaults synchornize];
NSKeyedArchiver
若是對象是NSString、NSDictionary、NSArray、NSData、NSNumber等類型,能夠直接用NSKeyedArchiver進行歸檔和恢復
不是全部的對象均可以直接用這種方法進行歸檔,只有遵照了NSCoding協議的對象才能夠
NSCoding協議有2個方法:
encodeWithCoder:
每次歸檔對象時,都會調用這個方法。通常在這個方法裏面指定如何歸檔對象中的每一個實例變量,可使用encodeObject:forKey:方法歸檔實例變量
initWithCoder:
每次從文件中恢復(解碼)對象時,都會調用這個方法。通常在這個方法裏面指定如何解碼文件中的數據爲對象的實例變量,可使用decodeObject:forKey方法解碼實例變量
NSKeyedArchiver-歸檔NSArray
歸檔一個NSArray對象到Documents/array.archive
NSArray *array = [NSArray arrayWithObjects:@」a」,@」b」,nil];
[NSKeyedArchiver archiveRootObject:array toFile:path];
恢復(解碼)NSArray對象
NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
NSKeyedArchiver-歸檔Person對象(Person.h)
@interface Person : NSObject<NSCoding>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) float height;
@end
NSKeyedArchiver-歸檔Person對象(Person.m)
@implementation Person
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.name forKey:@"name"];
[encoder encodeInt:self.age forKey:@"age"];
[encoder encodeFloat:self.height forKey:@"height"];
}
- (id)initWithCoder:(NSCoder *)decoder {
self.name = [decoder decodeObjectForKey:@"name"];
self.age = [decoder decodeIntForKey:@"age"];
self.height = [decoder decodeFloatForKey:@"height"];
return self;
}
- (void)dealloc {
[super dealloc];
[_name release];
}
@end
NSKeyedArchiver-歸檔Person對象(編碼和解碼)
歸檔(編碼)
Person *person = [[[Person alloc] init] autorelease];
person.name = @"GUOYULE";
person.age = 24;
person.height = 1.78f;
[NSKeyedArchiver archiveRootObject:person toFile:path];
恢復(解碼)
Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
NSKeyedArchiver-歸檔對象的注意
若是父類也遵照了NSCoding協議,請注意:
應該在encodeWithCoder:方法中加上一句
[super encodeWithCode:encode];
確保繼承的實例變量也能被編碼,即也能被歸檔
應該在initWithCoder:方法中加上一句
self = [super initWithCoder:decoder];
確保繼承的實例變量也能被解碼,即也能被恢復
NSData
使用archiveRootObject:toFile:方法能夠將一個對象直接寫入到一個文件中,但有時候可能想將多個對象寫入到同一個文件中,那麼就要使用NSData來進行歸檔對象
NSData能夠爲一些數據提供臨時存儲空間,以便隨後寫入文件,或者存放從磁盤讀取的文件內容。可使用[NSMutableData data]建立可變數據空間
NSData-歸檔2個Person對象到同一文件中
歸檔(編碼)
// 新建一塊可變數據區
NSMutableData *data = [NSMutableData data];
// 將數據區鏈接到一個NSKeyedArchiver對象
NSKeyedArchiver *archiver = [[[NSKeyedArchiver alloc] initForWritingWithMutableData:data] autorelease];
// 開始存檔對象,存檔的數據都會存儲到NSMutableData中
[archiver encodeObject:person1 forKey:@"person1"];
[archiver encodeObject:person2 forKey:@"person2"];
// 存檔完畢(必定要調用這個方法)
[archiver finishEncoding];
// 將存檔的數據寫入文件
[data writeToFile:path atomically:YES];
NSData-從同一文件中恢復2個Person對象
恢復(解碼)
// 從文件中讀取數據
NSData *data = [NSData dataWithContentsOfFile:path];
// 根據數據,解析成一個NSKeyedUnarchiver對象
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
Person *person1 = [unarchiver decodeObjectForKey:@"person1"];
Person *person2 = [unarchiver decodeObjectForKey:@"person2"];
// 恢復完畢
[unarchiver finishDecoding];
利用歸檔實現深複製
好比對一個Person對象進行深複製
// 臨時存儲person1的數據
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:person1];
// 解析data,生成一個新的Person對象
Student *person2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];
// 分別打印內存地址
NSLog(@"person1:0x%x", person1); // person1:0x7177a60
NSLog(@"person2:0x%x", person2); // person2:0x7177cf0
SQLite3
SQLite3是一款開源的嵌入式關係型數據庫,可移植性好、易使用、內存開銷小
SQLite3是無類型的,意味着你能夠保存任何類型的數據到任意表的任意字段中。好比下列的創表語句是合法的:
create table t_person(name, age);
爲了保證可讀性,建議仍是把字段類型加上:
create table t_person(name text, age integer);
SQLite3經常使用的5種數據類型:text、integer、float、boolean、blob
在iOS中使用SQLite3,首先要添加庫文件libsqlite3.dylib和導入主頭文件
建立、打開、關閉數據庫
建立或打開數據庫
// path爲:~/Documents/person.db
sqlite3 *db;
int result = sqlite3_open([path UTF8String], &db);
代碼解析:
sqlite3_open()將根據文件路徑打開數據庫,若是不存在,則會建立一個新的數據庫。若是result等於常量SQLITE_OK,則表示成功打開數據庫
sqlite3 *db:一個打開的數據庫實例
數據庫文件的路徑必須以C字符串(而非NSString)傳入
關閉數據庫:sqlite3_close(db);
執行不返回數據的SQL語句
執行創表語句
char *errorMsg; // 用來存儲錯誤信息
char *sql = "create table if not exists t_person(id integer primary key autoincrement, name text, age integer);";
int result = sqlite3_exec(db, sql, NULL, NULL, &errorMsg);
代碼解析:
sqlite3_exec()能夠執行任何SQL語句,好比創表、更新、插入和刪除操做。可是通常不用它執行查詢語句,由於它不會返回查詢到的數據
sqlite3_exec()還能夠執行的語句:
開啓事務:begin transaction;
回滾事務:rollback;
提交事務:commit;
帶佔位符插入數據
char *sql = "insert into t_person(name, age) values(?, ?);";
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK) {
sqlite3_bind_text(stmt, 1, "母雞", -1, NULL);
sqlite3_bind_int(stmt, 2, 27);
}
if (sqlite3_step(stmt) != SQLITE_DONE) {
NSLog(@"插入數據錯誤");
}
sqlite3_finalize(stmt);
代碼解析:
sqlite3_prepare_v2()返回值等於SQLITE_OK,說明SQL語句已經準備成功,沒有語法問題
sqlite3_bind_text():大部分綁定函數都只有3個參數
第1個參數是sqlite3_stmt *類型
第2個參數指佔位符的位置,第一個佔位符的位置是1,不是0
第3個參數指佔位符要綁定的值
第4個參數指在第3個參數中所傳遞數據的長度,對於C字符串,能夠傳遞-1代替字符串的長度
第5個參數是一個可選的函數回調,通常用於在語句執行後完成內存清理工做
sqlite_step():執行SQL語句,返回SQLITE_DONE表明成功執行完畢
sqlite_finalize():銷燬sqlite3_stmt *對象
查詢數據
char *sql = "select id,name,age from t_person;";
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK) {
while (sqlite3_step(stmt) == SQLITE_ROW) {
int _id = sqlite3_column_int(stmt, 0);
char *_name = (char *)sqlite3_column_text(stmt, 1);
NSString *name = [NSString stringWithUTF8String:_name];
int _age = sqlite3_column_int(stmt, 2);
NSLog(@"id=%i, name=%@, age=%i", _id, name, _age);
}
}
sqlite3_finalize(stmt);
代碼解析
sqlite3_step()返回SQLITE_ROW表明遍歷到一條新記錄
sqlite3_column_*()用於獲取每一個字段對應的值,第2個參數是字段的索引,從0開始
NSManagedObject
經過Core Data從數據庫取出的對象,默認狀況下都是NSManagedObject對象
NSManagedObject的工做模式有點相似於NSDictionary對象,經過鍵-值對來存取全部的實體屬性
setValue:forKey: 存儲屬性值(屬性名爲key)
valueForKey: 獲取屬性值(屬性名爲key)
搭建Core Data上下文環境
從應用程序包中加載模型文件
NSManagedObjectModel *model = [NSManagedObjectModel mergedModelFromBundles:nil];
傳入模型,初始化NSPersistentStoreCoordinator
NSPersistentStoreCoordinator *psc = [[[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model] autorelease];
構建SQLite文件路徑
NSString *docs = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSURL *url = [NSURL fileURLWithPath:[docs stringByAppendingPathComponent:@"person.data"]];
添加持久化存儲庫,這裏使用SQLite做爲存儲庫
NSError *error = nil;
NSPersistentStore *store = [psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error];
if (store == nil) { // 直接拋異常
[NSException raise:@"添加數據庫錯誤" format:@"%@", [error localizedDescription]];
}
初始化上下文,設置persistentStoreCoordinator屬性
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
context.persistentStoreCoordinator = psc;
// 用完以後,仍是要[context release];
添加數據
傳入上下文,建立一個Person實體對象
NSManagedObject *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];
設置簡單屬性
[person setValue:@"guoyule" forKey:@"name"];
[person setValue:[NSNumber numberWithInt:24] forKey:@"age"];
傳入上下文,建立一個Card實體對象
NSManagedObject *card = [NSEntityDescription insertNewObjectForEntityForName:@"Card" inManagedObjectContext:context];
[card setValue:@"4414241933432" forKey:@"no"];
設置Person和Card之間的關聯關係
[person setValue:card forKey:@"card"];
利用上下文對象,將數據同步到持久化存儲庫
NSError *error = nil;
BOOL success = [context save:&error];
if (!success) {
[NSException raise:@"訪問數據庫錯誤" format:@"%@", [error localizedDescription]];
}
// 若是是想作更新操做:只要在更改了實體對象的屬性後調用[context save:&error],就能將更改的數據同步到數據庫
查詢數據
初始化一個查詢請求
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
設置要查詢的實體
NSEntityDescription *desc = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:context];
設置排序(按照age降序)
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO];
request.sortDescriptors = [NSArray arrayWithObject:sort];
設置條件過濾(name like '%guoyule-1%')
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name like %@", @"*guoyule-1*"];
request.predicate = predicate;
執行請求
NSError *error = nil;
NSArray *objs = [context executeFetchRequest:request error:&error];
if (error) {
[NSException raise:@"查詢錯誤" format:@"%@", [error localizedDescription]];
}
遍歷數據
for (NSManagedObject *obj in objs) {
NSLog(@"name=%@", [obj valueForKey:@"name"]
}
刪除數據
傳入須要刪除的實體對象
[context deleteObject:managedObject];
將結果同步到數據庫
NSError *error = nil;
[context save:&error];
if (error) {
[NSException raise:@"刪除錯誤" format:@"%@", [error localizedDescription]];
}
Core Data的延遲加載
Core Data不會根據實體中的關聯關係當即獲取相應的關聯對象
好比經過Core Data取出Person實體時,並不會當即查詢相關聯的Card實體;當應用真的須要使用Card時,纔會查詢數據庫,加載Card實體的信息
建立NSManagedObject的子類
默認狀況下,利用Core Data取出的實體都是NSManagedObject類型的,可以利用鍵-值對來存取數據
可是通常狀況下,實體在存取數據的基礎上,有時還須要添加一些業務方法來完成一些其餘任務,那麼就必須建立NSManagedObject的子類
建立NSManagedObject的子類
那麼生成一個Person實體對象就應該這樣寫
Person *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];
person.name = @"GUOYULE";
person.age = [NSNumber numberWithInt:24];
Card *card = [NSEntityDescription insertNewObjectForEntityForName:@」Card" inManagedObjectContext:context];
card.no = @」4414245465656";
person.card = card;
數據存儲代碼
- (IBAction)savebtn:(id)sender {
// GYLPerson * p = [[GYLPerson alloc]init];
// p.name = @"guoyule";
// p.age = 24;
// p.hight = 178.0f;
GYLStudent * stu = [[GYLStudent alloc]init];
stu.name = @"guoyule";
stu.age = 24;
stu.hight = 178.0f;
stu.email = @"guoyulehit@icloud.com";
//2.獲取文件路徑
NSString * docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory , NSUserDomainMask , YES) lastObject];
//尾加文件名
NSString * path = [docPath stringByAppendingString:@"guoyule.arc"];
NSLog(@"path = %@",path);
// 3.將本身定義的對象保存到文件中
[NSKeyedArchiver archiveRootObject:stu toFile:path];
}
- (IBAction)readBtn:(id)sender {
NSString * docPth = [NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory,NSUserDomainMask,YES) lastObject];
NSString * path = [docPth stringByAppendingString:@"guoyule.arc"];
// 2.從文件中讀取對象
GYLStudent * guo = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
NSLog(@"%@,%d,%.1f,%@",guo.name,guo.age,guo.hight,guo.email);
}
偏好設置
//
// ViewController.m
// 偏好設置
//
// Created by GuoYule on 15/3/10.
// Copyright (c) 2015年 GuoYule. All rights reserved.
//
#import "ViewController.h"
@interface ViewController ()
- (IBAction)saveBtn:(id)sender;
- (IBAction)readBtn:(id)sender;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)saveBtn:(id)sender {
// 偏好設置是專門用來保存應用程序的配置的信息的,通常狀況下不要在偏好設置中保存其餘的數據
// 若是利用系統的偏好設置來保存數據,默認就是存儲在Preferences文件夾下面的
// 偏好設置會將全部的數據保存到同一個文件夾中
// 獲取默認的NSUserDefaults
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
// 保存數據(若是設置數據以後沒有同步, 會在未來某一時間點自動將數據保存到Preferences文件夾下面)
[defaults setObject:@"guoyule" forKey:@"name"];
[defaults setFloat:178.0 forKey:@"hight"];
[defaults setInteger:24 forKey:@"age"];
// 讓NSUserDefaults立刻保存
[defaults synchronize];
}
- (IBAction)readBtn:(id)sender {
NSUserDefaults * de = [NSUserDefaults standardUserDefaults];
NSLog(@"name = %@,hight = %f,age = %ld",[de objectForKey:@"name"],[de floatForKey:@"hight"],(long)[de integerForKey:@"age"]);
}
@end
modal的注意點
[self dismissViewControllerAnimated:YES completion:^{
NSLog(@"OK!!!");
}];
/*
若是控制器之間的關係比較緊密通常用 UINavigationController
若是控制器之間的關係不是很緊密能夠用Modal
*/
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(@"prepareForSegue");
// 1.取出目標控制器
UINavigationController *nav = segue.destinationViewController;
NJTwoViewController *two = ( NJTwoViewController *)nav.topViewController; // 獲取導航控制器棧頂的控制器
// 2.給目標控制器傳遞數據
two.name = @"lnj";
}
UITabBarController
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 1.建立window
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.backgroundColor = [UIColor whiteColor];
// 2.給window設置根控制器
UITabBarController * tab = [[UITabBarController alloc]init];
// 2.設置UITabBarController爲winow的根控制器
self.window.rootViewController = tab;
//建立並添加自子控制器
UIViewController * vc1 = [[UIViewController alloc]init];
vc1.tabBarItem.title = @"消息";
vc1.tabBarItem.image = [UIImage imageNamed:@"tab_recent_nor"];
vc1.tabBarItem.badgeValue = @"111";
vc1.view.backgroundColor = [UIColor blackColor];
UIViewController * vc2 = [[UIViewController alloc]init];
vc2.tabBarItem.title = @"聯繫人";
vc2.tabBarItem.image = [UIImage imageNamed:@"tab_buddy_nor"];
// vc2.tabBarItem.badgeValue = @"111";
vc2.view.backgroundColor = [UIColor blueColor];
UIViewController * vc3 = [[UIViewController alloc]init];
vc3.tabBarItem.title = @"空間";
vc3.tabBarItem.image = [UIImage imageNamed:@"tab_qworld_nor"];
vc3.tabBarItem.badgeValue = @"11";
vc3.view.backgroundColor = [UIColor greenColor];
UIViewController * vc4 = [[UIViewController alloc]init];
vc4.tabBarItem.title = @"設置";
vc4.tabBarItem.image = [UIImage imageNamed:@"tab_me_nor"];
// vc4.tabBarItem.badgeValue = @"111";
vc4.view.backgroundColor = [UIColor grayColor];
[tab addChildViewController:vc1];
[tab addChildViewController:vc2];
[tab addChildViewController:vc3];
[tab addChildViewController:vc4];
[self.window makeKeyAndVisible];
return YES;
}
tableView的刷新
1.tableView的刷新
1> 數據刷新的整體步驟
* 修改模型數據
* 刷新表格(刷新界面)
2> 刷新表格(刷新界面)的方法
* 全局刷新(每一行都會從新刷新)
- (void)reloadData;
* 局部刷新(使用前提: 刷新先後, 模型數據的個數不變)
- (void)reloadRows:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
* 局部刪除(使用前提: 模型數據減小的個數 == indexPaths的長度)
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
2.@property屬性的用法
* weak(assign) : 代理\UI控件
* strong(retain) : 其餘對象(除代理\UI控件\字符串之外的對象)
* copy : 字符串
* assign : 非對象類型(基本數據類型int\float\BOOL\枚舉\結構體)
繪圖
- (void)drawRect:(CGRect)rect {
// 這裏是Layer Graphics Context
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 繪製圖形
// 設置起點
CGContextMoveToPoint(ctx, 10, 10);
// 設置終點 ctx 是上下文
CGContextAddLineToPoint(ctx, 10, 100);
// 繪製圖形(渲染到layer上)view
CGContextStrokePath(ctx);
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
// 得到圖形的上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 繪製圖形的起點
CGContextMoveToPoint(ctx, 50, 0);
// 設置第二個點
CGContextAddLineToPoint(ctx, 0, 100);
// 設置第三個點
CGContextAddLineToPoint(ctx, 100, 100);
// 進行封口操做
CGContextClosePath(ctx);
// 渲染
CGContextStrokePath(ctx);
CGContextAddRect(ctx, CGRectMake(50, 100, 50, 100));
[[UIColor redColor]set];
// CGContextStrokePath(ctx);
CGContextFillPath(ctx);
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
//當自定義的view即將顯示的時候 就會調用這個方法
- (void)drawRect:(CGRect)rect {
// 這裏是Layer Graphics Context
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 繪製圖形
// 設置起點
CGContextMoveToPoint(ctx, 10, 10);
// 設置終點 ctx 是上下文
CGContextAddLineToPoint(ctx, 10, 100);
CGContextAddLineToPoint(ctx, 100, 100);
// 設置繪圖狀態
// 設置線條顏色
CGContextSetRGBStrokeColor(ctx, 1.0, 0, 0, 1.0);
// 設置線條寬度
CGContextSetLineWidth(ctx, 10.2);
// 設置線條圓頭
CGContextSetLineCap(ctx, kCGLineCapRound);
// 設置線條轉角的圓形
CGContextSetLineJoin(ctx, kCGLineJoinRound);
// 繪製圖形(渲染到layer上)view
CGContextStrokePath(ctx);
}
新建一個起點
void CGContextMoveToPoint(CGContextRef c, CGFloat x, CGFloat y)
添加新的線段到某個點
void CGContextAddLineToPoint(CGContextRef c, CGFloat x, CGFloat y)
添加一個矩形
void CGContextAddRect(CGContextRef c, CGRect rect)
添加一個橢圓
void CGContextAddEllipseInRect(CGContextRef context, CGRect rect)
添加一個圓弧
void CGContextAddArc(CGContextRef c, CGFloat x, CGFloat y,
CGFloat radius, CGFloat startAngle, CGFloat endAngle, int clockwise)
繪製平行線
/**
* 繪製平行線
*
* @param rect <#rect description#>
*/
- (void)drawRect:(CGRect)rect {
// 注意這是一個C語言的函數 沒有*
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGPoint addLines[] = {CGPointMake(10.0, 200),CGPointMake(50.0, 100),CGPointMake(90.0, 200),CGPointMake(130.0, 100),CGPointMake(170.0, 200),CGPointMake(210.0, 100)};
// 繪製
CGContextStrokeLineSegments(ctx, addLines, sizeof(addLines)/sizeof(addLines[0]));
}
漸變
- (void)drawRect:(CGRect)rect {
// 繪製漸變色
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGColorSpaceRef color = CGColorSpaceCreateDeviceRGB();//建立色彩空間
// 開始設置顏色
UIColor * start = [UIColor blueColor];
CGFloat * startColorComp = (CGFloat *)CGColorGetComponents([start CGColor]);
// 設置結束顏色
UIColor *end = [UIColor yellowColor];
CGFloat * endColorComp = (CGFloat *)CGColorGetComponents([end CGColor]);
// 建立顏色份量數組
CGFloat colorComponents[8] = {
startColorComp[0],startColorComp[1],startColorComp[2],startColorComp[3],endColorComp[0],endColorComp[1],endColorComp[2],endColorComp[3],
};
// 指定漸變開始位置和漸變結束位置
CGFloat colorIndices[2] = {0.0f ,1.0f,};
// 建立漸變
CGGradientRef gradient = CGGradientCreateWithColorComponents(color, (const CGFloat *)&colorComponents, (const CGFloat *)&colorIndices, 2);
CGPoint startPoint ,endPoint;
startPoint = CGPointMake(120, 260);
endPoint = CGPointMake(200.0, 200);
CGContextDrawLinearGradient(ctx, gradient, startPoint, endPoint, 0);
}
圖片的添加
- (void)drawRect:(CGRect)rect {
UIImage * image = [UIImage imageNamed:@"abc"];
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect re = CGRectMake(60, 60, 60, 60);
CGContextClipToRect(ctx, CGRectMake(0, 0, 320, 480));
CGContextDrawTiledImage(ctx, re, image.CGImage);
}
文字處理
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
/**
* CGTextDrawingMode mode 枚舉kCGTextFill,
kCGTextStroke,描邊
kCGTextFillStroke, 描邊加填充
kCGTextInvisible,
kCGTextFillClip, kCGTextFill,填充
kCGTextStrokeClip,
kCGTextFillStrokeClip,
kCGTextClip
*/
// CGContextSetTextDrawingMode(<#CGContextRef c#>, CGTextDrawingMode mode)
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 取得系統的字體
CGFontRef font = CGFontCreateWithFontName((CFStringRef)@"Helvetica");
// 自定義字體
CGContextSetFont(ctx, font);
// 設置字體字號
CGContextSetFontSize(ctx, 100.0);
CGContextSetRGBFillColor(ctx, 0.0, 1.0, 0, 1.0);
CGContextSetRGBStrokeColor(ctx, 1.0, 1.0, 0, 1.0);
CGContextSetTextMatrix(ctx, CGAffineTransformMakeScale(1.0, -1.0));
CGPoint point[] = {
CGPointMake(20, -120),
CGPointMake(120, -200),
CGPointMake(220, -300),
};
CGGlyph glyphs[23] ={90,91,92};
// 填充
CGContextSetTextDrawingMode(ctx, kCGTextFill);
CGContextShowGlyphsAtPositions(ctx, &glyphs[0], &point[0], 1);
// 描邊
CGContextSetTextDrawingMode(ctx, kCGTextStroke);
CGContextShowGlyphsAtPositions(ctx, &glyphs[1], &point[1], 1);
//
CGContextSetTextDrawingMode(ctx, kCGTextFillStrokeClip);
CGContextShowGlyphsAtPositions(ctx, &glyphs[2], &point[2], 1);
}
OC 畫圖
#import "GYLcircle.h"
@implementation GYLcircle
- (void)drawRect:(CGRect)rect
{
// 1.獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 畫圓
CGContextAddArc(ctx, 100, 100, 50, 0, 2 * M_PI, 0);
// 3.渲染 (注意, 畫線只能經過空心來畫)
CGContextFillPath(ctx);
}
- (void)test3
{
// 1.獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.畫餅狀圖
// 畫線
CGContextMoveToPoint(ctx, 100, 100);
CGContextAddLineToPoint(ctx, 100, 150);
// 畫圓弧
CGContextAddArc(ctx, 100, 100, 50, M_PI_2, M_PI, 0);
// CGContextAddArc(ctx, 100, 100, 50, -M_PI, M_PI_2, 1);
// 關閉路徑
CGContextClosePath(ctx);
// 3.渲染 (注意, 畫線只能經過空心來畫)
CGContextFillPath(ctx);
// CGContextStrokePath(ctx);
}
- (void)test2
{
// 畫圓弧
// 1.獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.畫圓弧
// x/y 圓心
// radius 半徑
// startAngle 開始的弧度
// endAngle 結束的弧度
// clockwise 畫圓弧的方向 (0 順時針, 1 逆時針)
// CGContextAddArc(ctx, 100, 100, 50, -M_PI_2, M_PI_2, 0);
CGContextAddArc(ctx, 100, 100, 50, M_PI_2, M_PI, 0);
CGContextClosePath(ctx);
// 3.渲染
// CGContextStrokePath(ctx);
CGContextFillPath(ctx);
}
- (void)test
{
// 畫圓
// 1.獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.畫圓
CGContextAddEllipseInRect(ctx, CGRectMake(50, 100, 50, 50));
[[UIColor greenColor] set];
// 3.渲染
// CGContextStrokePath(ctx);
CGContextFillPath(ctx);
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
// 加載的圖片
UIImage * image = [UIImage imageNamed:@"bg"];
// 利用OC方法將圖片繪製到layer上面
//1 將圖片繪製到指定的位置 point
[image drawAtPoint:CGPointMake(0, 0)];
// 2利用drawInRect的方法將圖片繪製到layer上面去 ,是經過拉伸原有的圖片進行的繪製
[image drawInRect:CGRectMake(0, 0, 200, 200)];
// 3.利用drawAsPatternInRect的方法將圖片繪製到layer上面去,是經過平鋪圖片實現的
[image drawAsPatternInRect:CGRectMake(0, 0, 320, 480)];
}
-(void)test
{
// 畫文字
NSString * str = @"Lenny";
// 獲取上下文
// CGContextRef ctx = UIGraphicsGetCurrentContext();
// 繪圖
// 不推薦使用C語言的方法繪製文字, 由於quraz2d中的座標系和UIkit中的座標系不一致, 繪製出來的文字是顛倒的, 並且經過C語言的方法繪製文字至關麻煩
// CGContextSelectFont(<#CGContextRef c#>, <#const char *name#>, <#CGFloat size#>, <#CGTextEncoding textEncoding#>)
// CGContextShowText(ctx, <#const char *string#>, <#size_t length#>)
// 繪製矩形
// 獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 繪圖
CGContextAddRect(ctx, CGRectMake(50, 50, 100, 100));
// 渲染
CGContextStrokePath(ctx);
NSMutableDictionary * md = [NSMutableDictionary dictionary];
// 設置字體的大小
md[NSFontAttributeName] = [UIFont systemFontOfSize:50];
// 設置字體的顏色
md[NSForegroundColorAttributeName] = [UIColor redColor];
// 設置字體的背景顏色
md[NSBackgroundColorAttributeName] = [UIColor whiteColor];
// 將字體畫到指定的點的位置
[str drawAtPoint:CGPointMake(10, 10) withAttributes:md];
// 將文字繪製到指定的範圍內, 若是一行裝不下會自動換行, 當文字超出範圍後就不顯示
[str drawInRect:CGRectMake(50, 50, 100, 100) withAttributes:md];
}
矩陣操做
// 畫四邊形
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 保存上下文
CGContextSaveGState(ctx);
// 注意:設置矩陣操做必須在添加繪圖信息以前
CGContextRotateCTM(ctx, M_PI_4);
// CGContextScaleCTM(ctx, 0.5, 0.5);
// CGContextTranslateCTM(ctx, 0, 150);
CGContextAddRect(ctx, CGRectMake(200, 100, 100, 100));
CGContextRestoreGState(ctx);
CGContextAddEllipseInRect(ctx, CGRectMake(20, 20, 100, 100));
CGContextStrokePath(ctx);
圖形上下文棧
- (void)drawRect:(CGRect)rect
{
// 獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 保存一份最純潔的圖形上下文
// 調用一次該方法就會拷貝一個上下文到棧中
CGContextSaveGState(ctx);
//CGContextSaveGState(ctx);
// 第一條線
// 利用圖形上下文保存繪圖信息
CGContextMoveToPoint(ctx, 150, 20);
CGContextAddLineToPoint(ctx, 20, 100);
// 設置第一條線的狀態
CGContextSetLineWidth(ctx, 10);
CGContextSetLineCap(ctx, kCGLineCapRound);
[[UIColor redColor] set];
// 渲染
CGContextStrokePath(ctx);
// 還原開始保存的那份最純潔的圖形上下文
CGContextRestoreGState(ctx);
// 第二條線
CGContextMoveToPoint(ctx, 80, 30);
CGContextAddLineToPoint(ctx, 80, 150);
/*
// 清空狀態
CGContextSetLineWidth(ctx, 5);
CGContextSetLineCap(ctx, kCGLineCapButt);
[[UIColor greenColor] set];
*/
// 還原開始保存的那份最純潔的圖形上下文
CGContextRestoreGState(ctx);
/*
// 第3條線
CGContextMoveToPoint(ctx, 200, 30);
CGContextAddLineToPoint(ctx, 80, 150);
*/
// 渲染
CGContextStrokePath(ctx);
Quartz 2D圖片的剪切
- (void)drawRect:(CGRect)rect
{
// Drawing code
// 畫圓, 以便於之後指定能夠顯示內容範圍
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextAddEllipseInRect(ctx, CGRectMake(100, 100, 50, 50));
// 指定上下文中能夠顯示內容的範圍
CGContextClip(ctx);
CGContextStrokePath(ctx);
/*
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2. 繪製三角形
// 設置起點
CGContextMoveToPoint(ctx, 100, 10);
// 設置第二個點
CGContextAddLineToPoint(ctx, 50, 100);
// 設置第三個點
CGContextAddLineToPoint(ctx, 150, 100);
// 設置終點
// CGContextAddLineToPoint(ctx, 100, 10);
// 關閉起點和終點
CGContextClosePath(ctx);
// 指定上下文中能夠顯示內容的範圍
// 注意,指定範圍(也就是指點剪切的方法必定要在繪製範圍以前調用)
CGContextClip(ctx);
// 3.渲染圖形到layer上
CGContextStrokePath(ctx);
*/
UIImage *image = [UIImage imageNamed:@"me"];
// 按照原始大小繪製
[image drawAtPoint:CGPointMake(100, 100)];
CGContextAddRect(ctx, CGRectMake(10, 10, 100, 100));
CGContextFillPath(ctx);
}
動態刷幀
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 進行清屏的操做
CGContextClearRect(ctx, rect);
self.imageY += 5;
if (self.imageY > rect.size.height) {
self.imageY = 0;
}
// Drawing code
UIImage *image = [UIImage imageNamed:@"snow"];
[image drawAtPoint:CGPointMake(10, self.imageY)];
// [self setNeedsDisplay];
}
-(void)awakeFromNib
{
NSLog(@"awakeFromNib");
// 建立CADisplayLink, 默認每秒60次
CADisplayLink *display = [CADisplayLink displayLinkWithTarget:self selector:@selector(updataImage)];
// 將CADisplayLink加入到消息循環中
[display addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)updataImage
{
[self setNeedsDisplay];
}
畫四邊形的方法
// 畫四邊形
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 1.第一種方式, 經過鏈接固定的點繪製四邊形
// CGContextMoveToPoint(ctx, 0, 0);
// CGContextAddLineToPoint(ctx, <#CGFloat x#>, <#CGFloat y#>)
// CGContextAddLineToPoint(ctx, <#CGFloat x#>, <#CGFloat y#>)
// CGContextAddLineToPoint(ctx, <#CGFloat x#>, <#CGFloat y#>)
// CGContextAddLineToPoint(ctx, <#CGFloat x#>, <#CGFloat y#>)
// 2.指定起點和寬高繪製四邊形
// CGContextAddRect(ctx, CGRectMake(10, 10, 100, 100));
// CGContextStrokePath(ctx);
// 3.兩步合爲一部
// CGContextStrokeRect(ctx, CGRectMake(10, 10, 100, 100));
// CGContextFillRect(ctx, CGRectMake(10, 10, 100, 100));
// 4.經過OC的方法繪製實心的四邊形, 注意沒有空心的方法
// UIRectFill(CGRectMake(10, 10, 100, 100));
// 5.經過繪製線條設置寬度
CGContextMoveToPoint(ctx, 10, 10);
CGContextAddLineToPoint(ctx, 100, 100);
CGContextSetLineWidth(ctx, 50);
CGContextStrokePath(ctx);
內存管理
- (void)drawRect:(CGRect)rect
{
// 1.獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.繪製圖形
/*
// 設置起點
CGContextMoveToPoint(ctx, 10, 10);
// 設置終點
CGContextAddLineToPoint(ctx, 100, 100);
// 3.畫圓
CGContextAddEllipseInRect(ctx, CGRectMake(50, 50, 50, 50));
*/
// 2.建立路徑(一個path就表明一條路徑)
// 但凡經過quarzt2d中的帶有create/ copy / retain 方法建立出來的值都必須手動的釋放
CGMutablePathRef path = CGPathCreateMutable();
// 設置起點
CGPathMoveToPoint(path, NULL, 10, 10);
// 設置終點
CGPathAddLineToPoint(path, NULL, 100, 100);
// 將路徑添加到上下文中
CGContextAddPath(ctx, path);
// 3.再建立一條路徑用於保存圓
CGMutablePathRef path2 = CGPathCreateMutable();
// 在path中添加畫的路徑
CGPathAddEllipseInRect(path2, NULL, CGRectMake(50, 50, 50, 50));
CGContextAddPath(ctx, path2);
// 3.渲染'
CGContextStrokePath(ctx);
// 釋放前面建立的兩條路徑
CGPathRelease(path);
CGPathRelease(path2);
// 下面這種方式也能夠釋放路徑
CFRelease(path);
CFRelease(path2);
bitmap
- (void)viewDidLoad {
[super viewDidLoad];
// 0 建立一個bitmap的上文
UIGraphicsGetImageFromCurrentImageContext();
/*
size :指定未來建立出來的bitmap的大小
opaque : YES:不透明 NO:透明
scale: 縮放比例
建立出來的bitmap就對應一個UIImage
*/
UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 200), NO, 0);
// 1.獲取文件的下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2.繪圖
CGContextAddEllipseInRect(ctx, CGRectMake(0, 0, 20, 20));
//3.渲染
CGContextStrokePath(ctx);
// 4.生成圖片
UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
// 5.顯示生成圖片
self.vi.image = image;
// 6.保存生成的圖片 可是要先保存成nsdata數據 而後再寫到本地
// 先將圖片轉換爲二進制數據, 而後再將圖片寫到文件中
NSData * data =UIImagePNGRepresentation(image);
[data writeToFile:@"/Users/guoyule/Desktop/Lenny.png" atomically:YES];
}
生成水印catagray
#import <UIKit/UIKit.h>
@interface UIImage (LK)
/**
* 生成水印
*
* @param bgName 背景圖片
* @param logNmae 水印圖片
*
* @return 生成好的圖片(帶水印的圖片)
*/
+ (instancetype)imageWithBackgroundImageName:(NSString *)bgName log:(NSString *)logNmae;
@end
#import "UIImage+LK.h"
@implementation UIImage (LK)
+ (instancetype)imageWithBackgroundImageName:(NSString *)bgName log:(NSString *)logNmae
{
// 0. 加載背景圖片
UIImage *image = [UIImage imageNamed:bgName];
// 1.建立bitmap上下文
// 執行完這一行在內存中就相遇建立了一個UIImage
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
// 2.繪圖圖片
// 繪製背景圖片
[image drawAtPoint:CGPointMake(0, 0)];
// 繪製水印'
UIImage *logImage = [UIImage imageNamed:logNmae];
CGFloat margin = 10;
CGFloat logY = margin;
CGFloat logX = image.size.width - margin - logImage.size.width;
[logImage drawAtPoint:CGPointMake(logX, logY)];
// NSString *str = @"黑馬程序員";
// [str drawAtPoint:CGPointMake(150, 50) withAttributes:nil];
// 3.得到圖片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
return newImage;
}
@end
UIResponder事件處理 _Lenny Kwok
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;
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;
遠程控制事件
- (void)remoteControlReceivedWithEvent:(UIEvent *)event;
UIView的觸摸事件處理
UIView是UIResponder的子類,能夠覆蓋下列4個方法處理不一樣的觸摸事件
一根或者多根手指開始觸摸view,系統會自動調用view的下面方法
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
一根或者多根手指在view上移動,系統會自動調用view的下面方法(隨着手指的移動,會持續調用該方法)
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
一根或者多根手指離開view,系統會自動調用view的下面方法
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
觸摸結束前,某個系統事件(例如電話呼入)會打斷觸摸過程,系統會自動調用view的下面方法
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
提示:touches中存放的都是UITouch對象
UITouch
當用戶用一根觸摸屏幕時,會建立一個與手指相關聯的UITouch對象
一根手指對應一個UITouch對象
UITouch的做用
保存着跟手指相關的信息,好比觸摸的位置、時間、階段
當手指移動時,系統會更新同一個UITouch對象,使之可以一直保存該手指在的觸摸位置
當手指離開屏幕時,系統會銷燬相應的UITouch對象
提示:iPhone開發中,要避免使用雙擊事件!
UITouch的屬性
觸摸產生時所處的窗口
@property(nonatomic,readonly,retain) UIWindow *window;
觸摸產生時所處的視圖
@property(nonatomic,readonly,retain) UIView *view;
短期內點按屏幕的次數,能夠根據tapCount判斷單擊、雙擊或更多的點擊
@property(nonatomic,readonly) NSUInteger tapCount;
記錄了觸摸事件產生或變化時的時間,單位是秒
@property(nonatomic,readonly) NSTimeInterval timestamp;
當前觸摸事件所處的狀態
@property(nonatomic,readonly) UITouchPhase phase;
- (CGPoint)locationInView:(UIView *)view;
返回值表示觸摸在view上的位置
這裏返回的位置是針對view的座標系的(以view的左上角爲原點(0, 0))
調用時傳入的view參數爲nil的話,返回的是觸摸點在UIWindow的位置
- (CGPoint)previousLocationInView:(UIView *)view;
該方法記錄了前一個觸摸點的位置
UIEvent
每產生一個事件,就會產生一個UIEvent對象
UIEvent:稱爲事件對象,記錄事件產生的時刻和類型
常見屬性
事件類型
@property(nonatomic,readonly) UIEventType type;
@property(nonatomic,readonly) UIEventSubtype subtype;
事件產生的時間
@property(nonatomic,readonly) NSTimeInterval timestamp;
UIEvent還提供了相應的方法能夠得到在某個view上面的觸摸對象(UITouch)
touches和event參數
一次完整的觸摸過程,會經歷3個狀態:
觸摸開始:- (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
4個觸摸事件處理方法中,都有NSSet *touches和UIEvent *event兩個參數
一次完整的觸摸過程當中,只會產生一個事件對象,4個觸摸方法都是同一個event參數
若是兩根手指同時觸摸一個view,那麼view只會調用一次touchesBegan:withEvent:方法,touches參數中裝着2個UITouch對象
若是這兩根手指一前一後分開觸摸同一個view,那麼view會分別調用2次touchesBegan:withEvent:方法,而且每次調用時的touches參數中只包含一個UITouch對象
根據touches中UITouch的個數能夠判斷出是單點觸摸仍是多點觸摸
事件的產生和傳遞
發生觸摸事件後,系統會將該事件加入到一個由UIApplication管理的事件隊列中
UIApplication會從事件隊列中取出最前面的事件,並將事件分發下去以便處理,一般,先發送事件給應用程序的主窗口(keyWindow)
主窗口會在視圖層次結構中找到一個最合適的視圖來處理觸摸事件
找到合適的視圖控件後,就會調用視圖控件的touches方法來做具體的事件處理
touchesBegan…
touchesMoved…
touchedEnded…
重點觸摸事件的傳遞
觸摸事件的傳遞是從父控件傳遞到子控件
點擊了綠色的view:
UIApplication -> UIWindow -> 白色 -> 綠色
點擊了藍色的view:
UIApplication -> UIWindow -> 白色 -> 橙色 -> 藍色
點擊了黃色的view:
UIApplication -> UIWindow -> 白色 -> 橙色 -> 藍色 -> 黃色
若是父控件不能接收觸摸事件,那麼子控件就不可能接收到觸摸事件(掌握)
如何找到最合適的控件來處理事件?
本身是否能接收觸摸事件?
觸摸點是否在本身身上?
從後往前遍歷子控件,重複前面的兩個步驟
若是沒有符合條件的子控件,那麼就本身最適合處理
1.事件的完整處理過程:
1> 先將事件對象由上往下傳遞(由父控件傳遞給子控件), 找到最合適的控件來處理這個事件
2> 調用最合適控件的touches.....方法
3> 若是調用了[super touches...];就會將事件順着響應者鏈條往上傳遞,傳遞給上一個響應者
4> 接着就會調用上一個響應者的touches.....方法
2.什麼是響應者鏈條?
1> 響應者鏈條是由多個響應者對象鏈接起來的鏈條(什麼是響應者對象: 能處理事件的對象)
2> 利用響應者鏈條, 能讓多個控件 處理 同一個觸摸事件
3> 怎麼利用鏈條往上傳遞?誰是上一個響應者
3.誰是上一個響應者(nextResponder)
1> 若是當前這個view是控制器的view, 那麼控制器就是上一個響應者
2> 若是當前這個view不是控制器的view, 那麼父控件就是上一個響應者
觸摸事件
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 1.打開交互
self.iconView.userInteractionEnabled = YES;
// 2.建立手勢識別器
UITapGestureRecognizer * tp = [[UITapGestureRecognizer alloc]init];
// 3.添加手勢到View
[self.iconView addGestureRecognizer:tp];
// 表示連着按下兩次
tp.numberOfTapsRequired = 2;
// 表示手指的根數
tp.numberOfTouchesRequired = 2;
NSInteger x= tp.numberOfTouches;
NSLog(@"%ld",(long)x);
// 4.監聽手勢識別器
[tp addTarget:self action:@selector(change)];
}
-(void)change
{
NSLog(@"他摸我");
}
UIGestureRecognizer
爲了完成手勢識別,必須藉助於手勢識別器----UIGestureRecognizer
利用UIGestureRecognizer,能輕鬆識別用戶在某個view上面作的一些常見手勢
UIGestureRecognizer是一個抽象類,定義了全部手勢的基本行爲,使用它的子類才能處理具體的手勢
UITapGestureRecognizer(敲擊)
UIPinchGestureRecognizer(捏合,用於縮放)
UIPanGestureRecognizer(拖拽)
UISwipeGestureRecognizer(輕掃)
UIRotationGestureRecognizer(旋轉)
UILongPressGestureRecognizer(長按)
UITapGestureRecognizer
每個手勢識別器的用法都差很少,好比UITapGestureRecognizer的使用步驟以下
建立手勢識別器對象
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
設置手勢識別器對象的具體屬性
// 連續敲擊2次
tap.numberOfTapsRequired = 2;
// 須要2根手指一塊兒敲擊
tap.numberOfTouchesRequired = 2;
添加手勢識別器到對應的view上
[self.iconView addGestureRecognizer:tap];
監聽手勢的觸發
[tap addTarget:self action:@selector(tapIconView:)];
手勢識別的狀態
typedef NS_ENUM(NSInteger, UIGestureRecognizerState) {
// 沒有觸摸事件發生,全部手勢識別的默認狀態
UIGestureRecognizerStatePossible,
// 一個手勢已經開始但還沒有改變或者完成時
UIGestureRecognizerStateBegan,
// 手勢狀態改變
UIGestureRecognizerStateChanged,
// 手勢完成
UIGestureRecognizerStateEnded,
// 手勢取消,恢復至Possible狀態
UIGestureRecognizerStateCancelled,
// 手勢失敗,恢復至Possible狀態
UIGestureRecognizerStateFailed,
// 識別到手勢識別
UIGestureRecognizerStateRecognized = UIGestureRecognizerStateEnded
};
捏合旋轉
// 該方法返回的BOOL值決定了view是否可以同時響應多個手勢
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
NSLog(@"%@ - %@", gestureRecognizer.class, otherGestureRecognizer.class);
return YES;
}
- (void)pichTest
{
// 捏合手勢
UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] init];
pinch.delegate = self;
[self.iconView addGestureRecognizer:pinch];
[pinch addTarget:self action:@selector(pinchView:)];
}
- (void)pinchView:(UIPinchGestureRecognizer *)pinch
{
// NSLog(@"捏合事件 %.1f", pinch.scale);
// self.iconView.transform = CGAffineTransformMakeScale(pinch.scale, pinch.scale);
// 1.0 * 0.9
self.iconView.transform = CGAffineTransformScale(self.iconView.transform, pinch.scale, pinch.scale);
pinch.scale = 1.0;
}
- (void)rotationTest
{
// 旋轉
UIRotationGestureRecognizer *gesture = [[UIRotationGestureRecognizer alloc] init];
gesture.delegate = self;
[self.iconView addGestureRecognizer:gesture];
[gesture addTarget:self action:@selector(rotationView:)];
}
- (void)rotationView:(UIRotationGestureRecognizer *)gesture
{
// NSLog(@"旋轉事件 %.1f", gesture.rotation);
// 每次從最初的位置開始
// self.iconView.transform = CGAffineTransformMakeRotation(gesture.rotation);
// 在傳入的transform基礎上遞增一個弧度
self.iconView.transform = CGAffineTransformRotate(self.iconView.transform, gesture.rotation);
// 將旋轉的弧度清零(注意不是將圖片旋轉的弧度清零, 而是將當前手指旋轉的弧度清零)
gesture.rotation = 0;// 若是理解不了 , 記住就OK
}
拖拽
- (void)viewDidLoad
{
[super viewDidLoad];
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] init];
[self.customView addGestureRecognizer:pan];
[pan addTarget:self action:@selector(panView:)];
}
- (void)panView:(UIPanGestureRecognizer *)pan
{
// 返回的值是以手指按下的點爲原點
// 1 2 3 4 5
CGPoint point = [pan translationInView:pan.view];
NSLog(@"拖拽事件 %@", NSStringFromCGPoint(point));
CGPoint temp = self.customView.center;
temp.x += point.x;
temp.y += point.y;
self.customView.center = temp;
// 理解不了就記住就OK
[pan setTranslation:CGPointZero inView:pan.view];
}
長按和輕掃
- (void)viewDidLoad
{
[super viewDidLoad];
// 向上
UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] init];
// 設置輕掃的方向
swipe.direction = UISwipeGestureRecognizerDirectionUp;
[self.customView addGestureRecognizer:swipe];
[swipe addTarget:self action:@selector(swipeView)];
// 向下
UISwipeGestureRecognizer *swipe2 = [[UISwipeGestureRecognizer alloc] init];
// 設置輕掃的方向
swipe2.direction = UISwipeGestureRecognizerDirectionDown;
[self.customView addGestureRecognizer:swipe2];
[swipe2 addTarget:self action:@selector(swipeView2)];
// 左邊
UISwipeGestureRecognizer *swipe3 = [[UISwipeGestureRecognizer alloc] init];
// 設置輕掃的方向
swipe3.direction = UISwipeGestureRecognizerDirectionLeft;
[self.customView addGestureRecognizer:swipe3];
[swipe3 addTarget:self action:@selector(swipeView3)];
// 右邊
UISwipeGestureRecognizer *swipe4 = [[UISwipeGestureRecognizer alloc] init];
// 設置輕掃的方向
swipe4.direction = UISwipeGestureRecognizerDirectionRight;
[self.customView addGestureRecognizer:swipe4];
[swipe4 addTarget:self action:@selector(swipeView4)];
}
- (void)swipeView4
{
NSLog(@"輕掃事件右");
}
- (void)swipeView3
{
NSLog(@"輕掃事件左");
}
- (void)swipeView2
{
NSLog(@"輕掃事件下");
}
- (void)swipeView
{
NSLog(@"輕掃事件上");
}
- (void)test
{
// 長按事件
// 1.建立手勢識別器
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] init];
// 1.1設置長按手勢識別器的屬性
// longPress.minimumPressDuration = 5;
// 手指按下後事件響應以前容許手指移動的偏移位
longPress.allowableMovement = 50;
// 2.添加手勢識別器到View
[self.customView addGestureRecognizer:longPress];
// 3.監聽手勢識別器
[longPress addTarget:self action:@selector(longPressView)];
}
-(void)longPressView
{
NSLog(@"長按事件");
}
CAlayer
- (void)viewDidLoad {
[super viewDidLoad];
// 設置layer邊框
self.imageView.layer.borderWidth = 10;
// 設置邊框的顏色
self.imageView.layer.borderColor = [UIColor redColor].CGColor;//注意borderColor的類型
// 設置layer的圓角(設置主圖層的圓角)
self.imageView.layer.cornerRadius = 10;
// 設置超出主圖層的部分進行剪切操做
// self.imageView.layer.masksToBounds = YES;
// self.imageView.clipsToBounds = YES;
// 設置的image不是展現在主圖層上的,是展現在子圖層上的
self.imageView.layer.contents = (id)[UIImage imageNamed:@"LoginScreen"].CGImage;
// 設置陰影的顏色
self.imageView.layer.shadowColor = [UIColor blackColor].CGColor;
// 設置陰影的偏移位
// 若是是正數,表明向右移動
// 上
self.imageView.layer.shadowOffset = CGSizeMake(10, 10);
// 設置陰影的透明度0~1 1 徹底不透明 0 徹底透明
self.imageView.layer.shadowOpacity =1;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// self.imageView.transform = CGAffineTransformMakeTranslation(0, 100);
// self.imageView.layer.transform = CATransform3DMakeTranslation(0, 100, 0);
// NSValue *v = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(0, -200, 0)];
// [self.imageView.layer setValue:v forKey:@"transform"];
// [self.imageView.layer setValue:@(100) forKey:@"transform.translation.x"];
// self.imageView.transform = CGAffineTransformMakeRotationxx(M_PI_4);
// self.imageView.layer.transform = CATransform3DMakeRotation(M_PI_4, 0, 0, 1);
[self.imageView.layer setValue:@"100" forKey:@"transform.translation.x"];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)test
{
// 設置layer邊框
self.imageView.layer.borderWidth = 10;
// 設置邊框的顏色
self.imageView.layer.borderColor = [UIColor redColor].CGColor;//注意borderColor的類型
// 設置layer的圓角(設置主圖層的圓角)
self.imageView.layer.cornerRadius = 10;
// 設置超出主圖層的部分進行剪切操做
// self.imageView.layer.masksToBounds = YES;
// self.imageView.clipsToBounds = YES;
// 設置的image不是展現在主圖層上的,是展現在子圖層上的
self.imageView.layer.contents = (id)[UIImage imageNamed:@"LoginScreen"].CGImage;
// 設置陰影的顏色
self.imageView.layer.shadowColor = [UIColor blackColor].CGColor;
// 設置陰影的偏移位
// 若是是正數,表明向右移動
// 上
self.imageView.layer.shadowOffset = CGSizeMake(10, 10);
// 設置陰影的透明度0~1 1 徹底不透明 0 徹底透明
self.imageView.layer.shadowOpacity =1;
}
-(void)test2
{
self.picyture.layer.borderWidth = 10;
self.imageView.layer.borderColor = [UIColor redColor].CGColor;
self.imageView.layer.cornerRadius = 10;
// self.iconView.layer.masksToBounds = YES;
// self.iconView.layer.bounds = CGRectMake(0, 0, 200, 200);
// self.iconView.layer.position = CGPointMake(100 , 100);
self.picyture.layer.masksToBounds = YES;
self.picyture.layer.bounds = CGRectMake(0, 0, 200, 200);
self.picyture.layer.position = CGPointMake(400, 200);
}
CALayer 新建
- (void)viewDidLoad
{
[super viewDidLoad];
// 若是一個控制是另一個控件的子控件, 那麼這個控件中的layer也是另一個控件的子layer
// NSLog(@"star - %@", self.view.layer.sublayers);
CALayer *layer = [CALayer layer];
layer.backgroundColor = [UIColor redColor].CGColor;
layer.bounds = CGRectMake(0, 0, 100, 100);
// layer.position = CGPointMake(200, 200);
// layer.contents = (id)[UIImage imageNamed:@"me"].CGImage;
[self.view.layer addSublayer:layer];
}
- (void)test
{
NSLog(@"star - %@", self.view.layer.sublayers);
// 1.建立layer
// CALayer *layer = [[CALayer alloc] init];
CALayer *layer = [CALayer layer];
layer.backgroundColor = [UIColor redColor].CGColor;
layer.bounds = CGRectMake(0, 0, 100, 100);
layer.position = CGPointMake(200, 200);
layer.borderWidth = 10;
layer.cornerRadius = 10;
// 將layer添加在界面上
[self.view.layer addSublayer:layer];
// NSLog(@"%@", layer.superlayer); // 獲取layer的父視圖
NSLog(@"end - %@", self.view.layer.sublayers);
//
// UIView *view = [[UIView alloc] init];
// view.superview;
// view.subviews;
// [self.view addSubview:view];
}
關於CALayer的疑惑
關於CALayer的疑惑
首先
CALayer是定義在QuartzCore框架中的
CGImageRef、CGColorRef兩種數據類型是定義在CoreGraphics框架中的
UIColor、UIImage是定義在UIKit框架中的
其次
QuartzCore框架和CoreGraphics框架是能夠跨平臺使用的,在iOS和Mac OS X上都能使用
可是UIKit只能在iOS中使用
爲了保證可移植性,QuartzCore不能使用UIImage、UIColor,只能使用CGImageRef、CGColorRef
UIView和CALayer的選擇
經過CALayer,就能作出跟UIImageView同樣的界面效果
既然CALayer和UIView都能實現相同的顯示效果,那究竟該選擇誰好呢?
其實,對比CALayer,UIView多了一個事件處理的功能。也就是說,CALayer不能處理用戶的觸摸事件,而UIView能夠
因此,若是顯示出來的東西須要跟用戶進行交互的話,用UIView;若是不須要跟用戶進行交互,用UIView或者CALayer均可以
固然,CALayer的性能會高一些,由於它少了事件處理的功能,更加輕量級
position和anchorPoint
CALayer有2個很是重要的屬性:position和anchorPoint
@property CGPoint position;
用來設置CALayer在父層中的位置
以父層的左上角爲原點(0, 0)
@property CGPoint anchorPoint;
稱爲「定位點」、「錨點」
決定着CALayer身上的哪一個點會在position屬性所指的位置
以本身的左上角爲原點(0, 0)
它的x、y取值範圍都是0~1,默認值爲(0.5, 0.5)
紅色圖層的anchorPoint是(0,0)
紅色圖層的anchorPoint是(0.5,0.5)
紅色圖層的anchorPoint是(1,1)
紅色圖層的anchorPoint是(0.5,0)
紅色圖層的anchorPoint是(1,0.5)
隱式動畫
每個UIView內部都默認關聯着一個CALayer,咱們可用稱這個Layer爲Root Layer(根層)
全部的非Root Layer,也就是手動建立的CALayer對象,都存在着隱式動畫
什麼是隱式動畫?
當對非Root Layer的部分屬性進行修改時,默認會自動產生一些動畫效果
而這些屬性稱爲Animatable Properties(可動畫屬性)
列舉幾個常見的Animatable Properties:
bounds:用於設置CALayer的寬度和高度。修改這個屬性會產生縮放動畫
backgroundColor:用於設置CALayer的背景色。修改這個屬性會產生背景色的漸變更畫
position:用於設置CALayer的位置。修改這個屬性會產平生移動畫
能夠經過動畫事務(CATransaction)關閉默認的隱式動畫效果
[CATransaction begin];
[CATransaction setDisableActions:YES];
self.myview.layer.position = CGPointMake(10, 10);
[CATransaction commit];
- (void)viewDidLoad {
[super viewDidLoad];
CALayer * layer = [[CALayer alloc]init];
layer.bounds = CGRectMake(0, 0, 200, 200);
layer.anchorPoint = CGPointMake(0.5, 0.5);
layer.backgroundColor = [UIColor redColor].CGColor;
[self.view.layer addSublayer:layer];
self.layer = layer;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// 關閉動畫
[CATransaction begin];
[CATransaction setDisableActions:YES];
self.layer.backgroundColor = [UIColor greenColor].CGColor;
self.layer.position = CGPointMake(200, 200);
// self.layer.position // 如何查看CALayer的某個屬性是否支持隱式動畫, 查看頭文件是否有 Animatable
// 如何查看是否CAlayer支持隱式動畫,查看頭文件是否有Animation
[CATransaction commit];
}
核心動畫(簡介)
Core Animation,中文翻譯爲核心動畫,它是一組很是強大的動畫處理API,使用它能作出很是炫麗的動畫效果,並且每每是事半功倍。也就是說,使用少許的代碼就能夠實現很是強大的功能。
Core Animation能夠用在Mac OS X和iOS平臺。
Core Animation的動畫執行過程都是在後臺操做的,不會阻塞主線程。
要注意的是,Core Animation是直接做用在CALayer上的,並不是UIView。
Core Animation的使用步驟
1.使用它須要先添加QuartzCore.framework框架和引入主頭文件<QuartzCore/QuartzCore.h>(iOS7不須要)
2.初始化一個CAAnimation對象,並設置一些動畫相關屬性
3.經過調用CALayer的addAnimation:forKey:方法增長CAAnimation對象到CALayer中,這樣就能開始執行動畫了
4.經過調用CALayer的removeAnimationForKey:方法能夠中止CALayer中的動畫
CAAnimation
全部動畫對象的父類,負責控制動畫的持續時間和速度,是個抽象類,不能直接使用,應該使用它具體的子類
屬性解析:(紅色表明來自CAMediaTiming協議的屬性)
duration:動畫的持續時間
repeatCount:動畫的重複次數
repeatDuration:動畫的重複時間
removedOnCompletion:默認爲YES,表明動畫執行完畢後就從圖層上移除,圖形會恢復到動畫執行前的狀態。若是想讓圖層保持顯示動畫執行後的狀態,那就設置爲NO,不過還要設置fillMode爲kCAFillModeForwards
fillMode:決定當前對象在非active時間段的行爲.好比動畫開始以前,動畫結束以後
beginTime:能夠用來設置動畫延遲執行時間,若想延遲2s,就設置爲CACurrentMediaTime()+2,CACurrentMediaTime()爲圖層的當前時間
timingFunction:速度控制函數,控制動畫運行的節奏
delegate:動畫代理
CAPropertyAnimation
是CAAnimation的子類,也是個抽象類,要想建立動畫對象,應該使用它的兩個子類:CABasicAnimation和CAKeyframeAnimation
屬性解析:
keyPath:經過指定CALayer的一個屬性名稱爲keyPath(NSString類型),而且對CALayer的這個屬性的值進行修改,達到相應的動畫效果。好比,指定@」position」爲keyPath,就修改CALayer的position屬性的值,以達到平移的動畫效果
CABasicAnimation
CAPropertyAnimation的子類
屬性解析:
fromValue:keyPath相應屬性的初始值
toValue:keyPath相應屬性的結束值
隨着動畫的進行,在長度爲duration的持續時間內,keyPath相應屬性的值從fromValue漸漸地變爲toValue
若是fillMode=kCAFillModeForwards和removedOnComletion=NO,那麼在動畫執行完畢後,圖層會保持顯示動畫執行後的狀態。但在實質上,圖層的屬性值仍是動畫執行前的初始值,並無真正被改變。好比,CALayer的position初始值爲(0,0),CABasicAnimation的fromValue爲(10,10),toValue爲(100,100),雖然動畫執行完畢後圖層保持在(100,100)這個位置,實質上圖層的position仍是爲(0,0)
CAKeyframeAnimation
CApropertyAnimation的子類,跟CABasicAnimation的區別是:CABasicAnimation只能從一個數值(fromValue)變到另外一個數值(toValue),而CAKeyframeAnimation會使用一個NSArray保存這些數值
屬性解析:
values:就是上述的NSArray對象。裏面的元素稱爲」關鍵幀」(keyframe)。動畫對象會在指定的時間(duration)內,依次顯示values數組中的每個關鍵幀
path:能夠設置一個CGPathRef\CGMutablePathRef,讓層跟着路徑移動。path只對CALayer的anchorPoint和position起做用。若是你設置了path,那麼values將被忽略
keyTimes:能夠爲對應的關鍵幀指定對應的時間點,其取值範圍爲0到1.0,keyTimes中的每個時間值都對應values中的每一幀.當keyTimes沒有設置的時候,各個關鍵幀的時間是平分的
CABasicAnimation可看作是最多隻有2個關鍵幀的CAKeyframeAnimation
CAAnimationGroup
CAAnimation的子類,能夠保存一組動畫對象,將CAAnimationGroup對象加入層後,組中全部動畫對象能夠同時併發運行
屬性解析:
animations:用來保存一組動畫對象的NSArray
默認狀況下,一組動畫對象是同時運行的,也能夠經過設置動畫對象的beginTime屬性來更改動畫的開始時間
CATransition
CAAnimation的子類,用於作轉場動畫,可以爲層提供移出屏幕和移入屏幕的動畫效果。iOS比Mac OS X的轉場動畫效果少一點
UINavigationController就是經過CATransition實現了將控制器的視圖推入屏幕的動畫效果
屬性解析:
type:動畫過渡類型
subtype:動畫過渡方向
startProgress:動畫起點(在總體動畫的百分比)
endProgress:動畫終點(在總體動畫的百分比)
UIView動畫
UIKit直接將動畫集成到UIView類中,當內部的一些屬性發生改變時,UIView將爲這些改變提供動畫支持
執行動畫所須要的工做由UIView類自動完成,但仍要在但願執行動畫時通知視圖,爲此須要將改變屬性的代碼放在[UIView beginAnimations:nil context:nil]和[UIView commitAnimations]之間
常見方法解析:
+ (void)setAnimationDelegate:(id)delegate
設置動畫代理對象,當動畫開始或者結束時會發消息給代理對象
+ (void)setAnimationWillStartSelector:(SEL)selector
當動畫即將開始時,執行delegate對象的selector,而且把beginAnimations:context:中傳入的參數傳進selector
+ (void)setAnimationDidStopSelector:(SEL)selector
當動畫結束時,執行delegate對象的selector,而且把beginAnimations:context:中傳入的參數傳進selector
+ (void)setAnimationDuration:(NSTimeInterval)duration
動畫的持續時間,秒爲單位
+ (void)setAnimationDelay:(NSTimeInterval)delay
動畫延遲delay秒後再開始
+ (void)setAnimationStartDate:(NSDate *)startDate
動畫的開始時間,默認爲now
+ (void)setAnimationCurve:(UIViewAnimationCurve)curve
動畫的節奏控制,具體看下面的」備註」
+ (void)setAnimationRepeatCount:(float)repeatCount
動畫的重複次數
+ (void)setAnimationRepeatAutoreverses:(BOOL)repeatAutoreverses
若是設置爲YES,表明動畫每次重複執行的效果會跟上一次相反
+ (void)setAnimationTransition:(UIViewAnimationTransition)transition forView:(UIView *)view cache:(BOOL)cache
設置視圖view的過渡效果, transition指定過渡類型, cache設置YES表明使用視圖緩存,性能較好
Block動畫
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
參數解析:
duration:動畫的持續時間
delay:動畫延遲delay秒後開始
options:動畫的節奏控制
animations:將改變視圖屬性的代碼放在這個block中
completion:動畫結束後,會自動調用這個block
+ (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
參數解析:
duration:動畫的持續時間
view:須要進行轉場動畫的視圖
options:轉場動畫的類型
animations:將改變視圖屬性的代碼放在這個block中
completion:動畫結束後,會自動調用這個block
+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion
方法調用完畢後,至關於執行了下面兩句代碼:
// 添加toView到父視圖
[fromView.superview addSubview:toView];
// 把fromView從父視圖中移除
[fromView.superview removeFromSuperview];
參數解析:
duration:動畫的持續時間
options:轉場動畫的類型
animations:將改變視圖屬性的代碼放在這個block中
completion:動畫結束後,會自動調用這個block
UIImageView的幀動畫
UIImageView可讓一系列的圖片在特定的時間內按順序顯示
相關屬性解析:
animationImages:要顯示的圖片(一個裝着UIImage的NSArray)
animationDuration:完整地顯示一次animationImages中的全部圖片所需的時間
animationRepeatCount:動畫的執行次數(默認爲0,表明無限循環)
相關方法解析:
- (void)startAnimating; 開始動畫
- (void)stopAnimating; 中止動畫
- (BOOL)isAnimating; 是否正在運行動畫
UIActivityIndicatorView
是一個旋轉進度輪,能夠用來告知用戶有一個操做正在進行中,通常用initWithActivityIndicatorStyle初始化
方法解析:
- (void)startAnimating; 開始動畫
- (void)stopAnimating; 中止動畫
- (BOOL)isAnimating; 是否正在運行動畫
UIActivityIndicatorViewStyle有3個值可供選擇:
UIActivityIndicatorViewStyleWhiteLarge //大型白色指示器
UIActivityIndicatorViewStyleWhite //標準尺寸白色指示器
UIActivityIndicatorViewStyleGray //灰色指示器,用於白色背景
抖動代碼
//
// ViewController.m
// 01-抖動
//
// Created by Lenny on 3/16/15.
// Copyright (c) 2015 Lenny. All rights reserved.
//
#import "ViewController.h"
#define angle2Radian(angle) ((angle) / 180.0 * M_PI)
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *imageView;
@end
@implementation ViewController
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// 1.建立核心動畫
CAKeyframeAnimation * kfr = [[CAKeyframeAnimation alloc]init];
// 2.建立核心動畫的類型
kfr.keyPath = @"transform.rotation";
// 度數/ 180.0 *M_PI
kfr.values = @[@(-angle2Radian(4)), @(angle2Radian(4)), @(-angle2Radian(4))];
kfr.removedOnCompletion = NO;
kfr.fillMode = kCAFillModeBackwards;
kfr.duration = 0.1;
// 設置重複次數
kfr.repeatCount = MAXFLOAT;
// 3添加核心動畫
[self.imageView.layer addAnimation:kfr forKey:nil];
}
@end
轉場幀動畫
- (IBAction)preImage:(id)sender {
self.index--;
if (self.index < 1) {
self.index = 7;
}
NSString * imageName = [NSString stringWithFormat:@"%d.jpg",self.index];
UIImage * image = [UIImage imageNamed:imageName];
self.imageView.image = image;
// 建立核心動畫
CATransition * ca = [CATransition animation];
// 動畫的過分類型
ca.type = @"cube";
// 動畫過分的方向
ca.subtype = kCATransitionFromRight;
// 動畫的起點終點動畫
// ca.startProgress = 0.5;
// ca.endProgress = 0.5;
// 動畫時間
ca.duration = 1;
[self.imageView.layer addAnimation:ca forKey:nil];
}
- (IBAction)nextImage:(id)sender {
self.index++;
if (self.index >7) {
self.index = 1;
}
NSString * imageName = [NSString stringWithFormat:@"%d.jpg",self.index];
UIImage * image = [UIImage imageNamed:imageName];
self.imageView.image = image;
// 建立核心動畫
CATransition * ca = [CATransition animation];
// 動畫的過分類型
ca.type = @"cube";
// 動畫過分的方向
ca.subtype = kCATransitionFromLeft;
// 動畫的起點終點動畫
// ca.startProgress = 0.5;
// ca.endProgress = 0.5;
// 動畫時間
ca.duration = 1;
[self.imageView.layer addAnimation:ca forKey:nil];
}
圖片的拉伸
UIImage *image = [UIImage imageNamed:@"RedButton"];
// 經過一張原始圖片生成一張可拉伸的圖片
CGFloat imageW = image.size.width * 0.5;
CGFloat imageH = image.size.height * 0.5;
UIImage *newImage = [image resizableImageWithCapInsets:UIEdgeInsetsMake(imageH, imageW, imageH, imageW) resizingMode:UIImageResizingModeStretch];
[self.loginBtn setBackgroundImage:newImage forState:UIControlStateNormal];
UIImage *selImage = [UIImage imageNamed:@"RedButtonPressed"];
CGFloat selImageW = selImage.size.width * 0.5;
CGFloat selImageH = selImage.size.height * 0.5;
UIImage *selNewImage = [selImage resizableImageWithCapInsets:UIEdgeInsetsMake(selImageH, selImageW, selImageH, selImageW) resizingMode:UIImageResizingModeStretch];
[self.loginBtn setBackgroundImage:selNewImage forState:UIControlStateHighlighted];
項目總結
自定義導航欄
- (void)addTabBarButtonWithNormalImageName:(NSString *)norName andDisableImageName:(NSString *)disName
{
// 3.1建立按鈕
LKTabBarBtn *btn = [[LKTabBarBtn alloc] init];
// 3.2設置按鈕上顯示的圖片
// 3.2.1設置默認狀態圖片
[btn setBackgroundImage:[UIImage imageNamed:norName] forState:UIControlStateNormal];
// 3.2.2設置不可用狀態圖片
[btn setBackgroundImage:[UIImage imageNamed:disName] forState:UIControlStateDisabled];
// 3.4添加按鈕到自定義TabBar
[self addSubview:btn];
// 3.5監聽按鈕點擊事件
[btn addTarget:self action:@selector(btnOnClick:) forControlEvents:UIControlEventTouchDown];
// 3.6設置默認選中按鈕
if (1 == self.subviews.count) {
[self btnOnClick:btn];
}
// 3.7設置按鈕高亮狀態不調整圖片
btn.adjustsImageWhenHighlighted = NO;
// 3.8設置tag
}
-(void)layoutSubviews
{
// 注意這是必需要寫的否則會出現不可預知的錯誤
// [super layoutSubviews];
NSLog(@"self.subviews.count = %lu",(unsigned long)self.subviews.count);
for (int i = 0; i < self.subviews.count ; i++) {
// NSLog(@"self.subviews.count = %lu",(unsigned long)self.subviews.count);
UIButton *btn = self.subviews[i];
// 3.3設置frame
// NSLog(@"initWithFrame %@", NSStringFromCGRect(self.frame));
CGFloat btnY = 0;
CGFloat btnW = self.frame.size.width / 5;
CGFloat btnH = self.frame.size.height;
CGFloat btnX = i * btnW;
btn.frame = CGRectMake(btnX, btnY, btnW, btnH);
btn.tag = i;
}
}
-(void)btnOnClick:(LKTabBarBtn * )btn
{
// NSLog(@"按鈕被點擊了 = %ld",(long)btn.tag);
if ([self.delegate respondsToSelector:@selector(tarBarDidSelectBtnFrom:to:)]) {
[self.delegate tarBarDidSelectBtnFrom:self.selectBtn.tag to:btn.tag];
}
// 0.取消上一次選中的按鈕
// self.selectBtn.selected = NO;
self.selectBtn.enabled = YES;
// 1.設置當前的點擊的按鈕的選中狀態
btn.enabled = NO;
// 2.記錄當前的按鈕
self.selectBtn = btn;
// 3.切換控制器
}
數據的傳遞經過代理
@protocol LKTabBarDelegate <NSObject>
-(void)tarBarDidSelectBtnFrom:(NSInteger)from to:(NSInteger)to;
@end
@interface LKTabBar : UIView
@property (nonatomic, weak) id<LKTabBarDelegate> delegate;
- (void)addTabBarButtonWithNormalImageName:(NSString *)norName andDisableImageName:(NSString *)disName;
@end
控制器操做
// 1.新建一個tabBar
LKTabBar * myTabBar = [[LKTabBar alloc]init];
// myTabBar.backgroundColor = [UIColor redColor];
myTabBar.frame = self.tabBar.bounds;
// 設置代理事件
myTabBar.delegate = self;
[self.tabBar addSubview:myTabBar];
for (int i = 0; i < self.viewControllers.count; i++) {
// 通知自定義TabBar建立按鈕
NSString *norImageName = [NSString stringWithFormat:@"TabBar%d", i + 1];
NSString *disableImageName = [NSString stringWithFormat:@"TabBar%dSel", i + 1];
// 只要調用自定義TabBar的該方法就會建立一個按鈕
[myTabBar addTabBarButtonWithNormalImageName:norImageName andDisableImageName:disableImageName];
控制器的跳轉
-(void)tarBarDidSelectBtnFrom:(NSInteger)from to:(NSInteger)to
{
self.selectedIndex = to;
NSLog(@"to = %ld",(long)to);
}
巧妙的使用父類
//只是調用一次
+(void)initialize
{
// NSLog(@"initialize");
// 1.設置導航條的主題
// 若是要同時設置不少UINavigationBar的樣式, 能夠經過設置UINavigationBar的主題的方式來設置以便簡化代碼
UINavigationBar *navBar = [UINavigationBar appearance];
// 1.1設置全部導航條的背景圖片
// 判斷當前運行的操做系統的版本
if ([[UIDevice currentDevice].systemVersion doubleValue] > 7.0) {
[navBar setBackgroundImage:[UIImage imageNamed:@"NavBar64"] forBarMetrics:UIBarMetricsDefault];
}else
{
[navBar setBackgroundImage:[UIImage imageNamed:@"NavBar"] forBarMetrics:UIBarMetricsDefault];
}
// 設置導航條上返回按鈕和圖片的顏色
[navBar setTintColor:[UIColor whiteColor]];
// 1.2設置全部導航條的標題顏色
NSMutableDictionary *md = [NSMutableDictionary dictionary];
md[NSFontAttributeName] = [UIFont systemFontOfSize:16];
md[NSForegroundColorAttributeName] = [UIColor whiteColor];
[navBar setTitleTextAttributes:md];
// 1.3設置UIBarButtonItem的主題
UIBarButtonItem *barItem = [UIBarButtonItem appearance];
// 判斷是不是IOS6 若是是IOS6就設置圖片
if (!([[UIDevice currentDevice].systemVersion doubleValue] > 7.0)) {
// 設置普通按鈕的圖片
UIImage *norImage = [UIImage imageNamed:@"NavButton"];
[barItem setBackgroundImage:norImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
UIImage *higImage = [UIImage imageNamed:@"NavButtonPressed"];
[barItem setBackgroundImage:higImage forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
// 設置返回按鈕的圖片
UIImage *norBackImage = [UIImage imageNamed:@"NavBackButton"];
[barItem setBackButtonBackgroundImage:norBackImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
UIImage *higBackImage = [UIImage imageNamed:@"NavBackButtonPressed"];
[barItem setBackButtonBackgroundImage:higBackImage forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
}else
{
// 是IOS7
NSMutableDictionary *barMd = [NSMutableDictionary dictionary];
barMd[NSFontAttributeName] = [UIFont systemFontOfSize:16];
barMd[NSForegroundColorAttributeName] = [UIColor whiteColor];
[barItem setTitleTextAttributes:barMd forState:UIControlStateNormal];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
// // 拿到目標控制器(即將要入棧的控制器), 設置它的自動隱藏tabbar
// viewController.hidesBottomBarWhenPushed = YES;
// [super pushViewController:viewController animated:animated];
// 拿到目標的控制器(即將要入棧的控制器),設置它的自動隱藏的tabBar
viewController.hidesBottomBarWhenPushed = YES;
[super pushViewController:viewController animated:YES];
}
不使用系統自帶的button
//
// LKTitleBtn.m
// 01-彩票
//
// Created by Lenny on 3/17/15.
// Copyright (c) 2015 Lenny. All rights reserved.
//
#import "LKTitleBtn.h"
#import <Availability.h>
@interface LKTitleBtn ()
@property(nonatomic,strong) UIFont * myFont;
@end
@implementation LKTitleBtn
-(id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
[self setup];
}
return self;
}
-(instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self setup];
}
return self;
}
-(void)setup
{
// 記錄按鈕標題的字體
self.myFont = [UIFont systemFontOfSize:15];
// 設置標題的字體
self.titleLabel.font = self.myFont;
// 設置按鈕的圖片顯示的內容默認爲拉伸不是居中
self.imageView.contentMode = UIViewContentModeCenter;
}
//用於返回按鈕上標題的位置,傳入按鈕的rect
-(CGRect)titleRectForContentRect:(CGRect)contentRect
{
CGFloat titleX = 0;
CGFloat titleY = 0;
CGFloat titleW = 0;
CGFloat titleH = contentRect.size.height;
// 獲取按鈕上的字體 1
[self titleForState:UIControlStateNormal];
// 獲取按鈕上的字體2
NSString * title = self.currentTitle;//建議使用這個方法 這個方法得到的是任何狀態下的title
CGSize maxSize = CGSizeMake(MAXFLOAT, MAXFLOAT);
NSMutableDictionary * md = [NSMutableDictionary dictionary];
// 死循環的緣由是self.titleLabel須要訪問titleLabel, 而self.titleLabel又須要調用當前方法獲取title的範圍, 全部死循環
// md[NSFontAttributeName] = self.titleLabel.font;
// NSLog(@"%@", self.myFont);
md[NSFontAttributeName] = self.myFont;
// 計算文字的範圍
// 判斷是不是xcode5 , 若是是就編譯一下代碼, 若是不是就不編譯
#ifdef __IPHONE_7_0
if (([[UIDevice currentDevice].systemVersion doubleValue] >= 7.0)) {
CGRect titleRect = [title boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:md context:nil];
titleW = titleRect.size.width;
}else{
CGSize titleSize = [title sizeWithFont:self.myFont];//過期的方法
titleW = titleSize.width;
}
#else
// XCODE4
CGSize titleSize = [title sizeWithFont:self.myFont];
titleW = titleSize.width;
#endif
return CGRectMake(titleX, titleY, titleW, titleH);
}
-(CGRect)imageRectForContentRect:(CGRect)contentRect{
CGFloat imageY = 0;
CGFloat imageH = contentRect.size.height;
CGFloat imageW = 16;//圖片的寬度
// 圖片的X = 按鈕的寬度 - 圖片的寬度
CGFloat imageX= contentRect.size.width - imageW;
return CGRectMake(imageX, imageY, imageW, imageH);
}
iOS 打電話的方式
最簡單最直接的方式:直接跳到撥號界面
1
NSURL *url = [NSURL URLWithString:@"tel://10010"];
[[UIApplication sharedApplication] openURL:url];
缺點
電話打完後,不會自動回到原應用,直接停留在通話記錄界面
2
撥號以前會彈框詢問用戶是否撥號,撥完後能自動回到原應用
NSURL *url = [NSURL URLWithString:@"telprompt://10010"];
[[UIApplication sharedApplication] openURL:url];
缺點
由於是私有API,因此可能不會被審覈經過
3
建立一個UIWebView來加載URL,撥完後能自動回到原應用
if (_webView == nil) {
_webView = [[UIWebView alloc] initWithFrame:CGRectZero];
}
[_webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"tel://10010"]]];
須要注意的是:這個webView千萬不要添加到界面上來,否則會擋住其餘界面
發短信-方法1
直接跳到發短信界面,可是不能指定短信內容,並且不能自動回到原應用
NSURL *url = [NSURL URLWithString:@"sms://10010"];
[[UIApplication sharedApplication] openURL:url];
發短信-方法2
若是想指定短信內容,那就得使用MessageUI框架
包含主頭文件
#import <MessageUI/MessageUI.h>
顯示發短信的控制器
MFMessageComposeViewController *vc = [[MFMessageComposeViewController alloc] init];
// 設置短信內容
vc.body = @"吃飯了沒?";
// 設置收件人列表
vc.recipients = @[@"10010", @"02010010"];
// 設置代理
vc.messageComposeDelegate = self;
// 顯示控制器
[self presentViewController:vc animated:YES completion:nil];
代理方法,當短信界面關閉的時候調用,發完後會自動回到原應用
- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result
{
// 關閉短信界面
[controller dismissViewControllerAnimated:YES completion:nil];
if (result == MessageComposeResultCancelled) {
NSLog(@"取消發送");
} else if (result == MessageComposeResultSent) {
NSLog(@"已經發出");
} else {
NSLog(@"發送失敗");
}
}
發郵件-方法1
用自帶的郵件客戶端,發完郵件後不會自動回到原應用
NSURL *url = [NSURL URLWithString:@"mailto://10010@qq.com"];
[[UIApplication sharedApplication] openURL:url];
發郵件-方法2
跟發短信的第2種方法差很少,只不過控制器類名叫作:MFMailComposeViewController
假設發送的郵件內容如右圖所示,代碼實現看備註
郵件發送後的代理方法回調,發完後會自動回到原應用
- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
// 關閉郵件界面
[controller dismissViewControllerAnimated:YES completion:nil];
if (result == MFMailComposeResultCancelled) {
NSLog(@"取消發送");
} else if (result == MFMailComposeResultSent) {
NSLog(@"已經發出");
} else {
NSLog(@"發送失敗");
}
}
打開其餘常見文件
若是想打開一些常見文件,好比html、txt、PDF、PPT等,均可以使用UIWebView打開
只須要告訴UIWebView文件的URL便可
至於打開一個遠程的共享資源,好比http協議的,也能夠調用系統自帶的Safari瀏覽器:
NSURL *url = [NSURL URLWithString:@」http://www.baidu.com"];
[[UIApplication sharedApplication] openURL:url];
有時候,須要在本應用中打開其餘應用,好比從A應用中跳轉到B應用
首先,B應用得有本身的URL地址(在Info.plist中配置)
B應用的URL地址就是:mj://ios.itcast.cn
接着在A應用中使用UIApplication完成跳轉
NSURL *url = [NSURL URLWithString:@"mj://ios.itcast.cn"];
[[UIApplication sharedApplication] openURL:url];
應用評分
爲了提升應用的用戶體驗,常常須要邀請用戶對應用進行評分
應用評分無非就是跳轉到AppStore展現本身的應用,而後由用戶本身撰寫評論
如何跳轉到AppStore,而且展現本身的應用
方法1
NSString *appid = @"444934666";
NSString *str = [NSString stringWithFormat:
@"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=%@", appid];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:str]];
方法2
NSString *str = [NSString stringWithFormat:
@"itms-apps://itunes.apple.com/cn/app/id%@?mt=8", appid];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:str]];
關於圖片的切割問題
// 8.切割圖片,將切割好的圖片設置到按鈕上
CGFloat imageH = NJImageHeight * [UIScreen mainScreen].scale;
CGFloat imageW = NJImageWidth * [UIScreen mainScreen].scale;
CGFloat imageY = 0;
CGFloat imageX = index * imageW;//設置第幾個的圖片的X的值
CGRect rect = CGRectMake(imageX, imageY, imageW, imageH);//切割的範圍
// 8.1根據rect切割圖片
// CGImage中rect是當作像素來使用
// UIKit 中是點座標系
// 座標系的特色:若是在非retain屏上 1個點等於1個像素
// 在retain屏上1個點等於2個像素
// 剪切默認狀態的圖片
CGImageRef norCGImageRef= CGImageCreateWithImageInRect(norImage.CGImage, rect);
// 將切割好的圖片轉換爲uiimage設置爲按鈕的背景
[btn setImage:[UIImage imageWithCGImage:norCGImageRef] forState:UIControlStateNormal];
// 剪切選中狀態圖片
CGImageRef selCGImageRef= CGImageCreateWithImageInRect(selImage.CGImage, rect);
// 將切割好的圖片轉換爲uiimage設置爲按鈕的背景
[btn setImage:[UIImage imageWithCGImage:selCGImageRef] forState:UIControlStateSelected];
CGFloat imageH = NJImageHeight * [UIScreen mainScreen].scale;//座標轉化爲像素
CGFloat imageW = NJImageWidth * [UIScreen mainScreen].scale;
[UIScreen mainScreen].scale 是不是Retina屏 這裏的返回值是 1 或者 2
轉盤的問題
// 建立12個按鈕添加到中間的輪盤上
for (int index = 0; index < 12; index++) {
// 1.建立按鈕
NJWheelButton *btn = [[NJWheelButton alloc] init];
// btn.backgroundColor = [UIColor redColor];
// 2.設置按鈕選中狀態的圖片
[btn setBackgroundImage:[UIImage imageNamed:@"LuckyRototeSelected"] forState:UIControlStateSelected];
// 3.設置按鈕的bounds
btn.bounds = CGRectMake(0, 0, 68, 143);
// 4.設置按鈕的錨點
btn.layer.anchorPoint = CGPointMake(0.5, 1);
// 5.設置按鈕的position
btn.layer.position = CGPointMake(self.frame.size.width * 0.5, self.frame.size.height * 0.5);
// 6然按鈕圍繞錨點旋轉(關鍵部分 先進行錨點的設置 再進行後面的操做)
// 6.1計算按鈕應該旋轉的弧度
CGFloat angle = (30 * index)/180.0 * M_PI;
btn.transform = CGAffineTransformMakeRotation(angle);
// 7.監聽按鈕的點擊事件
[btn addTarget:self action:@selector(update:) forControlEvents:UIControlEventTouchUpInside];
// 獲取當前是不是retain屏
// NSLog(@"%.1f", [UIScreen mainScreen].scale);
// 8.切割圖片,將切割好的圖片設置到按鈕上
CGFloat imageH = NJImageHeight * [UIScreen mainScreen].scale;//座標轉化爲像素
CGFloat imageW = NJImageWidth * [UIScreen mainScreen].scale;
CGFloat imageY = 0;
CGFloat imageX = index * imageW;//設置第幾個的圖片的X的值
CGRect rect = CGRectMake(imageX, imageY, imageW, imageH);//切割的範圍
// 8.1根據rect切割圖片
// CGImage中rect是當作像素來使用(這裏是關鍵點)
// UIKit 中是點座標系 可是在 CGImage中rect是當作像素來使用的 這裏所區分的是Retain屏幕的問題
// 座標系的特色:若是在非retain屏上 1個點等於1個像素
// 在retain屏上1個點等於2個像素
// 剪切默認狀態的圖片
CGImageRef norCGImageRef= CGImageCreateWithImageInRect(norImage.CGImage, rect);
// 將切割好的圖片轉換爲uiimage設置爲按鈕的背景(這裏必須進行uiimage的轉換不然會出現問題)
[btn setImage:[UIImage imageWithCGImage:norCGImageRef] forState:UIControlStateNormal];
// 剪切選中狀態圖片
CGImageRef selCGImageRef= CGImageCreateWithImageInRect(selImage.CGImage, rect);
// 將切割好的圖片轉換爲uiimage設置爲按鈕的背景
[btn setImage:[UIImage imageWithCGImage:selCGImageRef] forState:UIControlStateSelected];
// 添加按鈕到中間輪盤圖片上
[self.centerWheel addSubview:btn];
}
}
//讓輪盤上面的按鈕的點擊事件
- (void)update:(UIButton *)btn
{
self.selectButton.selected = NO;
btn.selected = YES;
self.selectButton = btn;
}
#import <UIKit/UIKit.h>
#define NJImageWidth 40
#define NJImageHeight 47
@interface NJWheelButton : UIButton
@end
#import "NJWheelButton.h"
//自定義了一個按鈕上面顯示圖片的操做 自定義一個button
@implementation NJWheelButton
- (CGRect)imageRectForContentRect:(CGRect)contentRect
{
CGFloat imageX = (contentRect.size.width - NJImageWidth ) * 0.5;
CGFloat imageY = 18;
return CGRectMake(imageX, imageY, NJImageWidth, NJImageHeight);
}
//這裏是爲了解決剛點擊下去的時候 會出現高亮的狀態 這裏實現的是自定義的高亮的狀態 將系統的高亮狀態屏蔽掉
- (void)setHighlighted:(BOOL)highlighted
{
}
@end
block
若是在block中有引用那個必定要講self設置微弱引用 __unsafe_unretained
// __unsafe_unretained NJShareViewController *unsafeSelf = self;
// __weak NJShareViewController *unsafeSelf = self;
// __weak 當對象釋放以後會自動設置爲nil, 而__unsafe_unretained不會 會出現空指針的錯誤
// self.age = 10;
// _age = 10;// 注意改行代碼會自動變成 self->_age = 10;
之後直接使用如下的操做
__weak typeof(self) unsafeSelf = self;//裝逼專用
進程
什麼是進程
進程是指在系統中正在運行的一個應用程序
每一個進程之間是獨立的,每一個進程均運行在其專用且受保護的內存空間內
好比同時打開QQ、Xcode,系統就會分別啓動2個進程
經過「活動監視器」能夠查看Mac系統中所開啓的進程
什麼是線程
1個進程要想執行任務,必須得有線程(每1個進程至少要有1條線程)
線程是進程的基本執行單元,一個進程(程序)的全部任務都在線程中執行
好比使用酷狗播放音樂、使用迅雷下載電影,都須要在線程中執行
線程的串行
1個線程中任務的執行是串行的
若是要在1個線程中執行多個任務,那麼只能一個一個地按順序執行這些任務
也就是說,在同一時間內,1個線程只能執行1個任務
好比在1個線程中下載3個文件(分別是文件A、文件B、文件C)
多線程
什麼是多線程
1個進程中能夠開啓多條線程,每條線程能夠並行(同時)執行不一樣的任務
進程 車間,線程 車間工人
多線程技術能夠提升程序的執行效率
好比同時開啓3條線程分別下載3個文件(分別是文件A、文件B、文件C)
多線程的原理
多線程的原理
同一時間,CPU只能處理1條線程,只有1條線程在工做(執行)
多線程併發(同時)執行,實際上是CPU快速地在多條線程之間調度(切換)
若是CPU調度線程的時間足夠快,就形成了多線程併發執行的假象
思考:若是線程很是很是多,會發生什麼狀況?
CPU會在N多線程之間調度,CPU會累死,消耗大量的CPU資源
每條線程被調度執行的頻次會下降(線程的執行效率下降)
多線程的優缺點
多線程的優勢
能適當提升程序的執行效率
能適當提升資源利用率(CPU、內存利用率)
多線程的缺點
開啓線程須要佔用必定的內存空間(默認狀況下,主線程佔用1M,子線程佔用512KB),若是開啓大量的線程,會佔用大量的內存空間,下降程序的性能
線程越多,CPU在調度線程上的開銷就越大
程序設計更加複雜:好比線程之間的通訊、多線程的數據共享
多線程在iOS開發中的應用
什麼是主線程
一個iOS程序運行後,默認會開啓1條線程,稱爲「主線程」或「UI線程」
主線程的主要做用
顯示\刷新UI界面
處理UI事件(好比點擊事件、滾動事件、拖拽事件等)
主線程的使用注意
別將比較耗時的操做放到主線程中
耗時操做會卡住主線程,嚴重影響UI的流暢度,給用戶一種「卡」的壞體驗
開發中的注意點
/ 凡是函數名種帶有create\copy\new\retain等字眼, 都須要在不須要使用這個數據的時候進行release
// GCD的數據類型在ARC環境下不須要再作release
// CF(Core Foundation)的數據類型在ARC環境下仍是須要再作release
在經常使用
數據請求GET方法
//
// ViewController.m
// NetWork 1
//
// Created by Lenny on 3/21/15.
// Copyright (c) 2015 Lenny. All rights reserved.
//
#import "ViewController.h"
#import "MBProgressHUD+MJ.h"
@interface ViewController ()<NSURLConnectionDataDelegate>
@property (weak, nonatomic) IBOutlet UITextField *nameField;
@property (weak, nonatomic) IBOutlet UITextField *pwdField;
- (IBAction)loginBtnClick;
/**
用來存放服務器返回的全部的數據
*/
@property(nonatomic,strong)NSMutableData * responseData;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
/**
登陸邏輯
*/
- (IBAction)loginBtnClick {
NSString *username = self.nameField.text;
NSString * pwd = self.pwdField.text;
if (username.length == 0) {//沒有用戶名的輸入
[MBProgressHUD showError:@"enter the user name"];
return;
}
if (pwd.length == 0) {
[MBProgressHUD showError:@"enter the pwd"];
return;
}
// 彈框正在登陸中....
[MBProgressHUD showMessage:@"正在拼命的爲您加載中...."];
// 2.發送請求給服務器(帳戶名稱和用戶密碼)
// GET請求:請求行\請求頭
// 2.1設置請求路徑
NSString * urlStr = [NSString stringWithFormat:@"http://192.168.1.200:8080/MJServer/login?username=%@&pwd=%@",username,pwd];
// 進行轉碼的操做 若是其中有中文字符 那麼將其轉走
urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
// URL裏面不能夠含有中文
NSURL * url = [NSURL URLWithString:urlStr];
// 2.2建立請求對象
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];//默認就是GET請求的方式
request.timeoutInterval = 10;//設置請求超時
// 發送請求 注意這裏的請求是異步請求
[self sendAsync:request];
NSLog(@"請求已經發出");
}
/**
發送異步請求的第一種方式1:類方法 block
*/
-(void)sendAsync:(NSURLRequest *)request
{
// 這裏隊列使用的是主線程
NSOperationQueue * queue = [NSOperationQueue mainQueue];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {//當請求結束的時候調用(拿到了服務器的數據,請求失敗)
// 隱藏HUD(刷新UI界面,必定要放在主線程中使用,不能放在子線程中使用)
[MBProgressHUD hideHUD];
/**
解析data :
{"error":"用戶名不存在"}
{"error":"密碼不正確"}
{"success":"登陸成功"}
*/
if (data) {//請求成功
NSDictionary * dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
NSString * error = dict[@"error"];
if (error) {//表示登陸失敗
[MBProgressHUD showError:error];
}else
{
[MBProgressHUD showSuccess:dict[@"success"]];
}
}else{
[MBProgressHUD showError:@"網絡繁忙,請稍後重試...."];
}
}];
}
/**
發送異步請求的方式2 :start方法,代理
*/
-(void)sendAsync2:(NSURLRequest *)request
{
NSURLConnection * conn = [NSURLConnection connectionWithRequest:request delegate:self];
[conn start];//異步執行開始
}
#pragma mark -NSURLConnectionDataDelegate
/**
請求錯誤(失敗)的時候調用(請求超時\斷網\沒有網絡,通常是指客戶頓的錯誤)
*/
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
NSLog(@"connection:didFailWithError");
[MBProgressHUD hideHUD];
[MBProgressHUD showError:@"網絡繁忙,請稍後再試"];
}
/**當接受到服務器的響應的時候(聯通了服務器)就會調用
*/
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(@"connection:didReceiveResponse");
// 初始化數據 告訴容器 你能夠接收數據了 將數據器準備好
self.responseData = [NSMutableData data];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(@"connection:didReceiveData");
// 將接收到數據放入到容器
[self.responseData appendData:data];
}
/**當服務器的數據接受完畢以後就會調用
*/
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(@"connectionDidFinishLoading");
// 隱藏HUD
[MBProgressHUD hideHUD];
// 解析服務器返回來的數據
NSDictionary * dict = [NSJSONSerialization JSONObjectWithData:self.responseData options:NSJSONReadingMutableLeaves error:nil ];
NSString *error = dict[@"error"];
if (error) {//登陸失敗
[MBProgressHUD showError:error];
}else{
NSString * success = dict[@"success"];
[MBProgressHUD showSuccess:success];
}
}
@end
POST
//
// ViewController.m
// POST
//
// Created by Lenny on 3/21/15.
// Copyright (c) 2015 Lenny. All rights reserved.
//
#import "ViewController.h"
#import "MBProgressHUD+MJ.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *nameField;
@property (weak, nonatomic) IBOutlet UITextField *pwdField;
- (IBAction)loginBtnClick;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
/**
登陸邏輯
*/
- (IBAction)loginBtnClick {
NSString *username = self.nameField.text;
NSString * pwd = self.pwdField.text;
if (username.length == 0) {//沒有用戶名的輸入
[MBProgressHUD showError:@"enter the user name"];
return;
}
if (pwd.length == 0) {
[MBProgressHUD showError:@"enter the pwd"];
return;
}
// 彈框正在登陸中....
[MBProgressHUD showMessage:@"正在拼命的爲您加載中...."];
// 2.發送請求給服務器(帳戶名稱和用戶密碼)
// GET請求:請求行\請求頭
// 2.1設置請求路徑
NSString * urlStr = [NSString stringWithFormat:@"http://192.168.1.200:8080/LKServer/login"];
// 進行轉碼的操做 若是其中有中文字符 那麼將其轉走
urlStr = [urlStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
// URL裏面不能夠含有中文
NSURL * url = [NSURL URLWithString:urlStr];
// 2.2建立請求對象
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];//默認就是GET請求的方式
request.HTTPMethod = @"POST";//設置成爲POST請求的方式
// 經過請求頭告訴服務器 客戶端的類型
[request setValue:@"iOS" forKey:@"User-Agent"];
// 設置請求體
NSString * param = [NSString stringWithFormat:@"username=%@&pwd=%@", username, pwd];
request.HTTPBody = [param dataUsingEncoding:NSUTF8StringEncoding];
request.timeoutInterval = 5;//設置請求超時
// 發送請求 注意這裏的請求是異步請求
// 發送數據請求
NSOperationQueue * queue = [NSOperationQueue mainQueue];
[NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {// 當請求結束的時候調用 (拿到了服務器的數據, 請求失敗)
// 隱藏HUD (刷新UI界面, 必定要放在主線程, 不能放在子線程)
[MBProgressHUD hideHUD];
if (data) {//請求成功
NSDictionary * dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
NSString * error = dict[@"error"];
if (error) {//登陸失敗
[MBProgressHUD showError:error];
}else
{
[MBProgressHUD showSuccess:dict[@"success"]];
}
}else{
[MBProgressHUD showError:@"網絡繁忙,請稍後再試"];
}
}];
}
@end
文件的解壓與壓縮
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSString * path = [[NSBundle mainBundle]pathForResource:@"guo.zip" ofType:nil];
NSString * despth = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask , YES)lastObject];
NSString *filepath = [despth stringByAppendingPathComponent:@"guo"];
NSLog(@"解壓中");
NSLog(@"%@",filepath);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[SSZipArchive unzipFileAtPath:path toDestination:filepath];
});
}
- (void)createZip
{
// [[NSBundle mainBundle] pathForResource:@"minion_01.png" ofType:nil];
// 1.得到mainBundle中全部的png的圖片路徑
NSArray *pngs = [[NSBundle mainBundle] pathsForResourcesOfType:@"png" inDirectory:nil];
// 2.zip文件路徑
NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *zipFilepath = [caches stringByAppendingPathComponent:@"pngs.zip"];
// 3.建立zip文件
[SSZipArchive createZipFileAtPath:zipFilepath withFilesAtPaths:pngs];
}
四個容易混淆的屬性
四個容易混淆的屬性:
1. textAligment : 文字的水平方向的對齊方式
1> 取值
NSTextAlignmentLeft = 0, // 左對齊
NSTextAlignmentCenter = 1, // 居中對齊
NSTextAlignmentRight = 2, // 右對齊
2> 哪些控件有這個屬性 : 通常可以顯示文字的控件都有這個屬性
* UITextField
* UILabel
* UITextView
2. contentVerticalAlignment : 內容的垂直方向的對齊方式
1> 取值
UIControlContentVerticalAlignmentCenter = 0, // 居中對齊
UIControlContentVerticalAlignmentTop = 1, // 頂部對齊
UIControlContentVerticalAlignmentBottom = 2, // 底部對齊
2> 哪些控件有這個屬性 : 繼承自UIControl的控件或者UIControl自己
* UIControl
* UIButton
* UITextField
* ...
3. contentHorizontalAlignment : 內容的水平方向的對齊方式
1> 取值
UIControlContentHorizontalAlignmentCenter = 0, // 居中對齊
UIControlContentHorizontalAlignmentLeft = 1, // 左對齊
UIControlContentHorizontalAlignmentRight = 2, // 右對齊
2> 哪些控件有這個屬性 : 繼承自UIControl的控件或者UIControl自己
* UIControl
* UIButton
* UITextField
* ...
4. contentMode : 內容模式(控制內容的對齊方式), 通常對UIImageView頗有用
1> 取值
/**
規律:
1.Scale : 圖片會拉伸
2.Aspect : 圖片會保持原來的寬高比
*/
// 前3個狀況, 圖片都會拉伸
// (默認)拉伸圖片至填充整個UIImageView(圖片的顯示尺寸會跟UIImageView的尺寸同樣)
UIViewContentModeScaleToFill,
// 按照圖片原來的寬高比進行伸縮, 伸縮至適應整個UIImageView(圖片的內容不能超出UIImageView的尺寸範圍)
UIViewContentModeScaleAspectFit,
// 按照圖片原來的寬高比進行伸縮, 伸縮至 圖片的寬度和UIImageView的寬度同樣 或者 圖片的高度和UIImageView的高度同樣
UIViewContentModeScaleAspectFill,
// 後面的全部狀況, 都會按照圖片的原來尺寸顯示, 不會進行拉伸
UIViewContentModeRedraw, // 當控件的尺寸改變了, 就會重繪一次(從新調用setNeedsDisplay, 調用drawRect:)
UIViewContentModeCenter,
UIViewContentModeTop,
UIViewContentModeBottom,
UIViewContentModeLeft,
UIViewContentModeRight,
UIViewContentModeTopLeft,
UIViewContentModeTopRight,
UIViewContentModeBottomLeft,
UIViewContentModeBottomRight,
2> 哪些控件有這個屬性 : 全部UI控件都有
5. 若是有多個屬性的做用衝突了, 只有1個屬性有效(就近原則)
item
1、UINavigationItem
1> 得到方式
self.navigationItem // self是指控制器
2> 做用
能夠用來設置當前控制器頂部導航欄的內容
// 設置導航欄中間的內容
self.navigationItem.title
self.navigationItem.titleView
2、UIBarButtonItem
1> 用在什麼地方
// 設置導航欄左上角的內容
self.navigationItem.leftBarButtonItem
// 設置導航欄右上角的內容
self.navigationItem.rightBarButtonItem
2> 做用
至關於一個按鈕
3、UITabBarItem
1> 得到方式
self.tabBarItem // self是指控制器
2> 做用
能夠用來設置當前控制器對應的選項卡標籤的內容
// 標籤的標題
self.tabBarItem.title
// 標籤的圖標
self.tabBarItem.image
// 標籤的選中圖標
self.tabBarItem.selectdImage
4、UINavigationBar
1. 導航控制器頂部的欄(UI控件)
2. UINavigationBar上面顯示什麼內容, 取決於當前控制器的navigationItem屬性
3. UINavigationBar是view, navigationItem是model
4. 由navigationItem給UINavigationBar提供顯示的數據
5、UITabBar
1. UITabBarController底部的選項卡條
6、UITabBarButton
1. UITabBar底部的每個標籤
2. 每個UITabBarButton裏面顯示什麼內容,取決於當前控制器的tabBarItem屬性
3. UITabBarButton是view, tabBarItem是model
4. 由tabBarItem給UITabBarButton提供顯示的數據