UI狀態(tài)保存和恢復(fù)(三)

UI狀態(tài)保存和恢復(fù)(三)

前面兩篇我們介紹了UI狀態(tài)保存和恢復(fù)的流程,UIStateRestoration協(xié)議類的方法,適用場(chǎng)景,調(diào)試策略,UIApplication,UIViewController,UIView關(guān)于UIStateRestoration協(xié)議所提供的接口方法以及如何實(shí)現(xiàn)UI狀態(tài)保存和恢復(fù)。本篇我們將介紹UIStateRestoration協(xié)議類中的UIDataSourceModelAssociation協(xié)議。

關(guān)于UIDataSourceModelAssociation協(xié)議

引用官網(wǎng)的解釋

Your data source objects can adopt this protocol to assist a corresponding table or collection view during the state restoration process. Those classes use the methods of this protocol to ensure that the same data objects (and not just the same row indexes) are scrolled into view and selected.
//你的數(shù)據(jù)源對(duì)象可以實(shí)現(xiàn)這個(gè)協(xié)議,在狀態(tài)恢復(fù)的過(guò)程中去支持相關(guān)的table or collection view;這些實(shí)現(xiàn)了該協(xié)議的類,使用這個(gè)協(xié)議的方法去保證相同的數(shù)據(jù)對(duì)象,(而不僅僅是相同的行的索引)被滾動(dòng)到視圖并且被選中。
Before you can implement this protocol, your app must be able to identify data objects consistently between app launches. This requires being able to take some identifying marker of the object and convert that marker into a string that can then be saved with the rest of the app state. For example, a Core Data app could convert a managed object’s ID into a URI that it could then convert into a string.
//在你實(shí)現(xiàn)這個(gè)協(xié)議之前,你的App必須能夠在App啟動(dòng)之間,一直(總是可以)辨別出數(shù)據(jù)源對(duì)象。這就要求對(duì)象能夠有一些辨認(rèn)標(biāo)識(shí),并且可以把標(biāo)識(shí)轉(zhuǎn)換為當(dāng)App狀態(tài)不活躍時(shí)能夠被存儲(chǔ)的字符串;
Currently, only the UITableView and UICollectionView classes support this protocol. You would implement this protocol in any objects you use as the data source for those classes. If you do not adopt the protocol in your data source, the views do not attempt to restore the selected and visible rows.
//目前,只有 UITableView 和 UICollectionView 類 支持這個(gè)協(xié)議。你將可以實(shí)現(xiàn)這個(gè)協(xié)議在任何你用來(lái)作為UITableView 和 UICollectionView數(shù)據(jù)源的對(duì)象中,如果在你的數(shù)據(jù)源對(duì)象中不實(shí)現(xiàn)這個(gè)協(xié)議,那么視圖將不會(huì)試著去恢復(fù)選中的和可見rows;

我們可以獲取到的主要信息有:

  • 只有 UITableViewUICollectionView類支持這個(gè)協(xié)議。
  • 我們的數(shù)據(jù)源中的每個(gè)數(shù)據(jù)對(duì)象(model)必須具備唯一辨認(rèn)標(biāo)識(shí)。
  • 使用這個(gè)協(xié)議的方法去保證相同的數(shù)據(jù)對(duì)象,(而不僅僅是相同的行的索引)被滾動(dòng)到視圖并且被選中。舉個(gè)場(chǎng)景的例子:TableView的數(shù)據(jù)源對(duì)象在上次保存時(shí),所保存的行的索引,可能會(huì)因?yàn)樵诋?dāng)前運(yùn)行周期內(nèi)數(shù)據(jù)源中數(shù)據(jù)的變動(dòng)發(fā)生變化。從而導(dǎo)致當(dāng)前選中的行所對(duì)應(yīng)的數(shù)據(jù)并非上次保存時(shí)的數(shù)據(jù)。
  • 若需要使用UIDataSourceModelAssociation,則:實(shí)現(xiàn)了UITableView 和 UICollectionView數(shù)據(jù)源協(xié)議的對(duì)象,負(fù)責(zé)實(shí)現(xiàn)這個(gè)協(xié)議的方法,否則不會(huì)生效。實(shí)際操作發(fā)現(xiàn)確實(shí)如此。

除了官網(wǎng)解釋,在實(shí)際操作中發(fā)現(xiàn)還需要設(shè)置UITableView 或UICollectionView的restorationIdentifier,否則UIDataSourceModelAssociation協(xié)議方法不會(huì)被調(diào)用。關(guān)于UITableView的restorationIdentifier查閱官方文檔如下:

To save and restore the table’s data, assign a nonempty value to the table view’s restorationIdentifier property. When its parent view controller is saved, the table view automatically saves the index paths for the currently selected and visible rows. If the table’s data source object adopts the UIDataSourceModelAssociation protocol, the table stores the unique IDs that you provide for those items instead of their index paths.

UITableView設(shè)置了restorationIdentifier,進(jìn)行UI的保存時(shí),tableView會(huì)自動(dòng)存儲(chǔ)當(dāng)前選中和可見行的索引。補(bǔ)充:還會(huì)存儲(chǔ)滾動(dòng)偏移,并可以恢復(fù)。

UIDataSourceModelAssociation使用
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    InfoModel *model = [self.dataSource objectAtIndex:indexPath.row];
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass(UITableViewCell.class) forIndexPath:indexPath];
    cell.textLabel.text = model.title;
    cell.restorationIdentifier = model.identifier;
    
    return cell;
}
- (NSString *)modelIdentifierForElementAtIndexPath:(NSIndexPath *)idx inView:(UIView *)view {
    //根據(jù)index 返回identifier
    NSString *identifier = nil;
    InfoModel *model = [self.dataSource objectAtIndex:idx.row];
    
    /*
     注釋①
     if (idx && view) {
       identifier = model.identifier;
    }
    */
    if (idx.row == _currentPath.row && view) {
        identifier = model.identifier;
    }
    //若是不定義_currentPath追蹤當(dāng)前選中的cell.會(huì)多保存一個(gè)cell,目前尚未有答案。
    return identifier;
}
//此方法 恢復(fù)時(shí)調(diào)用
- (NSIndexPath *)indexPathForElementWithModelIdentifier:(NSString *)identifier inView:(UIView *)view {
    //根據(jù)identifier 返回index;
    NSIndexPath *indexPath = nil;
    if (identifier && view) {
        __block NSInteger row = 0;
        [self.dataSource enumerateObjectsUsingBlock:^(InfoModel *obj, NSUInteger idx, BOOL * _Nonnull stop) {
            if ([obj.identifier isEqualToString:identifier]) {
                row = idx;
                *stop = YES;
            }
        }];
        indexPath = [NSIndexPath indexPathForRow:row inSection:0];
        _currentPath = indexPath;
        NSLog(@"當(dāng)前選中的數(shù)據(jù)源對(duì)象標(biāo)識(shí)是:%@,對(duì)象抬頭是:%@",[self.dataSource[indexPath.row] identifier],[self.dataSource[indexPath.row] title]);
    }

    return indexPath;
}

上述代碼方法-(NSString *)modelIdentifierForElementAtIndexPath:(NSIndexPath *)idx inView:(UIView *)view中注釋①描述:此方法會(huì)在保存時(shí)調(diào)用兩次,idx所返回的數(shù)據(jù)除了我們選中的行,還會(huì)返回一個(gè)其他行。 若是采用這種方式映射唯一標(biāo)識(shí),會(huì)出現(xiàn)保存了我們不需要的行的標(biāo)識(shí),導(dǎo)致恢復(fù)滑動(dòng)位置失效,針對(duì)此問(wèn)題目前筆者尚未有答案,查閱資料發(fā)現(xiàn)這個(gè)問(wèn)題曾經(jīng)是蘋果的一個(gè)BUG,若是大家知道具體原因,歡迎評(píng)論和補(bǔ)充。目前在此基礎(chǔ)上筆者自己想的解決辦法:定義_currentPath追蹤當(dāng)前選中的cell,保存時(shí)根據(jù)_currentPath保存我們需要的標(biāo)識(shí),測(cè)試中發(fā)現(xiàn)可以解決問(wèn)題。
QIRestorationDemo地址

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容