簡體   English   中英

無法結合 UIImageView 旋轉 animation 和 tableView 部分重新加載

[英]Can't combine UIImageView rotation animation and tableView section reload

我有 4 個部分,每個部分有 2 個嵌套行。 我通過點擊每個部分打開行。

這是我的初始數據的樣子。 它有titlesubtitleoptions (這是嵌套行應該顯示的內容):

   private var sections = [
        SortingSection(title: "По имени", subtitle: "Российский рубль", options: ["По возрастанию (А→Я)", "По убыванию (Я→А)"]),
        SortingSection(title: "По короткому имени", subtitle: "RUB", options: ["По возрастанию (А→Я)", "По убыванию (Я→А)"]),
        SortingSection(title: "По значению", subtitle: "86,22", options: ["По возрастанию (1→2)", "По убыванию (2→1)"]),
        SortingSection(title: "Своя", subtitle: "в любом порядке", options: ["Включить"])
   ]

當我點擊一個部分時,我希望它的附件( chevron.right ,制作為UIImageView )與嵌套行的擴展同步旋轉,當我再次單擊相同的關閉行為時。

我有一個名為 isOpened 的變量(布爾值,默認情況下為 false),我將其從 false 更改為 true 並在didSelectRowAt中每次點擊后返回。 基於此顯示所有嵌套單元格並旋轉UIImageView

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    if indexPath.row == 0 {
        sections[indexPath.section].isOpened.toggle()
        
        guard let cell = tableView.cellForRow(at: indexPath) as? MainSortTableViewCell else { return }
        
        UIView.animate(withDuration: 0.3) {
            if self.sections[indexPath.section].isOpened {
                cell.chevronImage.transform = CGAffineTransform(rotationAngle: .pi/2)
            } else {
                cell.chevronImage.transform = .identity
            }
        } completion: { _ in
            tableView.reloadSections([indexPath.section], with: .none)
        }
     }

正如您在上面看到的,我重新加載 tableView 部分以在 animation 之后的完成塊中顯示\隱藏嵌套行。我不能在 if\else 語句中使用reloadSections ,因為 chevron animation會被跳過

還有我的numberOrRowsInSection方法:

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    let section = sections[section]
    if section.isOpened {
        return section.options.count + 1
    } else {
        return 1
    }
}
  1. 這是它現在的樣子:點擊

  2. 這是我想要的(任何 iPhone 本機應用程序):點擊

我嘗試添加和刪除行而不是重新加載整個部分,但總是以錯誤告終:

UIView.animate(withDuration: 0.3) {
    if self.sections[indexPath.section].isOpened {
       cell.chevronImage.transform = CGAffineTransform(rotationAngle: .pi/2)
                
         for i in 0..<self.sections[indexPath.section].options.count {
                    tableView.insertRows(at: [IndexPath(row: 1+i, section: indexPath.section)], with: .none)
         }
      } else {
         cell.chevronImage.transform = .identity
                
       for i in 0..<self.sections[indexPath.section].options.count {
        tableView.deleteRows(at: [IndexPath(row: i-1, section: indexPath.section)], with: .none)
                }
            }
        }

如何更改我的代碼以解決任務並在嵌套行展開或關閉的同時為人字形設置動畫?

如您所見,如果要為單元格中的元素設置動畫,則不能在重新加載單元格的同時執行此操作。

因此,要獲得您想要的效果,一種方法是將您的數據拆分為“部分對”。

所以,而不是這個:

在此處輸入圖像描述

你會得到這個:

在此處輸入圖像描述

當點擊“標題”部分時,您可以在重新加載下一部分為該單元格設置圖像視圖旋轉動畫。

它需要對數據進行更多的管理——但實際上並沒有那么多。

例如,如果數據結構是:

struct SortingSection {
    var title: String = ""
    var subtitle: String = ""
    var options: [String] = []
    var isOpened: Bool = false
}

numberOfSections中我們可以返回sections.count * 2

然后,在numberOfRowsInSection中,我們將獲取“virtualSection”編號以獲取數據數組中的索引 - 如下所示:

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    let virtualSection: Int = section / 2
    let secItem = sections[virtualSection]
    if section % 2 == 0 {
        return 1
    }
    if secItem.isOpened {
        return secItem.options.count
    }
    return 0
}

同樣,在cellForRowAt中:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let virtualSection: Int = indexPath.section / 2
    let secItem = sections[virtualSection]
    if indexPath.section % 2 == 0 {
        // return a "header row cell"
    }
    // return a "option row cell"
}

