UITableView 内部的 UIRefreshControl 导致应用程序在旋转时冻结 - iOS 6+

Posted

技术标签:

【中文标题】UITableView 内部的 UIRefreshControl 导致应用程序在旋转时冻结 - iOS 6+【英文标题】:UIRefreshControl Inside of UITableView Causing App to Freeze on Rotation - iOS 6+ 【发布时间】:2014-01-02 18:19:02 【问题描述】:

Ok *** 人...我有一个非常有趣的问题,我已经尝试解决了好几天但无法弄清楚,所以我需要一些重要的帮助。这很可能是一个非常冗长的描述,但请耐心等待,并提前深深感谢您阅读所有这些,因为我拥有的单词越多,我就越能清楚地向大家描述全貌。我将尽我所能做到尽可能简洁和连贯。请让我知道我的不足之处。


这是我的问题的背景:我正在为我的 ios 应用程序使用 Storyboard,而对于我的应用程序中的特定导航选项卡,我必须为纵向和横向方向创建两个单独的场景。这样做的原因(而不是说,使用自动布局)是因为在这个选项卡中,有一些视觉元素(表格视图、Web 视图等),它们的布局根据方向而不同,而且更容易创建一个单独的方向场景来处理 UI 中的这种变化,而不是以编程方式进行 - (它也更容易理解和更清晰的代码)。因此,从所有这些中要记住的一点是,这两个独立的纵向和横向场景代表了我的应用程序中的 SAME TAB。 (旁注:这些场景当然是在 IB 中制作的)

现在是我之前在 UI 中提到的视觉元素——再深入一点,它们都是不同 UIViewControllers 的容器。我对应用程序中的所有内容进行了沙盒处理,并且几乎所有内容都具有一对一的关系,因此这些容器将映射到我为它们的特定目的创建的子类 UIViewControllers - 但这是第一个警告我的问题出现了。这是一个更清晰图片的实际示例,我有一个UIViewController,其中包含一个名为MXSAnnouncementsViewControllerUITableView,并且在横向和纵向场景中都存在相同的视图控制器。我没有创建该视图控制器的显式纵向或横向版本,而是让控制器跟踪两个指向特定方向的IBOutlet 属性(tableViewLandscapetableViewPortrait)@ 987654329@ - 和这种方法非常有效。此外,在我的MXSAnnouncementsViewController 中,我有一个名为tableView 的本地属性,它抽象了特定于方向的表视图。它被设置在viewDidLoad 中,您可以在下面看到:

- (void)viewDidLoad

    [super viewDidLoad];

    if (self.tableViewPortrait) 
        self.tableView = self.tableViewPortrait;
     else 
        self.tableView = self.tableViewLandscape;
    

    [self.tableView setDelegate:self];
    [self.tableView setDataSource:self];

    if (![MXSAnnouncementManager sharedAnnouncementManager].latestAnnouncements) 
        [MXSAnnouncementManager loadModel:@"MXSAnnouncementGroupAllAnnouncements" withBlock:^(id model, NSError *error) 
            if (!error) 
                self.arrayLatestAnnouncements = [MXSAnnouncementManager sharedAnnouncementManager].latestAnnouncements;
                [self.tableView reloadData];
             else 
                // show some error msg
            
        ];
     else 
        self.arrayLatestAnnouncements = [MXSAnnouncementManager sharedAnnouncementManager].latestAnnouncements;
    

    [self setupPullToRefresh];

每当我在选项卡中时,两个特定方向的IBOutlets 之一始终处于活动状态,并且在内存中有一个地址,而另一个是nil。每当我轮换时,角色就会反转——以前内存中的地址现在是nil,而另一个已经初始化和分配,这就是为什么我使用上面sn-p 中的tableView 属性所做的事情。这就是警告 #2 出现的地方,它是一个笨拙的 - 它与视图生命周期有关。为了清楚起见,这是一个实际示例:假设我以横向加载应用程序。当我这样做时,我的tableViewLandscape 插座在内存中有一个地址,我的tableViewPortrait 插座是nil。这是预期和期望的行为。现在,当我旋转应用程序时,疯狂的事情就开始了。这里有一个地方我需要你们所有人澄清UIViewControllers 的实例以及正常与不正常的情况,请非常缓慢而仔细地阅读以下内容。

