可能碰到的iOS筆試面試題(10)--UI

UI

viewcontroller的一些方法的說明viewDidLoad,viewWillDisappear, viewWillAppear方法的 順序和作用?

viewWillAppear:視圖即將可見時調用。默認情況下不執行任何操作

viewDidAppear:視圖已完全過渡到屏幕上時調用

viewWillDisappear:視圖被駁回時調用,覆蓋或以其他方式隱藏。默認情況下不執行任何操作

viewDidDisappear:視圖被駁回后調用,覆蓋或以其他方式隱藏。默認情況下不執行任何操作loadView; .這是當他們沒有正在使用nib視圖頁面,子類將會創建自己的自定義視圖層。絕不能直接調用。

viewDidLoad:在視圖加載后被調用,如果是在代碼中創建的視圖加載器,他將會在loadView方法后被調用,如果是從nib視圖頁面輸出,他將會在視圖設置好后后被調用。

「initWithNibName: bundle:」載入nib檔案來初始化「loadView」載入視圖「viewDidLoad」在載入視圖至內存后會呼叫的方法「viewDidUnload」在視圖從內存中釋放后會呼叫的方法 (當內存過低,釋放一些不需要的視圖時調用)

「viewWillAppear」當收到視圖在視窗將可見時的通知會呼叫的方法

「viewDidAppear」當收到視圖在視窗已可見時的通知會呼叫的方法

「viewWillDisappear」當收到視圖將去除、被覆蓋或隱藏于視窗時的通知會呼叫的方法

「viewDidDisappear」當收到視圖已去除、被覆蓋或隱藏于視窗時的通知會呼叫的方法

「didReceiveMemoryWarning」收到系統傳來的內存警告通知后會執行的方法

「shouldAutorotateToInterfaceOrientation」是否支持不同方向的旋轉視圖

「willAnimateRotationToInterfaceOrientation」在進行旋轉視圖前的會執行的方法(用于調整旋轉視圖之用)

代碼的執行順序

1、 alloc 創建對象,分配空間

2、init (initWithNibName) 初始化對象,初始化數據

3、loadView 從nib載入視圖 ,通常這一步不需要去干涉。除非你沒有使用xib文件創建視圖

4、viewDidLoad 載入完成,可以進行自定義數據以及動態創建其他控件

5、viewWillAppear 視圖將出現在屏幕之前,馬上這個視圖就會被展現在屏幕上了

6、viewDidAppear 視圖已在屏幕上渲染完成當一個視圖被移除屏幕并且銷毀的時候的執行順序,這個順序差不多和上面的相反

1、viewWillDisappear 視圖將被從屏幕上移除之前執行

2、viewDidDisappear 視圖已經被從屏幕上移除,用戶看不到這個視圖了

3、dealloc 視圖被銷毀,此處需要對你在init和viewDidLoad中創建的對象進行釋放

什么是key window?

  • 一個窗口當前能接受鍵盤和非觸摸事件時,便被認為是主窗口。而觸摸事件則被投遞到觸摸發生的窗口,沒有相應坐標值的事件被投遞到主窗口。同一時刻只有一個窗口是主窗口。

談一談你是怎么封裝view的

- 先添加所需子控件
- 再接收模型數據根據模型數據設置子控件數據和位置
- 簡而言之, 自己的事情自己做, 把不需要暴露出去的封裝起來

簡單說一下APP的啟動過程,從main文件開始說起

    程序啟動分為兩類:1.有storyboard 2.沒有storyboard
有storyboard情況下:
1.main函數
2.UIApplicationMain
* 創建UIApplication對象
* 創建UIApplication的delegate對象
3.根據Info.plist獲得Main.storyboard的文件名,加載Main.storyboard(有storyboard)
* 創建UIWindow
* 創建和設置UIWindow的rootViewController
* 顯示窗口

沒有storyboard情況下:
1.main函數
2.UIApplicationMain
* 創建UIApplication對象
* 創建UIApplication的delegate對象
3.delegate對象開始處理(監聽)系統事件(沒有storyboard)
* 程序啟動完畢的時候, 就會調用代理的application:didFinishLaunchingWithOptions:方法
* 在application:didFinishLaunchingWithOptions:中創建UIWindow
* 創建和設置UIWindow的rootViewController
* 顯示窗口

怎么解決緩存池滿的問題(cell)

iOS中不存在緩存池滿的情況,因為通常我們ios中開發,對象都是在需要的時候才會創建,有種常用的說話叫做懶加載,還有在UITableView中一般只會創建剛開始出現在屏幕中的cell,之后都是從緩存池里取,不會在創建新對象。緩存池里最多也就一兩個對象,緩存池滿的這種情況一般在開發java中比較常見,java中一般把最近最少使用的對象先釋放。

UIButton與UITableView的層級結構

  • 繼承結構,屬于內部的子控件結構

  • UIButton為:UIButton > UIControl > UIView > UIResponder > NSObject

  • UITableView為:UITableView > UIScrollView > UIView > UIResponder > NSObject

設置scroll view的contensize能在Viewdidload里設置么,為什么

  • 一般情況下可以設置在viewDidLoad中,但在autolayout下,系統會在viewDidAppear之前根據subview的constraint重新計算scrollview的contentsize。 這就是為什么,在viewdidload里面手動設置了contentsize沒用。因為在后面,會再重新計算一次,前面手動設置的值會被覆蓋掉。

  • 解決辦法就是:

    • 去除autolayout選項,自己手動設置contentsize

    • 如果要使用autolayout,要么自己設置完subview的constraint,然后讓系統自動根據constraint計算出contentsize。要么就在viewDidAppear里面自己手動設置contentsize。

