从 iOS 11 搜索控制器导航时不需要的 UITableView 重新加载动画

Posted

技术标签:

【中文标题】从 iOS 11 搜索控制器导航时不需要的 UITableView 重新加载动画【英文标题】:Unwanted UITableView reload animation when navigating from iOS 11 search controller 【发布时间】:2018-03-08 16:16:00 【问题描述】:

我有一个带有包含聊天列表的 tableview 的视图控制器,一个嵌入在导航项中的搜索控制器(ios 11 功能)

let searchController = UISearchController(searchResultsController: nil)
searchController.dimsBackgroundDuringPresentation = false
navigationItem.searchController = searchController
definesPresentationContext = true

当用户在表格视图中点击聊天时,应用会推送一个新的视图控制器,其中包含另一个表格视图,其中包含该聊天的消息。这就像它应该的那样工作:

问题是当用户激活搜索控制器,找到一些聊天并点击它时,包含聊天消息的表视图的推送视图控制器会执行一些非常奇怪的不应该发生的表视图动画:

我在实际导航之前加载数据并将其绑定到viewDidLoad 中的表格视图,仅使用表格视图上的reload()。有问题的表格视图使用自动布局和自定义单元格。

问题与UITableView has unwanted animation when reloadData is called 非常相似,但对我而言,它仅在 iOS 11 搜索控制器处于活动状态时才会发生。

编辑:如果我删除tableView.rowHeight = UITableViewAutomaticDimension 并使用func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat 使用固定高度,问题仍然存在

【问题讨论】:

我不确定,但如果您在 performWithoutAnimation 块中重新加载 tableview 会有所帮助。 UIView.performWithoutAnimation self.tableView.reloadData() 我试过了,没有帮助。在 viewDidLoad 中调用了 reloadData() 所以我认为 tableview 是在显示 VC 之前加载的,所以动画与 reload 无关。 这看起来像您的视图控制器在推送动画期间执行布局传递。我建议确保在呈现视图控制器之前,您的视图及其所有子视图都处于最终状态。 您是在 Interface builder 中还是通过代码进行自动布局?您可以发布您的自动布局代码/逻辑的摘录吗? 【参考方案1】:

如果您只是在推送新的 viewController 之前隐藏 searchBar,那么它可能会解决您的问题。

您需要为 searchBarCancelButton 创建一个全局变量,并在搜索时从其子视图中找到取消按钮

let buttons = searchController.searchBar.subviews.first?.subviews.filter  (view) -> Bool in
            return NSStringFromClass(view.classForCoder) == "UINavigationButton"
         as? [UIButton]
        searchBarCancelButton = buttons?.first

然后你可以手动取消它。

self.searchBarCancelButton?.sendActions(for: .touchUpInside)

【讨论】:

【参考方案2】:

就个人而言,我会在展示新的视图控制器之前简单地隐藏 searchView 控制器。 (例如使用 UIView.animates 和完成处理程序)

我不会尝试进一步调查,因为从 iOS11 开始,安全区域管理存在一个深奥的问题。一个错误? :)

即使是启动屏幕布局也没有得到正确处理。 这么多专业的徽标在发布时错过了它们的中间部分!

【讨论】:

我试过了,在导航到聊天详细信息之前隐藏了搜索。这对用户来说速度很慢,而且要让它变得活跃和集中也不是那么容易,因为键盘显示导航回聊天屏幕。 反过来呢?在显示细节控制器之前显示导航栏(没有延迟 - 同时)。 在最近的一个项目中,我在导航栏中嵌入了一个搜索字段。它可能会解决这个动画问题。但是,我同意它会改变您的界面。我要去很远 ;) 干杯,祝你好运。 我并没有真正找到解决方案,但是您建议在导航之前停用搜索并在返回时再次激活它是最好的方法,因此赏金归您所有 【参考方案3】:

您可以尝试在出列并设置单元格内容后立即调用cell.layoutIfNeeded()

【讨论】:

不知道有什么帮助,但我试过了,但没有。 它可能会有所帮助,因为它在将单元格出列和布置单元格子视图之后动画化了 frame.height 之间的差异。例如,如果您设置显式单元格高度,它将停止为单元格高度设置动画 如果我使用heightForRowAt 设置单元格高度,问题仍然存在,仍然看到不需要的动画 您可以在我的 repo github.com/blob8129/Search 中检查具有非常相似功能的非常小的测试项目。我无法重现不需要的动画效果。也许你能发现其中的不同。 你也可以尝试在 UIView.performWithoutAnimation 块中配置你的单元格。【参考方案4】:

iOS 11 彻底修改了安全区域 API,包括滚动视图插入调整行为,如果忽略这些行为可能会导致不需要的动画。因此,请禁用带有不需要的动画的滚动视图的自动内容插入调整:

if #available(iOS 11.0, *) 
    tableView.contentInsetAdjustmentBehavior = .never
 else 
    // < iOS 11 logic

【讨论】:

【参考方案5】:

不要在viewDidLoadviewWillAppear 中调用reloadData() 方法。而不是用viewDidLoad 中的空数据重新加载您的tableView,这样您的tableView 将不显示任何内容,然后在您的viewDidAppear 中调用reloadData() 方法来加载您的所有聊天记录。这将限制您的 tableView 加载不需要的动画。

var shouldShowEmpty = true

func viewDidLoad() 
    super.viewDidLoad()
    tableView.reloadData()

func viewDidAppear(_ animated: Bool) 
    super.viewDidAppear(animated)
    shouldShowEmpty = false
    tableView.reloadData()


func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        if shouldShowEmpty 
            return 0
        
        return yourArray.count
    

【讨论】:

这将导致数据显示“太晚”,对用户可见 你应该手动触发searchController的取消按钮。因为它关闭了导航栏,所以只有 resigningFirstResponder 是不够的。 我试过了,在导航到聊天详细信息之前隐藏了搜索。这对用户来说速度很慢,而且要让它变得活跃和集中也不是那么容易,因为键盘显示导航回聊天屏幕。 如果您将 nib 用于单元格,请在您的单元格类的 awakeFromNib 方法中写入 self.layoutIfNeeded()【参考方案6】:

根据附件,看起来由于自动布局,单元格的元素高度为零(UILabel or UIImageView),然后在重新加载表格视图时突然获取单元格内的数据,从而增加其高度(自动尺寸或其他)导致这个动画。

尝试在没有动画的情况下推送它或尝试设置固定单元格元素的高度和宽度并检查它是否仍然显示此动画。

您是否在不调用 UISearchBar 的情况下进行了检查,如果您在任何单元格上选择相同的动画?还是您尝试删除 UISearchbar 并在单元格上选择并检查动画部分?

请分享您的代码,以便我们看得更清楚。

【讨论】:

【参考方案7】:

我也遇到过类似的问题,我相信解决方案是一样的。键盘导致问题,更正确的是,keyboardWillHideNotification 触发器。您在底部有文本字段,它可能会监听键盘显示/隐藏的通知,如果您为底部约束设置动画以使键盘不与文本字段重叠,则会触发 layoutIfNeeded()。因此,当您在搜索文本字段中完成搜索时,keyboardWillHideNotification 会在不需要的时间触发。我通过调用解决了我的问题:

resignFirstResponder()

用于导致此事件的文本字段。在我的情况下,那是在按下按钮之后,在你的,我相信它在搜索表视图中的 didSelect tableView 单元格中。

如果您仍然在处理这个问题,请告诉我您是否设法解决它。我试图解决这个问题,这显然是如此简单明了。

【讨论】:

【参考方案8】:

在 VC2 中,在重新加载表之前尝试延迟函数

DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) tableView.reloadData

否则

在 VC1 中的 didSelectresign first responder 的搜索控制器,然后再推送到 VC2

在 VC1 中 didSelect resign first responder of search controller 并设置 delay,然后再推送到 VC2。

【讨论】:

使用幻数通常是一个半解决方案......为什么是 0.1,为什么不是 0.2 或 0.05.. 完成处理程序更安全。 是的,这是一个半解决方案,有时它可能会有所帮助..!

以上是关于从 iOS 11 搜索控制器导航时不需要的 UITableView 重新加载动画的主要内容,如果未能解决你的问题,请参考以下文章

iOS 11.2 - 在带有大标题的导航栏中时,搜索控制器栏背景清晰

UINavigationContoller interactivePopGestureRecognizer 在导航栏隐藏时不活动

在 iOS 11 上,导航项中的搜索栏在导航弹出时折叠并卡在状态栏下

iOS - 通过动画将搜索栏从中心移动到导航栏下方(Swift)

从视图控制器呈现导航视图 - IOS 7

iOS 7 导航栏中的搜索栏