旋转应用程序会立即导致相反方向的场景(MXSAnnouncementsViewController???的另一个实例)调用其 viewDidLoad 方法(在此示例中,我们处于横向,因此纵向场景调用该方法)。在该方法中,我的本地 tableView 属性被设置为该方向的当前活动表视图(参见上面的 sn-p)。当该方法完成时,MXSAnnouncementsViewController 的前一个 LANDSCAPE 实例调用其viewWillDisappear 方法,然后是 PORTRAIT 实例调用其viewWillAppear 方法,最后以 LANDSCAPE 实例调用其willRotateToInterfaceOrientation 回调结束——这就是我从断点看到的操作顺序。我真的希望你能得到这一切,因为我的思绪被这一切炸飞了。

如果此时你还在我身边,谢谢你,因为我们终于到了最后阶段。正如这篇文章的标题所暗示的,我要解决的问题是我的应用程序在轮换时冻结。如果你没有注意到viewDidLoad sn-p,最后一条要执行的指令是setupPullToRefresh 方法,如下所示:

- (void)setupPullToRefresh

    UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
    [refreshControl addTarget:self action:@selector(refreshTableView:) forControlEvents:UIControlEventValueChanged];
    [self.tableView addSubview:refreshControl];

由于我之前已经解释了轮换操作的整个视图生命周期顺序,长话短说,如果我在 viewDidLoad 末尾为 MXSAnnouncementsViewController 注释掉最后一条 setupPullToRefresh 指令,我的应用程序工作正常。如果我包含该指令,我的应用程序在第一次旋转时完全没有响应,我终生无法弄清楚原因。不确定我是否在这里处理边缘情况或其他情况。欢迎任何和所有见解,非常感谢您阅读所有这些!

【问题讨论】:

应用挂起时在做什么,主线程? 我还不是 iOS 开发和调试领域的专业人士,但我只是尝试使用 Instruments,它看起来不像主线程在做任何事情。我确实看到了 但我不确定这意味着什么。我当然可以通过一点方向更深入地挖掘,但是,我就像地面上的一块石头一样有用。 有时使用 || 暂停应用时会有线索并查看主线程的堆栈。 Unpauzing-pauzing-again 几次当然可能会给出不同的堆栈结果。 【参考方案1】:

您最好的方法可能是放弃当前的设计,即拥有两个独立的纵向和横向控制器。在 iOS 上,您应该始终按照您想要的方向重新布置视图,而不是破坏和重新创建所有内容。试图通过重新创建所有内容来处理它,我认为你只会让自己陷入困境。

如果您很了解,您可以使用自动布局在旋转时对视图进行复杂的重新排序,但最好的办法可能是废弃当前的代码来做横向,并编写代码来简单地在旋转时自己重新排列视图。以后您遇到的问题会少得多,而且您的代码也将更容易被其他人理解和维护。

当您删除那一段代码时,您的应用可能看起来运行良好,但可能在幕后发生了一些不太正确的事情,将来可能会再次困扰您。这可能就是添加代码行会破坏它的原因。

【讨论】:

【参考方案2】:

尝试旋转后添加

-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
    [self setupPullToRefresh];

如果这样做没有帮助,请仅创建一次 UIRefreshControl 并将其设置为旋转时的正确表。

如果这也没有帮助,请按照第一个给出的答案(@Gavin 的答案)并在 viewDidLoad 上仅创建一个表并在 -(void)viewWillLayoutSubviews 中重新布局内容

【讨论】:

以上是关于UITableView 内部的 UIRefreshControl 导致应用程序在旋转时冻结 - iOS 6+的主要内容,如果未能解决你的问题,请参考以下文章

拖动时删除 UITableView 和 UIRefreshControl 之间的空白

UIRefresh 控件 endRefreshing 不起作用

没有 UITableViewController 的 UIRefreshControl 不调用选择器

如何在UITableViewCell内部更新UITableView的高度约束

UITableView(VC内部),所有设备尺寸的限制?

未调用 UICollectionView 内部 UITableView 的委托方法