【问题标题】:Reload section without reloading section header重新加载节而不重新加载节标题
【发布时间】:2013-12-27 14:47:11
【问题描述】:

我有一个UITableView,标题视图中有一个UISegmentedControl。它应该与 App Store 应用程序中的工作方式完全相同:当用户滚动时,标题中的标题会滚动到屏幕之外,但 segmentedControl 会粘在 navigationBar 下方。

当用户选择一个段时,标题下方的部分应该重新加载一个漂亮的UITableViewRowAnimation。然而,正如我所说的tableView:reloadSections:withRowAnimation:,标题视图也是动画的,我想阻止它,因为它看起来很糟糕。

这是我的代码:

- (void)selectedSegmentIndexChanged:(UISegmentedControl *)sender
{
    int index = sender.selectedSegmentIndex;
    if (index < self.oldIndex) {
        [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationLeft];
    } else if (index > self.oldIndex) {
        [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationRight];
    }
    self.oldIndex = index;
}

有人知道如何在不重新加载标题本身的情况下重新加载标题下方的部分吗?

【问题讨论】:

  • 也许你只会重新加载行...
  • 你是对的,但是你有没有一个解决方案,在重新加载后的单元格数量与之前的数量不同时不会崩溃?
  • 是的,有一个合适的解决方案,它称为–deleteRowsAtIndexPaths:withRowAnimation:–insertRowsAtIndexPaths:withRowAnimation:。您可以在 Apple 的个人圣经中找到更多信息:developer.apple.com/Library/ios/documentation/UIKit/Reference/…
  • 不幸的是,我在这里遇到了同样的错误:*** Assertion failure in -[UITableView _endCellAnimationsWithContext:]...
  • 有很多很好的例子来说明如何正确地做到这一点;或者,如果您阅读了我为您链接的文档,那也将帮助您实现您想要的。使用UITableViews 是 iOS 开发中最复杂的部分之一,如果你发现很难处理它们,我不会怪你。

标签: ios objective-c uitableview


【解决方案1】:

也许你应该试试

[self.tableView reloadRowsAtIndexPaths:[self.tableView indexPathsForVisibleRows] withRowAnimation:UITableViewRowAnimationLeft] //or UITableViewRowAnimationRight

但是,我不确定,但我认为如果要重新加载的行数比以前少,它可能会引发一些错误。


编辑

我认为您可以处理[tableView beginUpdates][tableView endUpdates] 来解决您的问题。

例如,您有 2 个要显示的数据数组。让他们命名为oldArraynewArray。 您可以如何做的示例:

- (void)selectedSegmentIndexChanged:(UISegmentedControl *)sender
{
    [self.tableView setDataSource: newArray];
    int nbRowToDelete = [oldArray count];
    int nbRowToInsert = [newArray count];

    NSMutableArray *indexPathsToInsert = [[NSMutableArray alloc] init];
    for (NSInteger i = 0; i < nbRowToInsert; i++) {
        [indexPathsToInsert addObject:[NSIndexPath indexPathForRow:i inSection:section]];
    }

    NSMutableArray *indexPathsToDelete = [[NSMutableArray alloc] init];
    for (NSInteger i = 0; i < nbRowToDelete; i++) {
        [indexPathsToDelete addObject:[NSIndexPath indexPathForRow:i inSection:section]];
    }

    [self.tableView beginUpdates];
    [self.tableView deleteRowsAtIndexPaths:indexPathsToDelete withRowAnimation:UITableViewRowAnimationLeft];
    [self.tableView insertRowsAtIndexPaths:indexPathsToInsert withRowAnimation:UITableViewRowAnimationRight];
    [self.tableView endUpdates];
}

【讨论】:

  • 我当然试过了,但正如你所说,如果单元格数量不同,它会产生错误:*** Assertion failure in -[UITableView _endCellAnimationsWithContext:]
【解决方案2】:

如果您使用的是 Swift 2.0,请随意使用此扩展程序。

警告:传入错误的oldCountnewCount 会使您的程序崩溃。

extension UITableView{

func reloadRowsInSection(section: Int, oldCount:Int, newCount: Int){

    let maxCount = max(oldCount, newCount)
    let minCount = min(oldCount, newCount)

    var changed = [NSIndexPath]()

    for i in minCount..<maxCount {
        let indexPath = NSIndexPath(forRow: i, inSection: section)
        changed.append(indexPath)
    }

    var reload = [NSIndexPath]()
    for i in 0..<minCount{
        let indexPath = NSIndexPath(forRow: i, inSection: section)
        reload.append(indexPath)
    }

    beginUpdates()
    if(newCount > oldCount){
        insertRowsAtIndexPaths(changed, withRowAnimation: .Fade)
    }else if(oldCount > newCount){
        deleteRowsAtIndexPaths(changed, withRowAnimation: .Fade)
    }
    if(newCount > oldCount || newCount == oldCount){
        reloadRowsAtIndexPaths(reload, withRowAnimation: .None)
    }
    endUpdates()

}

【讨论】:

    【解决方案3】:

    试试这个:

    BOOL needsReloadHeader = YES;
    UIView *oldHeaderView = nil;
    
    -(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
        UIView *headerToReturn = nil;
        if(needsReloadHeader == YES) {
            headerToReturn = [[UIView alloc] init];
            // ...
            // custom your header view in this block
            // and save
            // ...
            oldHeaderView = headerToReturn;
        } else {
            headerToReturn = oldHeaderView;
        }
        return headerToReturn;
    }
    

    您只需在其他地方将“needsReloadHeader”更改为“NO”即可。

    【讨论】:

