UITableView 中的 UIView 动画在重新加载数据后不起作用

Posted

技术标签:

【中文标题】UITableView 中的 UIView 动画在重新加载数据后不起作用【英文标题】:UIView animation in UITableView doesn't work after reload data 【发布时间】:2018-01-29 13:51:35 【问题描述】:

我现在在我的应用程序中遇到一个奇怪的错误。我有一个 UIView 的扩展,它将指示器向左和向右旋转 90 度。我使用 UITableViewController 作为导航抽屉。我还添加了一个可扩展列表,并为主要区域(具有子项)使用了 UITableViewHeaderFooterView。

UITableViewHeaderFooterView 就像主要的“单元格”。在它们下面是 UITableView 的真实单元格,当用户点击 UITableViewHeaderFooterView 时,这些单元格会被删除和插入。

根据 WebView(主视图)的 URL,我想更新 tableView 的内容。一旦数据发生变化,扩展的动画就不再起作用。代码仍然被执行并且值是正确的。我只是再也看不到动画了。

UITableViewHeaderFooterView:

import UIKit

class TableSectionHeader: UITableViewHeaderFooterView 
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var containerView: UIView!
    @IBOutlet weak var indicatorImage: UIImageView!

    override func draw(_ rect: CGRect) 
        super.draw(rect)

        indicatorImage.image = IconFont.image(fromIcon: .next, size: Constants.Sizes.MENU_INDICATOR_SIZE, color: Constants.Colors.WHITE_COLOR)
    

    func setExpanded(expanded: Bool) 
        indicatorImage.rotate(expanded ? .pi / 2 : 0.0)
    

WebViewController里面的方法:

private func loadNewMenu(href: String) 
    NetworkService.sharedInstance.getNavigation(path: href, completion:  menu in
        if let menuController = self.menuVC 
            menuController.menuItems = menu
            menuController.tableView.reloadData()
        
    )
 

UIView 扩展:

import UIKit

extension UIView 

    func rotate(_ toValue: CGFloat, duration: CFTimeInterval = 0.2) 
        let animation = CABasicAnimation(keyPath: "transform.rotation")

        animation.toValue = toValue
        animation.duration = duration
        animation.isRemovedOnCompletion = false
        animation.fillMode = kCAFillModeForwards

        self.layer.add(animation, forKey: "rotationIndicator")
    


MenuTableViewController的相关方法:

// MARK: Custom Menu Methods

@objc func handleExpandClose(sender: UITapGestureRecognizer)         
    guard let section = sender.view?.tag else 
        return
    

    var indexPaths = [IndexPath]()

    for row in subItems.indices 
        let indexPath = IndexPath(row: row, section: section)
        indexPaths.append(indexPath)
    

    let isExpanded = menuItems[section].isExpanded
    menuItems[section].isExpanded = !isExpanded
    sectionHeaders[section].setExpanded(expanded: !isExpanded)

    if isExpanded 
        tableView.deleteRows(at: indexPaths, with: .fade)
     else 
        tableView.beginUpdates()
        closeOpenedSections(section: section)
        tableView.insertRows(at: indexPaths, with: .fade)
        tableView.endUpdates()
    


private func closeOpenedSections(section: Int) 
    var closeIndexPaths = [IndexPath]()

    for (sectionIndex, sec) in menuItems.enumerated() 
        guard let subItems = sec.subItems, sec.isExpanded, sectionIndex != section else 
            continue
        

        sec.isExpanded = false
        for rowIndex in subItems.indices 
            let closeIndexPath = IndexPath(row: rowIndex, section: sectionIndex)
            closeIndexPaths.append(closeIndexPath)
        

        sectionHeaders[sectionIndex].setExpanded(expanded: false)

        tableView.deleteRows(at: closeIndexPaths, with: .fade)
    

这就是我的 MenuTableViewController 中 viewForHeaderInSection 的实现:

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? 
    if let sectionHeader = self.tableView.dequeueReusableHeaderFooterView(withIdentifier: "TableSectionHeader") as? TableSectionHeader 
        sectionHeader.setUpHeader(emphasize: menuItems[section].emphasize)
        sectionHeader.titleLabel.text = menuItems[section].title

        let tap = UITapGestureRecognizer(target: self, action: #selector(MenuTableViewController.handleExpandClose(sender:)))
        sectionHeader.addGestureRecognizer(tap)

        sectionHeader.indicatorImage.isHidden = menuItems[section].subItems != nil ? false : true
        sectionHeader.tag = section
        self.sectionHeaders.append(sectionHeader)

        return sectionHeader
    

    return UIView()

我知道它有很多代码,但我猜它与 tableView 的 reloadData() 有某种关系。在我执行重新加载之前,动画效果很好。如果我在重新加载之前没有打开菜单,我也看不到这个错误。它只是在第一次重新加载数据之后发生(即使数据完全相同)。

同样,动画代码仍然被执行并且扩展的值是正确的(真/假)。我已经将动画发生在控制台上的 UIImageView 打印到控制台上,即使在重新加载后它似乎也是相同的视图。但是动画没有出现。

【问题讨论】:

如果你调用.reloadData(),可以触发动画再次运行吗? @DonMag 好吧,是的,它仍然有效。但数据也不会更新。 我没有对CABasicAnimation 做太多事情,所以只是抛出一个想法......在重新制作动画之前你需要调用.removeAnimation(forKey:) 吗? @DonMag 我一开始也是这么想的。但是我添加了一个 forEach 循环,它在重新加载之前删除了所有动画并且它不起作用:/ 嗯...好吧,看起来您是在完成块中调用.reloadData()。这是在主线程上运行的吗?我值得尝试将其包装在 DispatchQueue.main.async() 块中和/或确保您在主线程上调用 indicatorImage.rotate(...) 【参考方案1】:

原来是我自己搞错了!我使用了两个数组(sectionHeaders、menuItems)。当我收到新数据时,我只更改了其中一个。因此,单元格与我正在处理的内容之间存在不匹配。

【讨论】:

以上是关于UITableView 中的 UIView 动画在重新加载数据后不起作用的主要内容,如果未能解决你的问题,请参考以下文章

使用自动布局动画 UIView 动画 UITableView 复选标记

键盘动画移动 UITableView 部分标题

从屏幕右侧向左滑动 UIView

用动画调整 UIView 的大小,子视图不会动画

动画 UITableView 调整大小

UIView 动画打破 TableView