首页 > 解决方案 > UITableView 单元格中的旋转动画不起作用

问题描述

我正在尝试使其可扩展并制作一个简单的UITableView旋转动画,UIImage当我单击图像顺时针旋转 180° 时,当我再次单击单元格时,它将旋转 -180° 回到其正常状态,但动画不起作用并且第一次单击箭头不旋转时出现错误,我必须单击两次才能使其旋转为 gif。UITableViewCelldidSelectRowAtcellcell

在此处输入图像描述

测试模型:

struct Titles {
    var opened = Bool()
    var title = String()
    var sectionData = [String]()
}

视图控制器视图:

class ViewControllerView: UIView {

    var data = [Titles]()

    override init(frame: CGRect) {
        super.init(frame: frame)
        layoutUI()
        data = [
            Titles(opened: false, title: "Title1", sectionData: ["Cell1", "Cell2", "Cell3"]),
            Titles(opened: false, title: "Title2", sectionData: ["Cell1", "Cell2", "Cell3"]),
            Titles(opened: false, title: "Title3", sectionData: ["Cell1", "Cell2", "Cell3"])
        ]
    }

    lazy var recipesTableView: UITableView = {
        let recipesTableView = UITableView()
        recipesTableView.register(TableViewCell.self, forCellReuseIdentifier: "TableViewCell")
        recipesTableView.register(TableViewCellTwo.self, forCellReuseIdentifier: "TableViewCellTwo")
        return recipesTableView
    }()

}

extension ViewControllerView: UITableViewDelegate, UITableViewDataSource {

    func numberOfSections(in tableView: UITableView) -> Int {
        return data.count
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if data[section].opened == true {
            return data[section].sectionData.count + 1
        } else {
            return 1
        }
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if indexPath.row == 0 {
            let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell
            cell.title.text = data[indexPath.section].title
            return cell
        } else {
            let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCellTwo", for: indexPath) as! TableViewCellTwo
            cell.title.text = data[indexPath.section].sectionData[indexPath.row - 1]
            return cell
        }

    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if data[indexPath.section].opened == true {
            data[indexPath.section].opened = false
            let section = IndexSet.init(integer: indexPath.section)
            let cell = tableView.cellForRow(at: indexPath) as! TableViewCell
            UIView.animate(withDuration: 1, animations: {
                cell.arrowImage.transform = CGAffineTransform.init(rotationAngle: CGFloat.pi)
            })
            tableView.reloadSections(section, with: .none)

        } else {
            data[indexPath.section].opened = true
            let section = IndexSet.init(integer: indexPath.section)
            let cell = tableView.cellForRow(at: indexPath) as! TableViewCell
            UIView.animate(withDuration: 1, animations: {
                cell.arrowImage.transform = CGAffineTransform.identity
            })
            tableView.reloadSections(section, with: .none)

        }
    }
}

表视图单元:

class TableViewCell: UITableViewCell {

    lazy var arrowImage: UIImageView = {
        var arrow = UIImageView(image: UIImage(systemName: "arrowtriangle.down.fill"))
        arrow.tintColor = .black
        arrow.translatesAutoresizingMaskIntoConstraints = false
        return arrow
    }()
}

标签: iosswiftuitableview

解决方案


首先,我们声明一个带有可用人字形方向的枚举。

enum ChevronDirection: Double {
        case up = -180
        case down = 0
    }    

我们还可以添加一个扩展来使处理弧度更容易

extension Double {
    var degreesToRadians: CGFloat {
        return CGFloat(self) * (CGFloat(Double.pi) / 180.0)
    }
}

然后你应该创建一个 UITableViewHeaderFooterView 标题

class AnyNameHeader: UITableViewHeaderFooterView {

   @IBOutlet weak var imgChevron: UIImageView!
   var tapDelegate: DelegateToGetNotified?
   var currentSection: Int!

   func configure(_ text: String, section: Int, isExpanded: Bool) {
      currentSection = section
      self.set(isExpanded: isExpanded, animated: false)
  }

  func set(isExpanded: Bool, animated: Bool) {
      if isExpanded {
          setChevronDirection(ChevronDirection.up, animated: animated)
      } else {
          setChevronDirection(ChevronDirection.down, animated: animated)
      }
  }

  func setChevronDirection(_ direction: ChevronDirection, animated: Bool) {
      if animated {
          UIView.animate(withDuration: 0.4, delay: 0.0, options: UIViewAnimationOptions(), animations: { [weak self] in
              self?.imgChevron.transform = CGAffineTransform(rotationAngle: direction.rawValue.degreesToRadians)
              }, completion: nil)
      } else {
          self.imgChevron.transform = CGAffineTransform(rotationAngle: direction.rawValue.degreesToRadians)
      }
  }

  //Note: You can add your way to detect user tap over header for me i add a button over the header
  @IBAction func headerAction() {
      tapDelegate?.didTapHeader(sectionIndex: currentSection)
  }


} // End of header

然后我们进入 View 控制器的 Final 部分

    class MyViewController: UIViewController {

    var expandedSectionIndex : Int? = nil {
        didSet{
            tableView.beginUpdates()
            // Collapse previously expanded section
            if let oldValue = oldValue {
                tableView.deleteRows(at: indexPathsFor(section: oldValue), with: UITableViewRowAnimation.top)

                if let header = tableView.headerView(forSection: oldValue) as? AnyNameHeader {
                    header.set(isExpanded: false, animated: true)
                }
            }

            // Don't continue to Expand new section if already collapsing opened section
            if let oldValue = oldValue, let sectionIndex = expandedSectionIndex, sectionIndex == oldValue {
                expandedSectionIndex = nil
                tableView.endUpdates()
                return
            }

            // Expand new section
            if let expandedSectionIndex = expandedSectionIndex {
                tableView.insertRows(at: indexPathsFor(section: expandedSectionIndex), with: UITableViewRowAnimation.top)

                if let header = tableView.headerView(forSection: expandedSectionIndex) as? AnyNameHeader {
                    header.set(isExpanded: true, animated: true)
                }
            }
            tableView.endUpdates()
        }
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if isExpandedSection(section: section) {
            return data[section].sectionData.count
        } else {
            return 0
        }
    }
}

    // MARK: Expanded Section
    extension MyViewController {
        /// Returns Indexpaths to be Inserted or Removed for a given section Index
        fileprivate func indexPathsFor(section: Int) -> [IndexPath] {
            var newIndexPaths = [IndexPath]()
            for index in 0 ..< data[section].sectionData.count {
                let newIndexPath = IndexPath(row: index , section: section)
                newIndexPaths.append(newIndexPath)
            }
            return newIndexPaths
        }

        fileprivate func isExpandedSection(section: Int) -> Bool {
             return expandedSectionIndex == section
        }
    }

    extension MyViewController: DelegateToGetNotified {
        func didTapHeader(sectionIndex: Int) {
            expandedSectionIndex = sectionIndex
        }
    }

推荐阅读