代码之家  ›  专栏  ›  技术社区  ›  Emre Önder

当UITableView重新加载节时内容插入发生更改

  •  4
  • Emre Önder  · 技术社区  · 5 年前

    我有一个可扩展的 UITableView . 当用户点击标题时,相关单元格将显示动画(rowanimation.fade),然后 UITababVIEW 滚动到该标题(扩展标题)。当用户再次点击那个标题时,它就会折叠。

    我想要什么 实现 :我需要一个带有标题和单元格的可扩展的UITableView。当用户点击标题时,单元格需要用 RowAnimation.Fade 然后滚动到那个标题。

    另外,如果我能在用户点击头部时获得箭头动画效果会很好,但我认为这会导致另一个bug,因为我们在同一线程(主线程)上运行了太多动画。

    我的问题 是指当用户点击标题时,TableView内容插入会发生更改,整个标题将继续处于负Y位置。所以一个奇怪的动画发生了。(例如,标题看起来是单元格的中心)但是,动画完成后,一切看起来都是正确的。

    func toggleSection(header: DistrictTableViewHeader, section: Int) {
    self.selectedHeaderIndex = section
    self.cities[section].isCollapsed = !self.cities[section].isCollapsed
    let contentOffset = self.tableView.contentOffset
    self.tableView.reloadSections(IndexSet(integer: section), with: UITableView.RowAnimation.fade)
    self.tableView.scrollToRow(at: IndexPath(row: NSNotFound, section: section) /* you can pass NSNotFound to scroll to the top of the section even if that section has 0 rows */, at: UITableView.ScrollPosition.top, animated: true)
    }
    

    另外:我设置了标题和单元格的高度,如下所示。

    func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
        return 1
    }
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 60
    }
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 140
    }
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let header: DistrictTableViewHeader = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerId) as! DistrictTableViewHeader
        //let header = DistrictTableViewHeader()
    
        header.customInit(title: self.cities[section].name, section: section, delegate: self,isColapsed: self.cities[section].isCollapsed,isSelectedHeader: section == selectedHeaderIndex ? true : false)
        return header
    }
    

    我的自定义HeaderView类:

    protocol ExpandableHeaderViewDelegate {
    func toggleSection(header: DistrictTableViewHeader, section: Int)
    }
    
    class DistrictTableViewHeader: UITableViewHeaderFooterView {
    var delegate: ExpandableHeaderViewDelegate?
    var section: Int!
    
    let nameLabel: UILabel = {
       let l = UILabel()
        l.textColor = Color.DistrictsPage.headerTextColor
        return l
    }()
    
    private let arrowImage: UIImageView = {
      let i = UIImageView()
        let image = UIImage(named: "ileri")?.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
        i.image = image
        i.contentMode = .scaleAspectFit
        return i
    }()
    var willAnimate: Bool = false
    var isColapsed: Bool!{
        didSet{
            expandCollapseHeader()
        }
    }
    
    private func expandCollapseHeader(){
        if(willAnimate){
            if(!self.isColapsed){
                let degrees : Double = 90 //the value in degrees
                self.nameLabel.textColor = Color.Common.garantiLightGreen
                self.arrowImage.tintColor = Color.Common.garantiLightGreen
                self.arrowImage.transform = CGAffineTransform.init(rotationAngle: CGFloat(degrees * .pi/180))
                self.contentView.backgroundColor = UIColor(red:0.97, green:0.97, blue:0.97, alpha:1.0)
            }else{
                let degrees : Double = 0 //the value in degrees
                self.nameLabel.textColor = Color.DistrictsPage.headerTextColor
                self.arrowImage.tintColor = UIColor.black
                self.arrowImage.transform = CGAffineTransform.init(rotationAngle: CGFloat(degrees * .pi/180))
                self.contentView.backgroundColor = UIColor.white
            }
        }else{
            if(!isColapsed){
                let degrees : Double = 90 //the value in degrees
                self.nameLabel.textColor = Color.Common.garantiLightGreen
                self.arrowImage.tintColor = Color.Common.garantiLightGreen
                self.arrowImage.transform = CGAffineTransform.init(rotationAngle: CGFloat(degrees * .pi/180))
                self.contentView.backgroundColor = UIColor(red:0.97, green:0.97, blue:0.97, alpha:1.0)
            }else{
                let degrees : Double = 0 //the value in degrees
                self.nameLabel.textColor = Color.DistrictsPage.headerTextColor
                self.arrowImage.tintColor = UIColor.black
                self.arrowImage.transform = CGAffineTransform.init(rotationAngle: CGFloat(degrees * .pi/180))
                self.contentView.backgroundColor = UIColor.white
            }
            layoutSubviews()
        }
    }
    
    override init(reuseIdentifier: String?) {
    
        super.init(reuseIdentifier: reuseIdentifier)
        self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(selectHeaderAction)))
        nameLabel.translatesAutoresizingMaskIntoConstraints = false
        nameLabel.font = UIFont.systemFont(ofSize: 22)
        nameLabel.textColor = Color.DistrictsPage.headerTextColor
        contentView.addSubview(nameLabel)
        nameLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
        nameLabel.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 15).isActive = true
    
        arrowImage.tintColor =  UIColor(red:0.32, green:0.36, blue:0.36, alpha:1.0)
        arrowImage.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(arrowImage)
        arrowImage.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
        arrowImage.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -20).isActive = true
        arrowImage.widthAnchor.constraint(equalToConstant: 20).isActive = true
    
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    func rotate(_ toValue: CGFloat) {
        self.transform = CGAffineTransform.init(rotationAngle: toValue)
    }
    @objc func selectHeaderAction(gestureRecognizer: UITapGestureRecognizer) {
        let cell = gestureRecognizer.view as! DistrictTableViewHeader
        delegate?.toggleSection(header: self, section: cell.section)
    }
    
    
    func customInit(title: String, section: Int, delegate: ExpandableHeaderViewDelegate,isColapsed: Bool, isSelectedHeader: Bool) {
        self.nameLabel.text = title
        self.nameLabel.accessibilityIdentifier = title
        self.section = section
        self.delegate = delegate
        self.willAnimate = isSelectedHeader
        self.isColapsed = isColapsed
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        self.contentView.backgroundColor = UIColor.white
    
    }
    }
    

    在下图中,可以清楚地看到bug。当“一些数据”和“另一个城市”打开时,你点击“一些数据”。出现动画错误。“另一个城市“高于”它的“牢房”,然后上升。应该做的是,“另一个城市”应该留在它的位置,然后在“某些数据”单元关闭时向上移动。 enter image description here 示例项目: https://github.com/emreond/tableViewLayoutIssue

    2 回复  |  直到 5 年前
        1
  •  4
  •   Emre Önder    5 年前

    经过几天的搜索和尝试,我发现这种变化 UITableViewStyle 给小组表演技巧。因此,我更改了初始化 UITableView

    let tableView = UITableView.init(frame: CGRect.zero, style: .grouped)

    除了滚动,我还需要添加 CATransaction 以赶上完成。

    CATransaction.begin()
    DispatchQueue.main.async {
    self.tableView.beginUpdates()
    CATransaction.setCompletionBlock {
        // Code to be executed upon completion
            self.tableView.scrollToRow(at: IndexPath(row: NSNotFound, section: section) /* you can pass NSNotFound to scroll to the top of the section even if that section has 0 rows */, at: UITableView.ScrollPosition.top, animated: true)
    }
        self.tableView.reloadSections(IndexSet.init(integer: section), with: UITableView.RowAnimation.fade)
        self.tableView.endUpdates()
    }
    CATransaction.commit()
    }
    
        2
  •  -1
  •   Guillaume Ramey    5 年前

    有相同的问题,您可以将此作为修复方法:

    UIView.performWithoutAnimation {
        self.tableView.reloadSections(IndexSet(integer: section), with: .fade)
    }
    

    当然,你会失去动画,但也会失去闪烁。