Mad*_*bæk 5 uitableview ios swift uistackview
我正在尝试在 UITableView 内创建一个类似手风琴的动画,其中包含动态高度的单元格。
问题似乎是 reloadRows 导致即时单元格高度更新,因此虽然 tableview 动画,但单元格本身不会动画化其高度。这会导致其他单元格有时重叠并干扰扩展的单元格内容。
这是我所得到的最接近的细胞根据需要扩展的位置。然而,当折叠时,TextView 立即消失,标题悬挂在中间直到折叠。
我试图实现的效果与尝试 2 中展开时的效果完全相同,但折叠时的效果却相反(手风琴/显示风格)。
注意:如果可能的话,我还想继续使用 StackView 来设置hiddenUITextView 上的属性,因为我正在处理的实际项目涉及几个其他子视图,这些子视图的动画效果应该与 UITextView 类似。
我正在使用 XCode 11 beta 7。
struct Post {
var id: String
var title: String
var content: String
}
let posts = [
Post(id: "1", title: "Post 1", content: "Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est"),
Post(id: "2", title: "Post 2", content: "Lorem ipsum dolor"),
Post(id: "3", title: "Post 3", content: "Lorem ipsum dolor"),
Post(id: "4", title: "Post 4", content: "Lorem ipsum dolor"),
Post(id: "5", title: "Post 5", content: "Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda."),
Post(id: "6", title: "Post 6", content: "Lorem ipsum dolor")
]
Run Code Online (Sandbox Code Playgroud)
class MyTableViewController: UITableViewController {
var selectedRow: IndexPath? = nil
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.backgroundColor = .darkGray
self.tableView.rowHeight = UITableView.automaticDimension
self.tableView.estimatedRowHeight = 200
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath)
-> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(
withIdentifier: "Cell", for: indexPath) as? MyTableViewCell
else { fatalError() }
let post = posts[indexPath.row]
let isExpanded = selectedRow == indexPath
cell.configure(expanded: isExpanded, post: post)
return cell
}
override func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath) {
var reloadPaths: [IndexPath] = []
if selectedRow != nil {
reloadPaths.append(selectedRow!)
}
if selectedRow == indexPath {
selectedRow = nil
} else {
reloadPaths.append(indexPath)
selectedRow = indexPath
}
tableView.reloadRows(at: reloadPaths, with: .automatic)
}
}
Run Code Online (Sandbox Code Playgroud)
class MyTableViewCell: UITableViewCell {
@IBOutlet weak var title: UILabel!
@IBOutlet weak var textView: UITextView!
func configure(expanded: Bool, post: Post) {
title.text = post.title
textView.text = post.content
configureExpansion(expanded)
}
func configureExpansion(_ expanded: Bool) {
self.textView.isHidden = !expanded
self.contentView.backgroundColor = expanded ?
.red : .systemBackground
}
}
Run Code Online (Sandbox Code Playgroud)
与尝试 1 完全相同的代码,但...didSelectRowAt...替换为:
override func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath) {
tableView.beginUpdates()
if let selectedRow = selectedRow,
let prevCell = tableView.cellForRow(at: selectedRow) as? MyTableViewCell {
prevCell.configureExpansion(false)
}
let selectedCell = tableView.cellForRow(at: indexPath) as? MyTableViewCell
if selectedRow == indexPath {
selectedCell?.configureExpansion(false)
selectedRow = nil
} else {
selectedCell?.configureExpansion(true)
selectedRow = indexPath
}
tableView.endUpdates()
}
Run Code Online (Sandbox Code Playgroud)
我终于在 beginUpdates/endUpdates 的基础上解决了这个问题。我对问题和解决方案的进一步研究如下。
正如原始问题中所述,扩展阶段工作正常。这样做的原因是:
isHidden属性设置为时false,包含的堆栈视图会调整大小并为单元格提供新的固有内容大小endUpdates被调用时,单元将自动扩展,因为内部大小已经改变 和tableView.rowHeight = .automaticDimension。动画将根据需要逐渐揭示新内容。然而,折叠阶段不会产生相同的反向动画效果。不这样做的原因是:
isHidden属性设置为时true,包含的堆栈视图将隐藏视图并将单元格大小调整为新的固有内容大小。endUpdates,动画将立即删除 UITextView 来开始。想想看,这是预料之中的,因为它与扩张期间发生的情况相反。然而,它也破坏了所需的动画效果,让可见元素“悬挂”在中间,而不是在行缩小时逐渐隐藏 UITextView。为了获得所需的隐藏效果,UITextView 应在单元格缩小时的整个动画过程中保持可见。这包括几个步骤:
第 1 步:覆盖heightForRow
override func tableView(_ tableView: UITableView,
heightForRowAt indexPath: IndexPath) -> CGFloat {
// If a cell is collapsing, force it to its original height stored in collapsingRow?
// instead of using the intrinsic size and .automaticDimension
if indexPath == collapsingRow?.indexPath {
return collapsingRow!.height
} else {
return UITableView.automaticDimension
}
}
Run Code Online (Sandbox Code Playgroud)
现在,当单元格缩小时,UITextView 将保持可见,但由于自动布局,UITextView 会立即被剪裁,因为它锚定到单元格高度。
第 2 步:在 UIStackView 上创建高度约束,并在单元缩小时使其优先
2.1:在界面构建器中添加了 UIStackView 的高度约束
2.2:在 MyTableViewCell 中添加heightConstraint为强(不是弱,这很重要)出口
2.3在awakeFromNibMyTableViewCell中,设置heightConstraint.isActive = false保持默认行为
2.4在界面构建器中:确保UIStackView底部约束的优先级设置为低于高度约束的优先级。即999对于底部约束和1000对于高度约束。如果不这样做,会导致崩溃阶段的约束冲突。
步骤3:折叠时,激活heightConstraint并将其设置为UIStackView当前的固有大小。这会在单元格高度降低时保持 UITextView 的内容可见,但也会根据需要剪辑内容,从而产生“隐藏”效果。
if let expandedRow = expandedRow,
let prevCell = tableView.cellForRow(at: expandedRow.indexPath) as? MyTableViewCell {
prevCell.heightConstraint.constant = prevCell.stackView.frame.height
prevCell.heightConstraint.isActive = true
collapsingRow = expandedRow
}
Run Code Online (Sandbox Code Playgroud)
步骤 4:动画完成后使用以下命令重置状态CATransaction.setCompletionBlock
class MyTableViewController: UITableViewController {
var expandedRow: (indexPath: IndexPath, height: CGFloat)? = nil
var collapsingRow: (indexPath: IndexPath, height: CGFloat)? = nil
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.backgroundColor = .darkGray
self.tableView.rowHeight = UITableView.automaticDimension
self.tableView.estimatedRowHeight = 200
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(
withIdentifier: "Cell", for: indexPath) as? MyTableViewCell
else { fatalError() }
let post = posts[indexPath.row]
let isExpanded = expandedRow?.indexPath == indexPath
cell.configure(expanded: isExpanded, post: post)
return cell
}
override func tableView(_ tableView: UITableView,
heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath == collapsingRow?.indexPath {
return collapsingRow!.height
} else {
return UITableView.automaticDimension
}
}
override func tableView(_ tableView: UITableView,
didSelectRowAt indexPath: IndexPath) {
guard let tappedCell = tableView.cellForRow(at: indexPath) as? MyTableViewCell
else { return }
CATransaction.begin()
tableView.beginUpdates()
if let expandedRow = expandedRow,
let prevCell = tableView.cellForRow(at: expandedRow.indexPath)
as? MyTableViewCell {
prevCell.heightConstraint.constant = prevCell.stackView.frame.height
prevCell.heightConstraint.isActive = true
CATransaction.setCompletionBlock {
if let cell = tableView.cellForRow(at: expandedRow.indexPath)
as? MyTableViewCell {
cell.configureExpansion(false)
cell.heightConstraint.isActive = false
}
self.collapsingRow = nil
}
collapsingRow = expandedRow
}
if expandedRow?.indexPath == indexPath {
collapsingRow = expandedRow
expandedRow = nil
} else {
tappedCell.configureExpansion(true)
expandedRow = (indexPath: indexPath, height: tappedCell.frame.height)
}
tableView.endUpdates()
CATransaction.commit()
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2464 次 |
| 最近记录: |