面試 (四) : UI篇

Size Classes 具體使用

? 對屏幕進行分類

UIView和CALayer是什么關系?

? UIView顯示在屏幕上歸功于CALayer,通過調用drawRect方法來渲染自身的內容,調節(jié)CALayer屬性可以調整UIView的外觀,UIView繼承自UIResponder,比起CALayer可以響應用戶事件,Xcode6之后可以方便的通過視圖調試功能查看圖層之間的關系

? UIView是iOS系統(tǒng)中界面元素的基礎,所有的界面元素都繼承自它。它內部是由Core Animation來實現(xiàn)的,它真正的繪圖部分,是由一個叫CALayer(Core Animation Layer)的類來管理。UIView本身,更像是一個CALayer的管理器,訪問它的跟繪圖和坐標有關的屬性,如frame,bounds等,實際上內部都是訪問它所在CALayer的相關屬性

? UIView有個layer屬性,可以返回它的主CALayer實例,UIView有一個layerClass方法,返回主layer所使用的類,UIView的子類,可以通過重載這個方法,來讓UIView使用不同的CALayer來顯示,如:

  • (class) layerClass {

    // 使某個UIView的子類使用GL來進行繪制

return ([CAEAGLLayerclass]);

}

? UIView的CALayer類似UIView的子View樹形結構,也可以向它的layer上添加子layer,來完成某些特殊的顯示。例如下面的代碼會在目標View上敷上一層黑色的透明薄膜。

grayCover
= [[CALayer alloc]init];

grayCover.backgroudColor
= [[UIColor blackColor]colorWithAlphaComponent:0.2].CGColor;

[self.layer addSubLayer:grayCover];

? 補充部分,這部分有深度了,大致了解一下吧,UIView的layer樹形在系統(tǒng)內部被系統(tǒng)維護著三份copy

○ 邏輯樹,就是代碼里可以操縱的,例如更改layer的屬性等等就在這一份

○ 動畫樹,這是一個中間層,系統(tǒng)正是在這一層上更改屬性,進行各種渲染操作

○ 顯示樹,這棵樹的內容是當前正被顯示在屏幕上的內容

○ 這三棵樹的邏輯結構都是一樣的,區(qū)別只有各自的屬性

loadView的作用?

? loadView用來自定義view,只要實現(xiàn)了這個方法,其他通過xib或storyboard創(chuàng)建的view都不會被加載

看懂控制器view創(chuàng)建的這個圖就行


1.jpg

IBOutlet連出來的視圖屬性為什么可以被設置成weak?

? 因為父控件的subViews數(shù)組已經(jīng)對它有一個強引用

IB中User Defined Runtime Attributes如何使用?

? User Defined Runtime Attributes是一個不被看重但功能非常強大的的特性,它能夠通過KVC的方式配置一些你在interface builder中不能配置的屬性

? 當你希望在IB中作盡可能多得事情,這個特性能夠幫助你編寫更加輕量級的viewcontroller

沙盒目錄結構是怎樣的?各自用于那些場景?

? Application:存放程序源文件,上架前經(jīng)過數(shù)字簽名,上架后不可修改

? Documents:常用目錄,iCloud備份目錄,存放數(shù)據(jù)

?
Library

○ Caches:存放體積大又不需要備份的數(shù)據(jù)

○ Preference:設置目錄,iCloud會備份設置信息

? tmp:存放臨時文件,不會被備份,而且這個文件下的數(shù)據(jù)有可能隨時被清除的可能

pushViewController和presentViewController有什么區(qū)別

? 兩者都是在多個試圖控制器間跳轉的函數(shù)

? presentViewController提供的是一個模態(tài)視圖控制器(modal)

? pushViewController提供一個棧控制器數(shù)組,push/pop

請簡述UITableView的復用機制

? 每次創(chuàng)建cell的時候通過dequeueReusableCellWithIdentifier:方法創(chuàng)建cell,它先到緩存池中找指定標識的cell,如果沒有就直接返回nil

? 如果沒有找到指定標識的cell,那么會通過initWithStyle:reuseIdentifier:創(chuàng)建一個cell

? 當cell離開界面就會被放到緩存池中,以供下次復用

如何高性能的給
UIImageView 加個圓角?

? 不好的解決方案

○ 使用下面的方式會強制Core Animation提前渲染屏幕的離屏繪制, 而離屏繪制就會給性能帶來負面影響,會有卡頓的現(xiàn)象出現(xiàn)

self.view.layer.cornerRadius = 5;

self.view.layer.masksToBounds =YES;

? 正確的解決方案:使用繪圖技術

(UIImage *)circleImage

{

// NO代表透明

UIGraphicsBeginImageContextWithOptions(self.size,
NO, 0.0);

// 獲得上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();

// 添加一個圓

CGRect rect = CGRectMake(0, 0, self.size.width,
self.size.height);

CGContextAddEllipseInRect(ctx, rect);

// 裁剪

CGContextClip(ctx);

// 將圖片畫上去

[self drawInRect:rect];

UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

// 關閉上下文

UIGraphicsEndImageContext();

return image;

}

