如何使用 reloadItems(at: [indexPath]) 淡入淡出项目
How to fade items using reloadItems(at: [indexPath])
我有一个包含 uicollectionview
的视图控制器。每个 collectionview 单元格都包含一个按钮,单击该按钮会在单元格中添加一个新标签。为了扩展每个单元格的高度,我调用 reloadItems(at: [indexPath])
。
不幸的是调用reloadItems(at: [indexPath])
淡出旧标签并淡入新标签,如何防止任何标签淡出?
每次单击 addLabel
按钮时,错误变得更加明显:一个新标签淡入,但以前不可见的任何标签突然再次出现,并且以前可见的任何标签神奇地变为不可见再次。
reloadItems(at: [indexPath])
似乎以不同方式切换每个新标签的 alpha。我想在不让任何标签消失的情况下调整大小并向单元格添加新标签。
这是我的代码:
ViewController
class ViewController: UIViewController {
weak var collectionView: UICollectionView!
var expandedCellIdentifier = "ExpandableCell"
var cellWidth:CGFloat{
return collectionView.frame.size.width
}
var expandedHeight : CGFloat = 200
var notExpandedHeight : CGFloat = 50
//the first Int gives the row, the second Int gives the amount of labels in the row
var isExpanded = [Int:Int]()
override func viewDidLoad() {
super.viewDidLoad()
for i in 0..<4 {
isExpanded[i] = 1
}
}
}
extension ViewController:UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return isExpanded.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: expandedCellIdentifier, for: indexPath) as! ExpandableCell
cell.indexPath = indexPath
cell.delegate = self
cell.setupCell = "true"
return cell
}
}
extension ViewController:UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if isExpanded[indexPath.row]! > 1{
let height = (collectionView.frame.width/10)
let newHeight = height * CGFloat(isExpanded[indexPath.row]!)
return CGSize(width: cellWidth, height: newHeight)
}else{
return CGSize(width: cellWidth, height: collectionView.frame.width/6 )
}
}
}
extension ViewController:ExpandedCellDeleg{
func topButtonTouched(indexPath: IndexPath) {
isExpanded[indexPath.row] = isExpanded[indexPath.row]! + 1
UIView.animate(withDuration: 0.8, delay: 0.0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.9, options: UIView.AnimationOptions.curveEaseInOut, animations: {
self.collectionView.reloadItems(at: [indexPath])
}, completion: { success in
print("success")
})
}
}
协议
protocol ExpandedCellDeleg:NSObjectProtocol{
func topButtonTouched(indexPath:IndexPath)
}
ExpandableCell
class ExpandableCell: UICollectionViewCell {
weak var delegate:ExpandedCellDeleg?
public var amountOfIntervals:Int = 1
public var indexPath:IndexPath!
var setupCell: String? {
didSet {
print("cell should be setup!!")
}
}
let ivAddLabel: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.image = #imageLiteral(resourceName: "plus")
imageView.tintColor = .black
imageView.contentMode = .scaleToFill
imageView.backgroundColor = UIColor.clear
return imageView
}()
override init(frame: CGRect) {
super.init(frame: .zero)
contentView.addSubview(ivAddLabel)
let name = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 18))
name.center = CGPoint(x: Int(frame.width)/2 , y: 20)
name.textAlignment = .center
name.font = UIFont.systemFont(ofSize: 16)
name.textColor = UIColor.black
name.text = "Fred"
contentView.addSubview(name)
ivAddLabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -14).isActive = true
ivAddLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 10).isActive = true
ivAddLabel.widthAnchor.constraint(equalToConstant: 20).isActive = true
ivAddLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true
ivAddLabel.layer.masksToBounds = true
ivAddLabel.isUserInteractionEnabled = true
let addGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ivAddLabelSelected))
ivAddLabel.addGestureRecognizer(addGestureRecognizer)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func ivAddLabelSelected(){
print("add button was tapped!")
if let delegate = self.delegate{
amountOfIntervals = amountOfIntervals + 1
let height = (20*amountOfIntervals)
let name = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 18))
name.center = CGPoint(x: Int(frame.width)/2, y: height)
name.textAlignment = .center
name.font = UIFont.systemFont(ofSize: 16)
name.textColor = UIColor.black
name.text = "newFred"
name.alpha = 0.0
contentView.addSubview(name)
UIView.animate(withDuration: 0.2, animations: { name.alpha = 1.0 })
delegate.topButtonTouched(indexPath: indexPath)
}
}
}
我建议您阅读自调整 UICollectionViewCells(例如 UICollectionView Self Sizing Cells with Auto Layout) and UIStackView (e.g. https://janthielemann.de/ios-development/self-sizing-uicollectionviewcells-ios-10-swift-3/)。
您应该使用 UIStackView,对单元格 contentView 的顶部和底部边缘进行约束。
然后你可以将你的标签作为 managedSubviews 添加到你的 stackView。这将添加带有动画的标签。
使用自动调整大小的单元格,您不需要 reloadItems
,它应该按您预期的方式工作。
这是因为您为新标签设置了动画
UIView.animate(withDuration: 0.2, animations: { name.alpha = 1.0 })
并并行重新加载创建新 cell/reuses 现有单元格并显示它,但您也将重新加载包装到动画块中,这看起来很奇怪且无用:
UIView.animate(withDuration: 0.8, delay: 0.0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.9, options: UIView.AnimationOptions.curveEaseInOut, animations: {
self.collectionView.reloadItems(at: [indexPath])
}, completion: { success in
print("success")
})
您需要删除这两个动画并重新加载单元格。如果你需要一个很好的单元格扩展动画,你需要实现集合布局来处理所有状态——动画的开始、中间、结束。这个很难(硬。
尝试使用其他答案"UICollectionView Self Sizing Cells with Auto Layout"中的建议,如果没有帮助,那么要么忘记动画的想法,要么实现自定义布局。
我有一个包含 uicollectionview
的视图控制器。每个 collectionview 单元格都包含一个按钮,单击该按钮会在单元格中添加一个新标签。为了扩展每个单元格的高度,我调用 reloadItems(at: [indexPath])
。
不幸的是调用reloadItems(at: [indexPath])
淡出旧标签并淡入新标签,如何防止任何标签淡出?
每次单击 addLabel
按钮时,错误变得更加明显:一个新标签淡入,但以前不可见的任何标签突然再次出现,并且以前可见的任何标签神奇地变为不可见再次。
reloadItems(at: [indexPath])
似乎以不同方式切换每个新标签的 alpha。我想在不让任何标签消失的情况下调整大小并向单元格添加新标签。
这是我的代码:
ViewController
class ViewController: UIViewController {
weak var collectionView: UICollectionView!
var expandedCellIdentifier = "ExpandableCell"
var cellWidth:CGFloat{
return collectionView.frame.size.width
}
var expandedHeight : CGFloat = 200
var notExpandedHeight : CGFloat = 50
//the first Int gives the row, the second Int gives the amount of labels in the row
var isExpanded = [Int:Int]()
override func viewDidLoad() {
super.viewDidLoad()
for i in 0..<4 {
isExpanded[i] = 1
}
}
}
extension ViewController:UICollectionViewDataSource{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return isExpanded.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: expandedCellIdentifier, for: indexPath) as! ExpandableCell
cell.indexPath = indexPath
cell.delegate = self
cell.setupCell = "true"
return cell
}
}
extension ViewController:UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if isExpanded[indexPath.row]! > 1{
let height = (collectionView.frame.width/10)
let newHeight = height * CGFloat(isExpanded[indexPath.row]!)
return CGSize(width: cellWidth, height: newHeight)
}else{
return CGSize(width: cellWidth, height: collectionView.frame.width/6 )
}
}
}
extension ViewController:ExpandedCellDeleg{
func topButtonTouched(indexPath: IndexPath) {
isExpanded[indexPath.row] = isExpanded[indexPath.row]! + 1
UIView.animate(withDuration: 0.8, delay: 0.0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.9, options: UIView.AnimationOptions.curveEaseInOut, animations: {
self.collectionView.reloadItems(at: [indexPath])
}, completion: { success in
print("success")
})
}
}
协议
protocol ExpandedCellDeleg:NSObjectProtocol{
func topButtonTouched(indexPath:IndexPath)
}
ExpandableCell
class ExpandableCell: UICollectionViewCell {
weak var delegate:ExpandedCellDeleg?
public var amountOfIntervals:Int = 1
public var indexPath:IndexPath!
var setupCell: String? {
didSet {
print("cell should be setup!!")
}
}
let ivAddLabel: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.image = #imageLiteral(resourceName: "plus")
imageView.tintColor = .black
imageView.contentMode = .scaleToFill
imageView.backgroundColor = UIColor.clear
return imageView
}()
override init(frame: CGRect) {
super.init(frame: .zero)
contentView.addSubview(ivAddLabel)
let name = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 18))
name.center = CGPoint(x: Int(frame.width)/2 , y: 20)
name.textAlignment = .center
name.font = UIFont.systemFont(ofSize: 16)
name.textColor = UIColor.black
name.text = "Fred"
contentView.addSubview(name)
ivAddLabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -14).isActive = true
ivAddLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 10).isActive = true
ivAddLabel.widthAnchor.constraint(equalToConstant: 20).isActive = true
ivAddLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true
ivAddLabel.layer.masksToBounds = true
ivAddLabel.isUserInteractionEnabled = true
let addGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ivAddLabelSelected))
ivAddLabel.addGestureRecognizer(addGestureRecognizer)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func ivAddLabelSelected(){
print("add button was tapped!")
if let delegate = self.delegate{
amountOfIntervals = amountOfIntervals + 1
let height = (20*amountOfIntervals)
let name = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 18))
name.center = CGPoint(x: Int(frame.width)/2, y: height)
name.textAlignment = .center
name.font = UIFont.systemFont(ofSize: 16)
name.textColor = UIColor.black
name.text = "newFred"
name.alpha = 0.0
contentView.addSubview(name)
UIView.animate(withDuration: 0.2, animations: { name.alpha = 1.0 })
delegate.topButtonTouched(indexPath: indexPath)
}
}
}
我建议您阅读自调整 UICollectionViewCells(例如 UICollectionView Self Sizing Cells with Auto Layout) and UIStackView (e.g. https://janthielemann.de/ios-development/self-sizing-uicollectionviewcells-ios-10-swift-3/)。
您应该使用 UIStackView,对单元格 contentView 的顶部和底部边缘进行约束。
然后你可以将你的标签作为 managedSubviews 添加到你的 stackView。这将添加带有动画的标签。
使用自动调整大小的单元格,您不需要 reloadItems
,它应该按您预期的方式工作。
这是因为您为新标签设置了动画
UIView.animate(withDuration: 0.2, animations: { name.alpha = 1.0 })
并并行重新加载创建新 cell/reuses 现有单元格并显示它,但您也将重新加载包装到动画块中,这看起来很奇怪且无用:
UIView.animate(withDuration: 0.8, delay: 0.0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.9, options: UIView.AnimationOptions.curveEaseInOut, animations: {
self.collectionView.reloadItems(at: [indexPath])
}, completion: { success in
print("success")
})
您需要删除这两个动画并重新加载单元格。如果你需要一个很好的单元格扩展动画,你需要实现集合布局来处理所有状态——动画的开始、中间、结束。这个很难(硬。
尝试使用其他答案"UICollectionView Self Sizing Cells with Auto Layout"中的建议,如果没有帮助,那么要么忘记动画的想法,要么实现自定义布局。