[英]Can't combine UIImageView rotation animation and tableView section reload
我有 4 個部分,每個部分有 2 個嵌套行。 我通過點擊每個部分打開行。
這是我的初始數據的樣子。 它有title
, subtitle
和options
(這是嵌套行應該顯示的內容):
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
}
}
我嘗試添加和刪除行而不是重新加載整個部分,但總是以錯誤告終:
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.