? 還有一種方案:使用了貝塞爾曲線"切割"個這個圖片, 給UIImageView 添加了的圓角,其實也是通過繪圖技術來實現(xiàn)的

UIImageView
*imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];

imageView.center
= CGPointMake(200, 300);

UIImage
*anotherImage = [UIImage imageNamed:@"image"];

UIGraphicsBeginImageContextWithOptions(imageView.bounds.size,
NO, 1.0);

[[UIBezierPath
bezierPathWithRoundedRect:imageView.bounds

cornerRadius:50] addClip];

[anotherImage
drawInRect:imageView.bounds];

imageView.image
= UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

[self.view addSubview:imageView];

使用drawRect有什么影響?

? drawRect方法依賴Core Graphics框架來進行自定義的繪制

? 缺點:它處理touch事件時每次按鈕被點擊后,都會用setNeddsDisplay進行強制重繪;而且不止一次,每次單點事件觸發(fā)兩次執(zhí)行。這樣的話從性能的角度來說,對CPU和內存來說都是欠佳的。特別是如果在我們的界面上有多個這樣的UIButton實例,那就會很糟糕了

? 這個方法的調用機制也是非常特別. 當你調用setNeedsDisplay 方法時, UIKit 將會把當前圖層標記為dirty,但還是會顯示原來的內容,直到下一次的視圖渲染周期,才會將標記為dirty 的圖層重新建立Core Graphics上下文,然后將內存中的數(shù)據(jù)恢復出來, 再使用CGContextRef 進行繪制

描述下SDWebImage里面給UIImageView加載圖片的邏輯

? SDWebImage 中為UIImageView 提供了一個分類UIImageView+WebCache.h, 這個分類中有一個最常用的接口sd_setImageWithURL:placeholderImage:,會在真實圖片出現(xiàn)前會先顯示占位圖片,當真實圖片被加載出來后在替換占位圖片

? 加載圖片的過程大致如下:

○ 首先會在SDWebImageCache 中尋找圖片是否有對應的緩存, 它會以url
作為數(shù)據(jù)的索引先在內存中尋找是否有對應的緩存

○ 如果緩存未找到就會利用通過MD5處理過的key來繼續(xù)在磁盤中查詢對應的數(shù)據(jù), 如果找到了, 就會把磁盤中的數(shù)據(jù)加載到內存中,并將圖片顯示出來

○ 如果在內存和磁盤緩存中都沒有找到,就會向遠程服務器發(fā)送請求,開始下載圖片

○ 下載后的圖片會加入緩存中,并寫入磁盤中

○ 整個獲取圖片的過程都是在子線程中執(zhí)行,獲取到圖片后回到主線程將圖片顯示出來

設計個簡單的圖片內存緩存器

? 類似上面SDWebImage實現(xiàn)原理即可

? 一定要有移除策略:釋放數(shù)據(jù)模型對象

控制器的生命周期

? 就是問的view的生命周期,下面已經(jīng)按方法執(zhí)行順序進行了排序

// 自定義控制器view,這個方法只有實現(xiàn)了才會執(zhí)行

  • (void)loadView

{

self.view = [[UIView alloc] init];

self.view.backgroundColor = [UIColor
orangeColor];

}

// view是懶加載,只要view加載完畢就調用這個方法

  • (void)viewDidLoad

{

[super viewDidLoad];

NSLog(@"%s",func);

}

// view即將顯示

  • (void)viewWillAppear:(BOOL)animated

{

[super viewWillAppear:animated];

NSLog(@"%s",func);

}

// view即將開始布局子控件

  • (void)viewWillLayoutSubviews

{

[super viewWillLayoutSubviews];

NSLog(@"%s",func);

}

// view已經(jīng)完成子控件的布局

  • (void)viewDidLayoutSubviews

{

[super viewDidLayoutSubviews];

NSLog(@"%s",func);

}

// view已經(jīng)出現(xiàn)

  • (void)viewDidAppear:(BOOL)animated

{

[super viewDidAppear:animated];

NSLog(@"%s",func);

}

// view即將消失

  • (void)viewWillDisappear:(BOOL)animated

{

[super viewWillDisappear:animated];

NSLog(@"%s",func);

}

// view已經(jīng)消失

  • (void)viewDidDisappear:(BOOL)animated

{

[super viewDidDisappear:animated];

NSLog(@"%s",func);

}

// 收到內存警告

  • (void)didReceiveMemoryWarning

{

[super didReceiveMemoryWarning];

NSLog(@"%s",func);

}

// 方法已過期,即將銷毀view

  • (void)viewWillUnload

{

}

// 方法已過期,已經(jīng)銷毀view

  • (void)viewDidUnload

{

}

你是怎么封裝一個view的

? 可以通過純代碼或者xib的方式來封裝子控件

? 建立一個跟view相關的模型,然后將模型數(shù)據(jù)傳給view,通過模型上的數(shù)據(jù)給view的子控件賦值