簡述你對UIView、UIWindow和CALayer的理解

  • UIView: 屬于UIkit.framework框架,負責渲染矩形區域的內容,為矩形區域添加動畫,響應區域的觸摸事件,布局和管理一個或多個子視圖

  • UIWindow:屬于UIKit.framework框架,是一種特殊的UIView,通常在一個程序中只會有一個UIWindow,但可以手動創建多個UIWindow,同時加到程序里面。UIWindow在程序中主要起到三個作用:

    • 作為容器,包含app所要顯示的所有視圖

    • 傳遞觸摸消息到程序中view和其他對象

    • 與UIViewController協同工作,方便完成設備方向旋轉的支持

  • CAlayer:屬于QuartzCore.framework,是用來繪制內容的,對內容進行動畫處理依賴與UIView來進行顯示,不能處理用戶事件。

  • UIView和CALayer是相互依賴的,UIView依賴CALayer提供內容,CALayer依賴UIView的容器顯示繪制內容。

  • (補充)UIViewController:每個視圖控制器都有一個自帶的視圖,并且負責這個視圖相關的一切事務。方便管理視圖中的子視圖,負責model與view的通信;檢測設備旋轉以及內存警告;是所有視圖控制類的積累,定義了控制器的基本功能。

frame和bounds有什么不同?(Difference between frame and bounds?)

  • frame指的是:該view在父view坐標系統中的位置和大小(參照點是父親的坐標系統)

  • bounds指的是:該view在本身坐標系統中的位置和大小(參照點是本身坐標系統)

關于頁面間傳值的問題?

屬性傳值:A頁面設置屬性 NSString *paramString,在跳轉B頁面的時候初始化paramString。
//A頁面.h文件
@property (nonatomic, copy)NSString *paramString;
//A頁面.m文件
 NextViewController *nextVC = [[NextViewController alloc] init];
 nextVC.paramString = @"參數傳質";
 [self presentViewController:nextVC animated:YES completion:nil];
 
委托delegate傳值:在B頁面定義delegate,并且設置delegate屬性,在A頁面實現delegate協議

通知notification傳值:在B頁面中發送通知,在A頁面注冊觀察者并且在不用的時候移除觀察者。
//B頁面發送通知 
[[NSNotificationCenter defaultCenter] postNotificationName:@"ChangeNameNotification" object:self userInfo:@{@"name":self.nameTextField.text}];
 [self dismissViewControllerAnimated:YES completion:nil]; 
//A頁面注冊觀察者
 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ChangeNameNotification:) name:@"ChangeNameNotification" object:nil];
}
//觀察到通知時候的處理方法
-(void)ChangeNameNotification:(NSNotification*)notification{
    NSDictionary *nameDictionary = [notification userInfo];
    self.nameLabel.text = [nameDictionary objectForKey:@"name"];
}
//通知不使用的時候移除觀察者
 [[NSNotificationCenter defaultCenter] removeObserver:self];


block傳值:在B頁面定義一個block類型的變量,在B頁面跳轉A的時候調用這個block。在A頁面跳轉到B頁面的時候對B頁面的block賦值。
//B頁面定義block,并設置block類型的變量
 typedef void (^ablock)(NSString *str);
 @property (nonatomic, copy) ablock block; 
//B頁面跳轉到A頁面調用這個block
 self.block(self.nameTextField.text);
 [self dismissViewControllerAnimated:YES completion:nil];
//A頁面跳轉到B頁面的時候對B頁面的block賦值,這樣在B頁面跳轉的時候就會回調這個block函數
 [self presentViewController:second animated:YES completion:nil];
 second.block = ^(NSString *str){
        self.nameLabel.text = str;
    }; 
 
 kvo傳值:在A頁面設置B頁面的變量second,并且對這個變量進行觀察
- (void)addObserver:(NSObject * _Nonnull)anObserver forKeyPath:(NSString * _Nonnull)keyPath options:(NSKeyValueObservingOptions)options context:(void * _Nullable)context
并在A頁面實現
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context方法。
在B頁面對變量keyPath進行設置,在A頁面都會觀察的到。
@property (nonatomic, strong) SecondViewController *second; 
//在A視圖跳轉到B視圖的地方添加如下代碼 
self.second = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil];
[self.second addObserver:self forKeyPath:@"userName" options:NSKeyValueObservingOptionNew context:nil];
[self presentViewController:self.second animated:YES completion:nil];
//實現這個觀察對象的方法
 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 
//在B頁面對userName進行設置,在A頁面都可以間聽到
 
 單例模式傳值:通過全局的方式保存
 對于通知代理面試常問, 代理和通知分別在什么情況下使用? 區別? 各自優點?

關于視圖的生命周期的問題

  • 首先判斷控制器是否有視圖,如果沒有就調用loadView方法創建:通過storyboard或者代碼;

  • 隨后調用viewDidLoad,可以進行下一步的初始化操作;只會被調用一次;

  • 在視圖顯示之前調用viewWillAppear;該函數可以多次調用;

  • 視圖viewDidAppear

  • 在視圖顯示之前調用viewWillDisappear;該函數可以多次調用;如需要);

  • 在布局變化前后,調用viewWill/DidLayoutSubviews處理相關信息;

  • 視圖生命周期圖

  • image
    image

響應者鏈條? (What is responder chain?)

  • 事件響應鏈。包括點擊事件,畫面刷新事件等。在視圖棧內從上至下,或者從下之上傳播. 可以說點事件的分發,傳遞以及處理。具體可以去看下touch事件這塊。

  • 首先解釋響應者鏈的概念

    • UIResponder類,是UIKIT中一個用于處理事件響應的基類。窗口上的所有事件觸發,都由該類響應(即事件處理入口)。所以,窗口上的View及控制器都是派生于該類的,例如UIView、UIViewController等。

    • 調用UIResponder類提供的方法或屬性,我們就可以捕捉到窗口上的所有響應事件,并進行處理。

    • 響應者鏈條是由多個響應者對象連接起來的鏈條,其中響應者對象是能處理事件的對象,所有的View和ViewController都是響應者對象,利用響應者鏈條能讓多個控件處理同一個觸摸事件.

  • 事件傳遞機制

如果當前view不能處理當前事件,那么事件將會沿著響應者鏈(Responder Chain)進行傳遞,知道遇到能處理該事件的響應者(Responsder Object)。
image
image
- 接收事件的initial view如果不能處理該事件并且她不是頂層的View,則事件會往它的父View進行傳遞。

- initial view的父View獲取事件后如果仍不能處理,則繼續往上傳遞,循環這個過程。如果頂層的View還是不能處理這個事件的話,則會將事件傳遞給它們的ViewController,

- 如果ViewController也不能處理,則傳遞給Window(UIWindow),此時Window不能處理的話就將事件傳遞UIApplication,最后如果連Application也不能處理,則廢棄該事件

ViewController的loadView,viewDidLoad,viewDidUnload分別是在什么時候調用的?在自定義ViewController的時候這幾個函數里面應該做什么工作?

  • viewDidLoad在view從nib文件初始化時調用,

  • loadView在controller的view為nil時調用。

  • 此方法在編程實現view時調用, view控制器默認會注冊memory warning notification,當viewcontroller的任何view沒有用的時候,viewDidUnload會被調用,在這里實現將retain的view release,如果是retain的IBOutlet view屬性則不要在這里release,IBOutlet會負責release。

UITableView的重用機制?(或者如何在一個view上顯示多個tableView,tableView要求不同的數據源以及不同的樣式 (要求自定義cell), 如何組織各個tableView的delegate和dataSource?請說說實現思路?)

  • 查看UITableView頭文件,會找到NSMutableArray *visiableCells,和NSMutableArray * reusableTableCells兩個結構。

  • visiableCells內保存當前顯示的cells,reusableTableCells保存可重用的cells。

  • TableView顯示之初,reusableTableCells為空,那么
    [tableView dequeueReusableCellWithIdentifier:CellIdentifier]返回nil。

  • 開始的cell都是通過 [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] 來創建,而且cellForRowAtIndexPath只是調用最大顯示cell數的次數。 比如:有100條數據,iPhone一屏最多顯示10個cell。

  • 程序最開始顯示TableView的情況是:

    • 用[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]創建10次cell,并給cell指定同樣的重用標識(當然,可以為不同顯示類型的cell指定不同的標識)。并且10個cell全部都加入到visiableCells數組,reusableTableCells為空。

    • 向下拖動tableView,當cell1完全移出屏幕,并且cell11(它也是alloc出來的,原因同上)完全顯示出來的時候。cell11加入到visiableCells,cell1移出visiableCells,cell1加入到reusableTableCells。

    • 接著向下拖動tableView,因為reusableTableCells中已經有值,所以,當需要顯示新的cell, cellForRowAtIndexPath再次被調用的時候,[tableView dequeueReusableCellWithIdentifier:CellIdentifier],返回cell1。 cell1加入到visiableCells,cell1 移出reusableTableCells;cell2移出 visiableCells,cell2加入到reusableTableCells。之后再需要顯示的Cell就可 以正常重用了.

    • 注意:配置Cell的時候一定要注意,對取出的重用的cell做重新賦值,不要遺留老數據。

在一個tableView中需要自定義多種樣式的cell(兩種或三種),通常你如何實現,說說思路即可?

  • 同上!

UITableView的性能優化? 滑動的時候有種卡的感覺是為什么?怎么解決?

  • 在使用第三方應用時,卻經常遇到性能上的問題,普遍表現在滾動時比較卡,特別是cell中包含圖片的情況時。

  • 實際上針對性地優化一下就可以解決tableView滑動的時候卡頓的問題:

    • 使用不透明視圖。不透明的視圖可以提高渲染的速度。可以將cell及其子視圖的opaque屬性設為YES(默認值)。

    • 不要重復創建不必要的cell。UITableView只需要一屏幕的UITableViewCell對象即可。因此在cell不可見時,可以將其緩存起來,而在需要時繼續使用它即可。注意:cell被重用時,需要調用setNeedsDisplayInRect:或setNeedsDisplay方法重繪cell。

    • 減少動畫效果的使用,最好不要使用insertRowsAtIndexPaths:withRowAnimation:方法,而是直接調用reloadData方法。

    • 減少視圖的數目。Cell包含了textLabel、detailTextLabel和imageView等view,而你還可以自定義一些視圖放在它的contentView里,創建它會消耗較多資源,并且也影響渲染的性能。

    • cell包含圖片,且數目較多,使用自定義的cell速度會比使用默認的要快。繼承UITableViewCell,重寫drawRect方法:- (void)drawRect:(CGRect)rect { if (image) { [image drawAtPoint:imagePoint]; self.image = nil; } else { [placeHolder drawAtPoint:imagePoint]; } [text drawInRect:textRect withFont:font lineBreakMode:UILineBreakModeTailTruncation]; }
      不過這樣一來,你會發現選中一行后,這個cell就變藍了,其中的內容就被擋住了。最簡單的方法就是將cell的selectionStyle屬性設為UITableViewCellSelectionStyleNone,這樣就不會被高亮了。

    -不需要與用戶交互時,使用CALayer,將內容繪制到layer上,然后對cell的contentView.layer調用addSublayer:方法。這個例子中,layer并不會顯著影響性能,但如果layer透明,或者有圓角、變形等效果,就會影響到繪制速度了。解決辦法可參見后面的預渲染圖像。

    • 不要做多余的繪制工作。在實現drawRect:的時候,它的rect參數就是需要繪制的區域,這個區域之外的不需要進行繪制。

    • 預渲染圖像。你會發現即使做到了上述幾點,當新的圖像出現時,仍然會有短暫的停頓現象。解決的辦法就是在圖形上下文中畫,導出成UIImage對象,然后再繪制到屏幕。(頭像圓角,或者其他變形的時候,用圖形上下文能提高性能。)異步繪制

    • 不要阻塞主線程。tableview在更新數據時,整個界面卡住不動,完全不響應用戶請求。常見的是網絡請求,等待時間長待數秒。

    • 解決方案:使用多線程,讓子線程去執行這些函數或方法。

    • 注意:當下載線程數超過2時,會顯著影響主線程的性能。所以在不需要響應用戶請求時,下載線程數可以增加到5,不建議再加了,以加快下載速度。如果用戶正在交互,應把線程數量控制在2個以內。

    • 提前計算并緩存好高度,因為heightforrowatindexpath調用非常頻繁

    • 選擇正確的數據結構:學會選擇對業務場景最合適的數組結構是寫出高效代碼的基礎。比如,數組: 有序的一組值。使用索引來查詢很快,使用值查詢很慢,插入/刪除很慢。字典: 存儲鍵值對,用鍵來查找比較快。集合: 無序的一組值,用值來查找很快,插入/刪除很快。

    • gzip/zip壓縮:當從服務端下載相關附件時,可以通過gzip/zip壓縮后再下載,使得內存更小,下載速度也更快。

tableview的cell里如何嵌套collection view?

思路同網易新聞類似,用自定義的繼承自UITableViewCell的類,在initWithFrame的構造方法中, 初始化自定義的繼承自UICollectionView的類

下拉和上拉的原理?

  • 以tableView的上拉刷新為例:

    • 為了進行無縫閱讀, 通過tableView的代理方法, willDisplayCell判斷是否是最后一行,

    • 如果是最后一行, 在顯示最后一行的同時, 判斷當前是否存在上拉刷新

    • 如果當前沒有上拉刷新, 就進行加載數據, 啟動小菊花轉啊轉。

  • 以tableView的下拉刷新為例:

    • 判斷當前的上拉刷新視圖是否動畫

    • 如果沒有動畫, 就不是上拉刷新

    • 然后下拉刷新加載數據

    • 加載完畢數據關閉刷新

如何實現cell的動態的行高?

  • 如果希望每條數據顯示自身的行高, 必須設置兩個屬性, 1.預估行高, 2.自定義行高

  • 設置預估行高 tableView.estimatedRowHeight = 200

  • 設置定義行高 tableView.rowHeight = UITableViewAutomaticDimension

  • 如果要讓自定義行高有效, 必須讓容器視圖有一個自下而上的約束

談談webView

  • iOS開發中webview和native code的配合上的一些經驗和技巧。

  • webview與運維成本低,更新幾乎不依賴App的版本;但在交互和性能上與跟native code有很大差距。

  • native code與之對應。

  • HTML5確實給web帶入了一個新時代。這個時代是什么,web app。也就是說,只有脫離native的這個前提,在瀏覽器的環境下,HTML5的意義才能顯現,而我們討論iOS App的時候,HTML5顯然沒什么意義。

  • 不管是用webview還是native code,兩個原則:

    • 用戶體驗不打折

    • 運維成本低

  • 為什么不提開發成本。因為做web開發和iOS開發根本就是兩回事。當然,web開發發展了這么多年,對于某些功能實現是要比native app快。但多數情況,同一個功能,對于iOS開發者和web開發者,用各自擅長的方式開發成本都最低,所以說某個功能開發成本低,往往是一個偽命題。

  • 剛剛說了,webview的優勢在于更新不依賴版本,那么在一款App中,只有會頻繁更新的界面考慮webview才有意義。那么哪些界面會頻繁更新,這就要因App而異了。

  • 首頁。首頁資源可謂必爭之地,內容一天一換是正常現象,一天幾換也不稀奇。而如果僅僅是內容的更換,非要上個webview就顯得有些激進了。而事實上首頁的變化千奇百怪,逢年過節變個臉,特殊情況掛個公告,偶爾還要特批強推一把某個業務,等等。此前,我在設計App首頁的時候,把首頁配置設計的非常復雜。App端要處理n種情況,n各參數,server端要記住n種規則,直到一天,我崩潰了,把首頁完全換成webview,才豁然開朗。

  • 活動頁。做互聯網都知道,活動,是一個最常見的運營手段。特點是,周期短,功能少,但基本不能復用。這些特點都標識了活動不適合做native,要用webview實現。即使有人告訴你說,我的活動是一個長期活動而且形式不變,也不要相信他。因為在第二期,第三期,第四期他會分別加上一些非常詭異,卻有很合理的小變更,而這些變更是你在那個版本根本無法實現的。

  • 試水的新功能。這種界面,往往設計不成熟,需要在運行過程中不斷收集用戶反饋,更新升級,甚至決定去留。所以,只有webview才能hold住如此不穩定的功能。切記在一個功能還沒有確定之前,不要大張旗鼓單位開發native code,要知道,你寫的這些代碼,三天后就要改一遍,而且要發布上線。

  • 富文本內容。這個不用多說了吧,按照HTML的常用標簽做一個webtext可不是小工程。而且富文本的變化太多了,一點無法匹配,都會導致整個界面巨丑。

  • OK,上邊說了我認為最該使用webview的4個界面,分別帶有不同的特點,但webview的交互是個短板,因此webview在一個App中,只能作為界面,不允許在界面中出現動作。而一個webview的界面如何跟native code結合起來呢,我的答案是,超鏈接。在webview上點擊超鏈接,會調用webview delegate的shouldload方法,自這里攔截請求,進行處理。由于webview的鏈接都是URL,因此我建議,把整個App的界面都用URL管理起來。

  • 長相問題,webview很難長成native的view。方案:長不成也要裝成。在一些情況下,禁用webview滾動,使用滾動框架(iScroll不錯)去實現。webview上下留出200pixel的空白背景,y從-200開始。否則大家知道,webview上下會有陰影的背景,不藏起來會很丑。等等,還有很多其他的方法去偽裝webview,是要視情景而用。

  • cell中嵌套webview,在oc中調用js獲取web的高度, CGFloat height = [[self.webView stringByEvaluatingJavaScriptFromString:@"document.body.offsetHeight"] floatValue];在通過webViewDidFinishLoad里面更新行高。

awakeFromNib與viewdidload區別

  • awakefromnib:當.nib文件被加載的時候,會發送一個awakefromnib消息到.nib文件中的每個對象,每個對象都可以定義自己的awakefromnib函數來響應這個消息,執行必要操作。也就是說 通過.nib文件創建view對象執行awakefromnib
  • viewdidload:當view對象被加載到內存就會執行viewdidload,不管是通過nib還是代碼形式,創建對象就會執行viewdidload

layoutSubview何時調用?

  • 初始化init方法時不會觸發
  • 滾動uiscrollview觸發
  • 旋轉屏幕觸發
  • 改變view的值觸發,前提是frame改變了
  • 改變uiview的大小觸發

viewcontroller的didreceivememorywaring在什么時候調用 默認操作是什么

  • 應用程序收到來自系統的內存警告時,調用didreceivememorywaring方法
  • 默認做法:控制器上的view不再窗口上顯示時,調用viewWillUnload,直接銷毀view,并調用viewdidunload

UIWindow和UIView和 CALayer 的聯系和區別?

  • UIView是視圖的基類,UIViewController是視圖控制器的基類,UIResponder是表示一個可以在屏幕上響應觸摸事件的對象;
  • UIwindow是UIView的子類,UIWindow的主要作用:一是提供一個區域來顯示UIView,二是將事件(event)的分發給UIView,一個應用基本上只有一個UIWindow.
    圖層不會直接渲染到屏幕上,UIView是iOS系統中界面元素的基礎,所有的界面元素都是繼承自它。它本身完全是由CoreAnimation來實現的。它真正的繪圖部分,是由一個CALayer類來管理。UIView本身更像是一個CALayer的管理器。一個UIView上可以有n個CALayer,每個layer顯示一種東西,增強UIView的展現能力。
  • 都可以顯示屏幕效果
  • 如果需要用戶交互就要用UIVIew,其可接收觸摸事件(繼承UIResponder),而CALayer不能接收觸摸事件
  • 如果沒有用戶交互可選用CALayer,因為其所在庫較小,占用的資源較少

UIScrollView

  • contentsize 內容視圖的尺寸
  • contentoffset 內容視圖當前位置相對滾動視圖frame的偏移量
  • contentinset 內容視圖相對滾動視圖frame的展示原點

如何實現瀑布流,流水布局

  • 使用UICollectionView
  • 使用自定義的FlowLayout
  • 需要在layoutAttributesForElementsInRect中設置自定義的布局(item的frame)
  • 在 prepareLayout中計算布局
  • 遍歷數據內容,根據索引取出對應的attributes(使用layoutAttributesForCellWithIndexPath),根據九宮格算法設置布局
  • 細節1: 實時布局,重寫shouldInvalidateLayoutForBoundsChange(bounds改變重新布局,scrollview的contentoffset>bounds)
  • 細節2: 計算設置itemsize(保證內容顯示完整,uicollectionview的content size是根據itemize計算的),根據列最大高度/對應列數量求出,最大高度累加得到
  • 細節3: 追加item到最短列,避免底部參差不齊.

UIImage有哪幾種加載方式

  • 二進制 imageWithData
  • Bundle imageWithName
  • 本地路徑 imageWithContentOfFile

描述九宮格算法

  • NSInteger col = x;//定義列數
  • NSInteger index = self.shopsView.subviews.count;//獲取下標
  • CGFloat margin = (self.shopsView.frame.size.width - col*viewW) / (col - 1);//定義間隔
  • CGFloat viewX = (index % col ) * (viewW + margin);
  • CGFloat viewY = (index / col ) * (viewH + 10);

實現圖片輪播圖

  • ScrollView只需要設置三個ImageView即可,并且默認顯示中間的ImageView
  • 根據ScrollView的移動情況,迅速變化三個ImageView中圖片數據
  • ImageView更新完畢后,偷偷把ScrollView拉回到中間的ImageView位置,這樣視覺效果上就實現了無限循環的效果

應用的生命周期

  • -(BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions 告訴代理進程啟動但還沒進入狀態保存
  • -(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 告訴代理啟動基本完成程序準備開始運行
  • -(void)applicationWillResignActive:(UIApplication *)application 當應用程序將要入非活動狀態執行,在此期間,應用程序不接收消息或事件,比如來電話了
  • -(void)applicationDidBecomeActive:(UIApplication *)application 當應用程序入活動狀態執行,這個剛好跟上面那個方法相反
  • -(void)applicationDidEnterBackground:(UIApplication *)application 當程序被推送到后臺的時候調用。所以要設置后臺繼續運行,則在這個函數里面設置即可
  • -(void)applicationWillEnterForeground:(UIApplication *)application 當程序從后臺將要重新回到前臺時候調用,這個剛好跟上面的那個方法相反。
  • -(void)applicationWillTerminate:(UIApplication *)application 當程序將要退出是被調用,通常是用來保存數據和一些退出前的清理工作。

load initialize方法的區別

  • +(void)load;
    • 當類對象被引入項目時, runtime 會向每一個類對象發送 load 消息
    • load 方法會在每一個類甚至分類被引入時僅調用一次,調用的順序:父類優先于子類, 子類優先于分類
    • load 方法不會被類自動繼承
  • +(void)initialize;
    • 也是在第一次使用這個類的時候會調用這個方法

UIScrollView 大概是如何實現的,它是如何捕捉、響應手勢的?

  • 我對UIScrollView的理解是frame就是他的contentSize,bounds就是他的可視范圍,通過改變bounds從而達到讓用戶誤以為在滾動,以下是一個簡單的UIScrollView實現

  • 第二個問題個人理解是解決手勢沖突,對自己添加的手勢進行捕獲和響應

// 讓UIScrollView遵守UIGestureRecognizerDelegate協議,實現這個方法,在這里方法里對添加的手勢進行處理就可以解決沖突

  • (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer

+[UIView animateWithDuration:animations:completion:] 內部大概是如何實現的?

animateWithDuration:這就等于創建一個定時器
animations:這是創建定時器需要實現的SEL
completion:是定時器結束以后的一個回調block

什么時候會發生「隱式動畫」?

  • 當改變CALayer的一個可做動畫的屬性,它并不能立刻在屏幕上體現出來.相反,它是從先前的值平滑過渡到新的值。這一切都是默認的行為,你不需要做額外的操作,這就是隱式動畫

如何把一張大圖縮小為1/4大小的縮略圖?

imgData = UIImageJPEGRepresentation(image, 0.6f)

當TableView的Cell改變時,如何讓這些改變以動畫的形式呈現?

[tableView deselectRowAtIndexPath:indexPath animated:TRUE];
// 重點是這2句代碼實現的功能
[tableView beginUpdates];
[tableView endUpdates];

為什么當 Core Animation 完成時,layer 又會恢復到原先的狀態?

  • 因為這些產生的動畫只是假象,并沒有對layer進行改變.那么為什么會這樣呢,這里要講一下圖層樹里的呈現樹.呈現樹實際上是模型圖層的復制,但是它的屬性值表示了當前外觀效果,動畫的過程實際上只是修改了呈現樹,并沒有對圖層的屬性進行改變,所以在動畫結束以后圖層會恢復到原先狀態

設計一個進度條。

  1. 自定義一個UIView的子類

     //提供一個成員屬性,接收下載進度值
     @property (nonatomic, assign) CGFloat progress;
    
  2. 重寫成員屬性progress的setter

     //每次改變成員屬性progress的值,就會調用它的setter
     -(void)setProgress:(CGFloat)progress
     {
     _progress = progress;
     //當下載進度改變時,手動調用重繪方法
     [self setNeedsDisplay];
     }
    
  3. 重寫

     -(void)drawRect:(CGRect)rect(核心)
     -(void)drawRect:(CGRect)rect
     {
     //設置圓弧的半徑
     CGFloat radius = rect.size.width * 0.5;
     //設置圓弧的圓心
     CGPoint center = CGPointMake(radius, radius);
     //設置圓弧的開始的角度(弧度制)
     CGFloat startAngle = - M_PI_2;
     //設置圓弧的終止角度
     CGFloat endAngle = - M_PI_2 + 2 * M_PI * self.progress;
     //使用UIBezierPath類繪制圓弧
     UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius - 5 startAngle:startAngle endAngle:endAngle clockwise:YES];
     //將繪制的圓弧渲染到圖層上(即顯示出來)
     [path stroke];
     }
    

如何播放 GIF 圖片,有什么優化方案么?

  • UIImageView用來顯示圖片, 使用UIImageView中的動畫數組來實現圖片的動畫效果

  • 用UIWebView來顯示動態圖片

  • 第三方顯示框架

  • 通過UIImageView顯示動畫效果,實際上是把動態的圖拆成了一組靜態的圖,放到數組中,播放的時候依次從數組中取出。如果播放的圖片比較少占得內存比較小或者比較常用(比如工具條上一直顯示的動態小圖標),可以選擇用imageNamed:方式獲取圖片,但是通過這種方式加到內存中,使用結束,不會自己釋放,多次播放動畫會造成內存溢出問題。因此,對于大圖或經常更換的圖,在取圖片的時候可以選擇imageWithContentsOfFile:方式獲取圖片,優化內存。

  • 使用UIWebView顯示圖片需要注意顯示圖片的尺寸與UIWebView尺寸的設置,如果只是為了顯示動態圖片,可以禁止UIWebView滾動。在顯示動態圖片的時候,即使是動圖的背景處為透明,默認顯示出來是白色背景,這個時候需要手動設置UIWebView的透明才能達到顯示動圖背景透明的效果。

有哪幾種方式可以對圖片進行縮放,使用 CoreGraphics 縮放時有什么注意事項?

  • UIImageView整體拉伸
  • UIImage局部拉伸
  • UIImage修改大小
  • images.xcassets:多虧了Xcode中Asset Catalog的slice和dice,我們不需要代碼也能拉伸圖片。首先在Xcode中選中圖片,然后點擊右下角的Show Slicing:
  • 圖形上下文等比例縮放

XIB與Storyboards的優缺點?

- XIB:在編譯前就提供了可視化界面,可以直接拖控件,也可以直接給控件添加約束,更直觀一些,而且類文件中就少了創建控件的代碼,確實簡化不少,通常每個XIB對應一個類。

- Storyboard:在編譯前提供了可視化界面,可拖控件,可加約束,在開發時比較直觀,而且一個storyboard可以有很多的界面,每個界面對應一個類文件,通過storybard,可以直觀地看出整個App的結構。

- XIB:需求變動時,需要修改XIB很大,有時候甚至需要重新添加約束,導致開發周期變長。XIB載入相比純代碼自然要慢一些。對于比較復雜邏輯控制不同狀態下顯示不同內容時,使用XIB是比較困難的。當多人團隊或者多團隊開發時,如果XIB文件被發動,極易導致沖突,而且解決沖突相對要困難很多。

- Storyboard:需求變動時,需要修改storyboard上對應的界面的約束,與XIB一樣可能要重新添加約束,或者添加約束會造成大量的沖突,尤其是多團隊開發。對于復雜邏輯控制不同顯示內容時,比較困難。當多人團隊或者多團隊開發時,大家會同時修改一個storyboard,導致大量沖突,解決起來相當困難。

控制器View的加載過程?

當程序訪問了控制器的View屬性時會先判斷控制器的View是否存在,如果存在就直接返回已經存在的View;
如果不存在,就會先調用loadView這個方法;如果控制器的loadView方法實現了,就會按照loadView方法加載自定義的View;
如果控制器的loadView方法沒有實現就會判斷storyboard是否存在;
如果storyboard存在就會按照storyboard加載控制器的View;如果storyboard不存在,就會創建一個空視圖返回。

應用程序的啟動流程?

1.執行Main
2.執行UIApplicationMain函數.
3.創建UIApplication對象,并設置UIApplicationMain對象的代理.
  UIApplication的第三個參數就是UIApplication的名稱,如果指定為nil,它會默認為UIApplication.
  UIApplication的第四個參數為UIApplication的代理.
4.開啟一個主運行循環.保證應用程序不退出.
5.加載info.plist.加載配置文件.判斷一下info.plist文件當中有沒有Main storyboard file base name里面有沒有指定storyboard文件,如果有就去加載info.plist文件,如果沒有,那么應用程序加載完畢.

事件傳遞與響應的完整過程?

在產生一個事件時,系統會將該事件加入到一個由UIApplication管理的事件隊列中,
UIApplication會從事件隊列中取出最前面的事件,將它傳遞給先發送事件給應用程序的主窗口.
主窗口會調用hitTest方法尋找最適合的視圖控件,找到后就會調用視圖控件的touches方法來做具體的事情.
當調用touches方法,它的默認做法, 就會將事件順著響應者鏈條往上傳遞,
傳遞給上一個響應者,接著就會調用上一個響應者的touches方法

下列回調機制的理解不正確的是

A target-action:當兩個對象之間有?較緊密的關系時,如視圖控制器與其下的某個視圖。       
B delegate:當某個對象收到多個事件,并要求同一個對象來處理所有事件時。委托機制必須依賴于某個協議定義的?法來發送消息。       
C NSNotification:當需要多個對象或兩個無關對象處理同一個事件時。       
D Block:適?于回調只發?生一次的簡單任務。
參考答案:B

給UIImageView添加圓角

  • 最直接的方法就是使用如下屬性設置:
imgView.layer.cornerRadius = 10;
// 這一行代碼是很消耗性能的
imgView.clipsToBounds = YES;
  • 好處是使用簡單,操作方便。壞處是離屏渲染(off-screen-rendering)需要消耗性能。對于圖片比較多的視圖上,不建議使用這種方法來設置圓角。通常來說,計算機系統中CPU、GPU、顯示器是協同工作的。CPU計算好顯示內容提交到GPU,GPU渲染完成后將渲染結果放入幀緩沖區。
  • 簡單來說,離屏渲染,導致本該GPU干的活,結果交給了CPU來干,而CPU又不擅長GPU干的活,于是拖慢了UI層的FPS(數據幀率),并且離屏需要創建新的緩沖區和上下文切換,因此消耗較大的性能。
  • 給UIImage添加生成圓角圖片的擴展API:
- (UIImage *)hyb_imageWithCornerRadius:(CGFloat)radius {
  CGRect rect = (CGRect){0.f, 0.f, self.size};
  
  UIGraphicsBeginImageContextWithOptions(self.size, NO, UIScreen.mainScreen.scale);
  CGContextAddPath(UIGraphicsGetCurrentContext(),
                   [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius].CGPath);
  CGContextClip(UIGraphicsGetCurrentContext());
  
  [self drawInRect:rect];
  UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  
  UIGraphicsEndImageContext();
  
  return image;
}

然后調用時就直接傳一個圓角來處理:

imgView.image = [[UIImage imageNamed:@"test"] hyb_imageWithCornerRadius:4];

這么做就是on-screen-rendering了,通過模擬器->debug->Color Off-screen-rendering看到沒有離屏渲染了!(黃色的小圓角沒有顯示了,說明這個不是離屏渲染了)

  • 在畫之前先通過UIBezierPath添加裁剪,但是這種不實用
- (void)drawRect:(CGRect)rect {
  CGRect bounds = self.bounds;
  [[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:8.0] addClip];
 
  [self.image drawInRect:bounds];
}
  • 通過mask遮罩實現

一個view已經初始化完畢,view上面添加了n個button(可能使用循環創建),除用view的tag之外,還可以采用什么辦法來找到自己想要的button來修改Button的值

這個問題有很多種方式,而且不同的使用場景也不一樣的。比如說:
?   第一種:如果是點擊某個按鈕后,才會刷新它的值,其它不用修改,那么不用引用任何按鈕,直接在回調時,就已經將接收響應的按鈕給傳過來了,直接通過它修改即可。
?   第二種:點擊某個按鈕后,所有與之同類型的按鈕都要修改值,那么可以通過在創建按鈕時將按鈕存入到數組中,在需要的時候遍歷查找。

使用drawRect有什么影響?

  • drawRect方法依賴Core Graphics框架來進行自定義的繪制,但這種方法主要的缺點就是它處理touch事件的方式:每次按鈕被點擊后,都會用setNeddsDisplay進行強制重繪;而且不止一次,每次單點事件觸發兩次執行。這樣的話從性能的角度來說,對CPU和內存來說都是欠佳的。特別是如果在我們的界面上有多個這樣的UIButton實例。

viewWillLayoutSubView你總是知道的。

controller layout觸發的時候,開發者有機會去重新layout自己的各個subview。
橫豎屏切換的時候,系統會響應一些函數,其中 viewWillLayoutSubviews 和 viewDidLayoutSubviews。
- (void)viewWillLayoutSubviews  {       
[self _shouldRotateToOrientation:(UIDeviceOrientation)[UIApplication sharedApplication].statusBarOrientation];  
} 
 -(void)_shouldRotateToOrientation:(UIDeviceOrientation)orientation {         

 if (orientation == UIDeviceOrientationPortrait ||orientation ==                 UIDeviceOrientationPortraitUpsideDown)

  {           // 豎屏 } 

 else {          // 橫屏     } }
通過上述一個函數就知道橫豎屏切換的接口了。 
注意:viewWillLayoutSubviews只能用在ViewController里面,在view里面沒有響應。

一個tableView是否可以關聯兩個不同的數據源?

  • 當然是可以關聯多個不同的數據源,但是不能同時使用多個數據源而已。比如,一個列表有兩個篩選功能,一個是篩選城市,一個是篩選時間,那么這兩個就是兩個數據源了。當篩選城市時,就會使用城市數據源;當篩選時間時,就會使用時間數據源。

如何自動計算cell的高度?

  • 實現原理:通過數據模型的id作為key,以確保唯一,如何才能保證復用cell時不會出現混亂。在配置完數據后,通過更新約束,得到最后一個控件的frame,就只可以判斷cell實際需要的高度,并且緩存下來,下次再獲取時,判斷是否存在,若存在則直接返回。因此,只會計算一遍

UITableView是如何計算內容高度的?為什么初始化時配置數據時,獲取行高的代理方法會調用數據條數次?

  • UITableView是繼承于UIScrollView的,因此也有contentSize。要得到tableview的contentsize,就需要得到所有cell的高度,從而計算出總高度,才能得到contentsize。因此,在reloadData時,就會調用該代理方法數據條數次。

一個tableView是否可以關聯兩個不同的數據源?你會怎么處理?

答:首先我們從代碼來看,數據源如何關聯上的,其實是在數據源關聯的代理方法里實現的。
因此我們并不關心如何去關聯他,他怎么關聯上,方法只是讓我返回根據自己的需要去設置如相關的數據源。
因此,我覺得可以設置多個數據源啊,但是有個問題是,你這是想干嘛呢?想讓列表如何顯示,不同的數據源分區塊顯示?

給出委托方法的實例,并且說出UITableVIew的Data Source方法

CocoaTouch框架中用到了大量委托,其中UITableViewDelegate就是委托機制的典型應用,是一個典型的使用委托來實現適配器模式,其中UITableViewDelegate協議是目標,tableview是適配器,實現UITableViewDelegate協議,并將自身設置為talbeview的delegate的對象,是被適配器,一般情況下該對象是UITableViewController。
UITableVIew的Data Source方法有- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

cocoa touch框架

答:iPhone OS 應用程序的基礎 Cocoa Touch 框架重用了許多 Mac 系統的成熟模式,但是它更多地專注于觸摸的接口和優化。

UIKit 為您提供了在 iPhone OS 上實現圖形,事件驅動程序的基本工具,其建立在和 Mac OS X 中一樣的 Foundation 框架上,包括文件處理,網絡,字符串操作等。

Cocoa Touch 具有和 iPhone 用戶接口一致的特殊設計。有了 UIKit,您可以使用 iPhone OS 上的獨特的圖形接口控件,按鈕,以及全屏視圖的功能,您還可以使用加速儀和多點觸摸手勢來控制您的應用。

各色俱全的框架 除了UIKit 外,Cocoa Touch 包含了創建世界一流 iPhone 應用程序需要的所有框架,從三維圖形,到專業音效,甚至提供設備訪問 API 以控制攝像頭,或通過 GPS 獲知當前位置。

Cocoa Touch 既包含只需要幾行代碼就可以完成全部任務的強大的 Objective-C 框架,也在需要時提供基礎的 C 語言 API 來直接訪問系統。這些框架包括:

Core Animation:通過 Core Animation,您就可以通過一個基于組合獨立圖層的簡單的編程模型來創建豐富的用戶體驗。

Core Audio:Core Audio 是播放,處理和錄制音頻的專業技術,能夠輕松為您的應用程序添加強大的音頻功能。

Core Data:提供了一個面向對象的數據管理解決方案,它易于使用和理解,甚至可處理任何應用或大或小的數據模型。

功能列表:框架分類

下面是 Cocoa Touch 中一小部分可用的框架:

音頻和視頻:Core Audio ,OpenAL ,Media Library ,AV Foundation

數據管理 :Core Data ,SQLite

圖形和動畫 :Core Animation ,OpenGL ES ,Quartz 2D

網絡:Bonjour ,WebKit ,BSD Sockets

用戶應用:Address Book ,Core Location ,Map Kit ,Store Kit

xib文件的構成分為哪3個圖標?都具有什么功能。

File’s Owner 是所有 nib 文件中的每個圖標,它表示從磁盤加載 nib 文件的對象;

First Responder 就是用戶當前正在與之交互的對象;

View 顯示用戶界面;完成用戶交互;是 UIView 類或其子類。

簡述應用程序按Home鍵進入后臺時的生命周期,和從后臺回到前臺時的生命周期? 應用程序:

-[AppDelegate application:willFinishLaunchingWithOptions:]
-[AppDelegate application:didFinishLaunchingWithOptions:]
-[AppDelegate applicationDidBecomeActive:] 

退到后臺:

-[AppDelegate applicationWillResignActive:] 
-[AppDelegate applicationDidEnterBackground:] 

回到前臺:

-[AppDelegate applicationWillEnterForeground:] 
-[AppDelegate applicationDidBecomeActive:] 

ViewController之間,
加載頁面:

-[mainViewController viewDidLoad] -[mainViewController viewWillAppear:] -[mainViewController viewWillLayoutSubviews] -[mainViewController viewDidLayoutSubviews] -[mainViewController viewDidAppear:] 

退出當前頁面:

-[mainViewController viewWillDisappear:]
-[mainViewController viewDidDisappear:]

返回之前頁面:

-[mainViewController viewWillAppear:]
-[mainViewController viewWillLayoutSubviews]
-[mainViewController viewDidLayoutSubviews]
-[mainViewController viewDidAppear:]

是否使用Core Text或者Core Image等?如果使用過,請談談你使用Core Text或者Core Image的體驗。

CoreText
?   隨意修改文本的樣式
?   圖文混排(純C語言)
?   國外:Niumb
Core Image(濾鏡處理)
* 能調節圖片的各種屬性(對比度, 色溫, 色差等)

分析一下使用手機獲取驗證碼注冊賬號的實現邏輯(給了一個示例圖),發送到手機的驗證碼超過60秒鐘后重新發送

  • 定義一個label屬性,賦值為60秒,再定義一個count 設置一個timer 每次減少一秒 把count-- 再把count的值拼接到label上 當count == 0 的時候 再顯示重新發送

你做iphone開發時候,有哪些傳值方式,view和view之間是如何傳值的?

block, target-action ,代理,屬性

有哪幾種手勢通知方法、寫清楚方法名?

-(void)touchesBegan:(NSSet)touchedwithEvent:(UIEvent)event;

-(void)touchesMoved:(NSSet)touched withEvent:(UIEvent)event;

-(void)touchesEnded:(NSSet)touchedwithEvent:(UIEvent)event;

-(void)touchesCanceled:(NSSet)touchedwithEvent:(UIEvent)event;

文章如有問題,請留言,我將及時更正。

滿地打滾賣萌求贊,如果本文幫助到你,輕點下方的紅心,給作者君增加更新的動力。

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

推薦閱讀更多精彩內容

  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,229評論 4 61
  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結起來就是把...
    Dove_iOS閱讀 27,210評論 30 471
  • 曾經在某一瞬間,我們都以為自己長大了。但是有一天,我們終于發現,長大的含義除了欲望,還有勇氣、責任、堅強以及某種必...
    18183b55ea01閱讀 300評論 0 1
  • 第一天發現這個軟件,好久沒寫東西了,希望堅持下來
    昀義閱讀 140評論 0 0
  • 你/ 斬了我的首/ 割去我的臀/ 扒掉我的皮/ 剔下我的骨/ 攪碎我的肉/ 榨干我的血/ 然后/ 還嫌我粘手。
    裸山閱讀 593評論 2 5