    • 我在做或多或少相同的事情。问题是调用tableView:reloadSections:withRowAnimation: 时标题仍在动画中。以UITableViewRowAnimationLeft 为例,标题向左滑出,然后再次从右侧滑入。
    • 抱歉,我对此一无所知:) ...也许您可以尝试实现您的自定义“重新加载”方法。
    【解决方案4】:

    Intentss 扩展的 Objective-c 版本

    @interface UITableView (Extensions)
    
    - (void)reloadRowsInSection:(NSUInteger)sectionIndex withRowAnimation:(UITableViewRowAnimation)rowAnimation oldCount:(NSUInteger)oldCount newCount:(NSUInteger)newCount;
    
    @end
    
    
    @implementation UITableView (Extensions)
    
    - (void)reloadRowsInSection:(NSUInteger)sectionIndex withRowAnimation:(UITableViewRowAnimation)rowAnimation oldCount:(NSUInteger)oldCount newCount:(NSUInteger)newCount {
    
        NSUInteger minCount = MIN(oldCount, newCount);
    
        NSMutableArray *insert = [NSMutableArray array];
        NSMutableArray *delete = [NSMutableArray array];
        NSMutableArray *reload = [NSMutableArray array];
    
        for (NSUInteger row = oldCount; row < newCount; row++) {
            [insert addObject:[NSIndexPath indexPathForRow:row inSection:sectionIndex]];
        }
    
        for (NSUInteger row = newCount; row < oldCount; row++) {
            [delete addObject:[NSIndexPath indexPathForRow:row inSection:sectionIndex]];
        }
    
        for (NSUInteger row = 0; row < minCount; row++) {
            [reload addObject:[NSIndexPath indexPathForRow:row inSection:sectionIndex]];
        }
    
        [self beginUpdates];
    
        [self insertRowsAtIndexPaths:insert withRowAnimation:rowAnimation];
        [self deleteRowsAtIndexPaths:delete withRowAnimation:rowAnimation];
        [self reloadRowsAtIndexPaths:reload withRowAnimation:rowAnimation];
    
        [self endUpdates];
    }
    
    @end
    

    【讨论】:

    • 您可以使用[self numberOfRowsInSection:sectionIndex] 在方法内部获取oldCount 变量,而不是将其作为参数传递。
    【解决方案5】:

    您正在重新加载该部分,因此显然该部分中的所有内容都将被重新加载(包括标题)。

    为什么不把 UISegmentedControl 放在 UITableView 的tableHeaderView 中呢?这将允许您完全遵循您所追求的行为。

    【讨论】:

    • 这可能会完美运行。但是,我希望像 App Store 应用程序中的行为,当用户滚动时,顶部的标题会滚动,但 segmentedControl 会粘在 navigationBar 的正下方。因此,我将 title 和 segmentedControl 分成了两个不同的标题。所以我想这不再适用于你的方法......无论如何谢谢:)
    • 那你不妨用 UICollectionView 创建一个自定义布局。您可以使用自定义 UICollectionViewLayouts 轻松拉出粘性标题。您还应该在问题中提及您的其他要求。
    【解决方案6】:

    简单的答案就是不要重新加载动画部分,只需使用 UITableViewRowAnimationNone。

    现在您正在使用 UITableViewRowAnimationLeft 和 UITableViewRowAnimationRight,它们也会将您的部分滑入和滑出。

    但是,即使使用 UITableViewRowAnimationNone,如果更新前的单元格数与更新后的单元格数不同,行仍然会被动画化。

    另外,关于这个主题的好书,here

    干杯。

    【讨论】:

    • 这个问题的重点是保留动画。删除它们只是一个简单的方法。
    【解决方案7】:

    这是您可以使用并且仍然使用动画的另一种方式。

    假设您有一个动态数据源,当您选择某些内容时它会发生变化,并且您只想更新该部分的行,而将部分标题保持在顶部,不受影响。

    /** I get the desired handler from the handler collection. This handler is just a
     simple NSObject subclass subscribed to UITableViewDelegate and UITableViewDataSource
     protocols. **/
    id handler = [self.tableViewHandlers objectForKey:[NSNumber numberWithInteger:index]];
    
    /**  Get the rows which will be deleted  */
    NSInteger numberOfRows = [self.tableView numberOfRowsInSection:sectionIndex];
    NSMutableArray* indexPathArray = [NSMutableArray array];
    
    for (int rowIndex = 0; rowIndex < numberOfRows; rowIndex++){
        [indexPathArray addObject:[NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]];
    }
    
    /**  Update the handler  */
    [self.tableView setDataSource:handler];
    [self.tableView setDelegate:handler];
    
    /**  Get the rows which will be added  */
    NSInteger newNumberOfRows = [handler tableView:self.tableView numberOfRowsInSection:sectionIndex];
    NSMutableArray* newIndexPathArray = [NSMutableArray array];
    
    for (int rowIndex = 0; rowIndex < newNumberOfRows; rowIndex++){
        [newIndexPathArray addObject:[NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]];
    }
    
    /**  Perform updates  */
    [self.tableView beginUpdates];
    [self.tableView deleteRowsAtIndexPaths:indexPathArray withRowAnimation:UITableViewRowAnimationFade];
    [self.tableView insertRowsAtIndexPaths:newIndexPathArray withRowAnimation:UITableViewRowAnimationFade];
    [self.tableView endUpdates];
    

    请注意,请遵守指定的操作顺序,UITableView 要求。 如果您只有一个处理程序(数据源和委托),则很容易修改上述代码以获得相同的结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-04-03
      • 1970-01-01
      • 2011-08-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多