/**

  • 純代碼初始化控件時一定會走這個方法

*/

  • (instancetype)initWithFrame:(CGRect)frame

{

if(self
= [super initWithFrame:frame])

{

  [self setup];

}

return

self;

}

/**

  • 通過xib初始化控件時一定會走這個方法

*/

  • (id)initWithCoder:(NSCoder *)aDecoder

{

if(self
= [super initWithCoder:aDecoder])

{

  [self setup];

}

return

self;

}

  • (void)setup

{

// 初始化代碼

}

如何進行iOS6、7的適配

? 通過判斷版本來控制,來執(zhí)行響應的代碼

? 功能適配:保證同一個功能在6、7上都能用

? UI適配:保證各自的顯示風格

// iOS版本為7.0以上(包含7.0)

define iOS7 ([[UIDevice currentDevice].systemVersion

doubleValue]>=7.0)

如何渲染UILabel的文字?

? 通過NSAttributedString/NSMutableAttributedString(富文本)

UIScrollView的contentSize能否在viewDidLoad中設置?

? 能

? 因為UIScrollView的內容尺寸是根據(jù)其內部的內容來決定的,所以是可以在viewDidLoad中設置的

? 補充:(這僅僅是一種特殊情況)

○ 前提,控制器B是控制器A的一個子控制器,且控制器B的內容只在控制器A的view的部分區(qū)域中顯示

○ 假設控制器B的view中有一個UIScrollView這樣一個子控件

○ 如果此時在控制器B的viewDidLoad中設置UIScrollView的contentSize的話會導致不準確的問題

○ 因為任何控制器的view在viewDidLoad的時候的尺寸都是不準確的,如果有子控件的尺寸依賴父控件的尺寸,在這個方法中設置會導致子控件的frame不準確,所以這時應該在下面的方法中設置子控件的尺寸

-(void)viewDidLayoutSubviews;

觸摸事件的傳遞

? 觸摸事件的傳遞是從父控件傳遞到子控件

? 如果父控件不能接收觸摸事件,那么子控件就不可能接收到觸摸事件

? 不能接受觸摸事件的四種情況

○ 不接收用戶交互,即:userInteractionEnabled = NO

○ 隱藏,即:hidden = YES

○ 透明,即:alpha <= 0.01

○ 未啟用,即:enabled = NO

? 提示:UIImageView的userInteractionEnabled默認就是NO,因此UIImageView以及它的子控件默認是不能接收觸摸事件的

? 如何找到最合適處理事件的控件:

○ 首先,判斷自己能否接收觸摸事件

§ 可以通過重寫hitTest:withEvent:方法驗證

○ 其次,判斷觸摸點是否在自己身上

§ 對應方法pointInside:withEvent:

○ 從后往前(先遍歷最后添加的子控件)遍歷子控件,重復前面的兩個步驟

○ 如果沒有符合條件的子控件,那么就自己處理

事件響應者鏈

? 如果當前view是控制器的view,那么就傳遞給控制器

? 如果控制器不存在,則將其傳遞給它的父控件

? 在視圖層次結構的最頂層視圖也不能處理接收到的事件或消息,則將事件或消息傳遞給UIWindow對象進行處理

? 如果UIWindow對象也不處理,則將事件或消息傳遞給UIApplication對象

? 如果UIApplication也不能處理該事件或消息,則將其丟棄

? 補充:如何判斷上一個響應者

○ 如果當前這個view是控制器的view,那么控制器就是上一個響應者

如果當前這個view不是控制器的view,那么父控件就是上一個響應者


2.jpg

如何實現(xiàn)類似QQ的三角形頭像

?
Quartz2D

? 使用coreGraphics裁剪出一個三角形

核心動畫里包含什么?

? 基本動畫

? 回頭自己總結吧

如何使用核心動畫?

? 創(chuàng)建

? 設置相關屬性

添加到CALayer上,會自動執(zhí)行動畫

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 在iOS中隨處都可以看到絢麗的動畫效果,實現(xiàn)這些動畫的過程并不復雜,今天將帶大家一窺iOS動畫全貌。在這里你可以看...
    F麥子閱讀 5,141評論 5 13
  • 在iOS中隨處都可以看到絢麗的動畫效果,實現(xiàn)這些動畫的過程并不復雜,今天將帶大家一窺ios動畫全貌。在這里你可以看...
    每天刷兩次牙閱讀 8,566評論 6 30
  • 7、不使用IB是,下面這樣做有什么問題? 6、請說說Layer和View的關系,以及你是如何使用它們的。 1.首先...
    AlanGe閱讀 719評論 0 1
  • 1.自定義控件 a.繼承某個控件 b.重寫initWithFrame方法可以設置一些它的屬性 c.在layouts...
    圍繞的城閱讀 3,469評論 2 4
  • 我喜歡被汗水包裹的寧靜,在哪里我能找到原始的自己,最初的自己,沒有爾虞我詐的自己,我想做回我自己……不與人爭,不與...
    咪渣哥閱讀 173評論 0 0