最后,在didSelectRowAt中:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    let virtualSection: Int = indexPath.section / 2
    // if it's a "header row"
    if indexPath.section % 2 == 0 {
        sections[virtualSection].isOpened.toggle()
        guard let c = tableView.cellForRow(at: indexPath) as? ExpandCell else { return }
        UIView.animate(withDuration: 0.3) {
            if self.sections[virtualSection].isOpened {
                c.chevronImageView.transform = CGAffineTransform(rotationAngle: .pi/2)
            } else {
                c.chevronImageView.transform = .identity
            }
            // reload the NEXT section
            tableView.reloadSections([indexPath.section + 1], with: .automatic)
        }
    }

}

這是一個完整的嘗試實現。 一切都是通過代碼完成的(沒有@IBOutlet連接),因此創建一個新的UITableViewController並將其自定義 class 分配給ExpandSectionTableViewController

struct SortingSection {
    var title: String = ""
    var subtitle: String = ""
    var options: [String] = []
    var isOpened: Bool = false
}
class ExpandCell: UITableViewCell {
    let titleLabel = UILabel()
    let subtitleLabel = UILabel()
    let chevronImageView = UIImageView()
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() {
        
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(titleLabel)
        
        subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(subtitleLabel)
        
        chevronImageView.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(chevronImageView)
        
        let g = contentView.layoutMarginsGuide
        NSLayoutConstraint.activate([
            titleLabel.topAnchor.constraint(equalTo: g.topAnchor),
            titleLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            
            subtitleLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 4.0),
            subtitleLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            
            chevronImageView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            chevronImageView.widthAnchor.constraint(equalToConstant: 40.0),
            chevronImageView.heightAnchor.constraint(equalTo: chevronImageView.widthAnchor),
            chevronImageView.centerYAnchor.constraint(equalTo: g.centerYAnchor),
            
            subtitleLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor),
            
        ])
    
        subtitleLabel.font = .systemFont(ofSize: 12.0, weight: .regular)
        subtitleLabel.textColor = .gray
        
        chevronImageView.contentMode = .center
        let cfg = UIImage.SymbolConfiguration(pointSize: 24.0, weight: .regular)
        if let img = UIImage(systemName: "chevron.right", withConfiguration: cfg) {
            chevronImageView.image = img
        }
        
    }
}
class SubCell: UITableViewCell {
    
    let titleLabel = UILabel()
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() {
        
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(titleLabel)
        
        let g = contentView.layoutMarginsGuide
        NSLayoutConstraint.activate([
            titleLabel.topAnchor.constraint(equalTo: g.topAnchor),
            titleLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            titleLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor),
        ])
        
        titleLabel.font = .italicSystemFont(ofSize: 15.0)
        
    }
}

class ExpandSectionTableViewController: UITableViewController {
    
    var sections: [SortingSection] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let optCounts: [Int] = [
            2, 3, 2, 5, 4, 2, 2, 3, 3, 4, 2, 1, 2, 3, 4, 3, 2
        ]
        for (i, val) in optCounts.enumerated() {
            var opts: [String] = []
            for n in 1...val {
                opts.append("Section \(i) - Option \(n)")
            }
            sections.append(SortingSection(title: "Title \(i)", subtitle: "Subtitle \(i)", options: opts, isOpened: false))
        }
        
        tableView.register(ExpandCell.self, forCellReuseIdentifier: "expCell")
        tableView.register(SubCell.self, forCellReuseIdentifier: "subCell")
    }
    override func numberOfSections(in tableView: UITableView) -> Int {
        return sections.count * 2
    }
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        let virtualSection: Int = section / 2
        let secItem = sections[virtualSection]
        if section % 2 == 0 {
            return 1
        }
        if secItem.isOpened {
            return secItem.options.count
        }
        return 0

    }
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let virtualSection: Int = indexPath.section / 2
        let secItem = sections[virtualSection]
        if indexPath.section % 2 == 0 {
            let c = tableView.dequeueReusableCell(withIdentifier: "expCell", for: indexPath) as! ExpandCell
            c.titleLabel.text = secItem.title
            c.subtitleLabel.text = secItem.subtitle
            c.chevronImageView.transform = secItem.isOpened ? CGAffineTransform(rotationAngle: .pi/2) : .identity
            c.selectionStyle = .none
            return c
        }
        let c = tableView.dequeueReusableCell(withIdentifier: "subCell", for: indexPath) as! SubCell
        c.titleLabel.text = secItem.options[indexPath.row]
        return c

    }
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        let virtualSection: Int = indexPath.section / 2
        // if it's a "header row"
        if indexPath.section % 2 == 0 {
            sections[virtualSection].isOpened.toggle()
            guard let c = tableView.cellForRow(at: indexPath) as? ExpandCell else { return }
            UIView.animate(withDuration: 0.3) {
                if self.sections[virtualSection].isOpened {
                    c.chevronImageView.transform = CGAffineTransform(rotationAngle: .pi/2)
                } else {
                    c.chevronImageView.transform = .identity
                }
                // reload the NEXT section
                tableView.reloadSections([indexPath.section + 1], with: .automatic)
            }
        }

    }
    
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM