iOS UI狀態保存和恢復(三)

級別: ★★☆☆☆
標簽:「iOS」「UIStateRestoration」
作者: 沐靈洛
審校: QiShare團隊


前面兩篇我們介紹了UI狀態保存和恢復的流程,UIStateRestoration協議類的方法,適用場景,調試策略,UIApplication,UIViewController,UIView關于UIStateRestoration協議所提供的接口方法以及如何實現UI狀態保存和恢復。本篇我們將介紹UIStateRestoration協議類中的UIDataSourceModelAssociation協議。

關于UIDataSourceModelAssociation協議

引用官網的解釋

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.
//你的數據源對象可以實現這個協議,在狀態恢復的過程中去支持相關的table or collection view;這些實現了該協議的類,使用這個協議的方法去保證相同的數據對象,(而不僅僅是相同的行的索引)被滾動到視圖并且被選中。
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.
//在你實現這個協議之前,你的App必須能夠在App啟動之間,一直(總是可以)辨別出數據源對象。這就要求對象能夠有一些辨認標識,并且可以把標識轉換為當App狀態不活躍時能夠被存儲的字符串;
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 類 支持這個協議。你將可以實現這個協議在任何你用來作為UITableView 和 UICollectionView數據源的對象中,如果在你的數據源對象中不實現這個協議,那么視圖將不會試著去恢復選中的和可見rows;

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

  • 只有 UITableViewUICollectionView類支持這個協議。
  • 我們的數據源中的每個數據對象(model)必須具備唯一辨認標識。
  • 使用這個協議的方法去保證相同的數據對象,(而不僅僅是相同的行的索引)被滾動到視圖并且被選中。舉個場景的例子:TableView的數據源對象在上次保存時,所保存的行的索引,可能會因為在當前運行周期內數據源中數據的變動發生變化。從而導致當前選中的行所對應的數據并非上次保存時的數據。
  • 若需要使用UIDataSourceModelAssociation,則:實現了UITableView 和 UICollectionView數據源協議的對象,負責實現這個協議的方法,否則不會生效。實際操作發現確實如此。

除了官網解釋,在實際操作中發現還需要設置UITableView 或UICollectionView的restorationIdentifier,否則UIDataSourceModelAssociation協議方法不會被調用。關于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設置了restorationIdentifier,進行UI的保存時,tableView會自動存儲當前選中和可見行的索引。補充:還會存儲滾動偏移,并可以恢復。

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 {
    //根據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追蹤當前選中的cell.會多保存一個cell,目前尚未有答案。
    return identifier;
}
//此方法 恢復時調用
- (NSIndexPath *)indexPathForElementWithModelIdentifier:(NSString *)identifier inView:(UIView *)view {
    //根據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(@"當前選中的數據源對象標識是:%@,對象抬頭是:%@",[self.dataSource[indexPath.row] identifier],[self.dataSource[indexPath.row] title]);
    }

    return indexPath;
}

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


推薦文章:
iOS UI狀態保存和恢復(二)
iOS UI狀態保存和恢復(一)
Swift 運算符
iOS 中精確定時的常用方法
Sign In With Apple(一)
算法小專欄:動態規劃(一)
Dart基礎(一)
Dart基礎(二)
Dart基礎(三)
Dart基礎(四)

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

推薦閱讀更多精彩內容