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
,其中包含一个名为MXSAnnouncementsViewController
的UITableView
,并且在横向和纵向场景中都存在相同的视图控制器。我没有创建该视图控制器的显式纵向或横向版本,而是让控制器跟踪两个指向特定方向的IBOutlet
属性(tableViewLandscape
和tableViewPortrait
)@ 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 不调用选择器