我想使用组合布局创建扩展单元格。我面临的一个问题是,每次当我的单元格扩展时,我都会在扩展完成后立即发生这种不需要的偏移变化(将单元格向右移动)。对于我的一生,我不明白为什么它会滚动。从展开状态来看,屏幕左侧的空间并不是不够。所以我不确定是什么原因造成的,以及我们是否可以手动将其关闭。任何建议将不胜感激!
class ViewController: UIViewController, UICollectionViewDelegate {
lazy var collectionView: UICollectionView = {
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: createLayout())
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.delegate = self
collectionView.contentInsetAdjustmentBehavior = .never
collectionView.alwaysBounceVertical = true
collectionView.alwaysBounceHorizontal = true
return collectionView
}()
var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
var items = [Item]()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
setupCollectionViewDataSource()
...
}
func setupCollectionViewDataSource() {
dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView, cellProvider: { collectionView, indexPath, itemIdentifier in
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ProminentCell.cellId, for: indexPath) as! ProminentCell
cell.backgroundColor = self.colors.randomElement()
cell.numberLabel.text = String(indexPath.item)
cell.delegate = self
return cell
})
updateDataSource()
}
func createLayout() -> UICollectionViewLayout {
let layout = UICollectionViewCompositionalLayout { sectionNumber, env in
let itemSize = NSCollectionLayoutSize(widthDimension: .estimated(325), heightDimension: .absolute(708))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: itemSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
section.orthogonalScrollingBehavior = .continuous
section.interGroupSpacing = 10
return section
}
return layout
}
private func updateDataSource() {
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections([.carousel])
snapshot.appendItems(items)
dataSource.apply(snapshot, animatingDifferences: true, completion: nil)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let cell = collectionView.cellForItem(at: indexPath) as? ProminentCell {
cell.currentState = .expanded
}
}
func collectionView(_ collectionView: UICollectionView, didUpdateFocusIn context: UICollectionViewFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
guard let nextIndexPath = context.nextFocusedIndexPath, let prevIndexPath = context.previouslyFocusedIndexPath else { return }
collectionView.isScrollEnabled = false
collectionView.scrollToItem(at: nextIndexPath, at: .left, animated: true)
}
}
extension ViewController: ProminentCellDelegate {
func expandCell(cell: ProminentCell) {
let snapshot = dataSource.snapshot()
dataSource.apply(snapshot, animatingDifferences: true, completion: nil)
}
}
细胞类
protocol ProminentCellDelegate: AnyObject {
func expandCell(cell: ProminentCell)
func collapseCell(cell: ProminentCell)
}
enum ProminentCellState {
case collapsed, expanded
}
class ProminentCell: UICollectionViewCell {
static let cellId = "cellId"
weak var delegate: ProminentCellDelegate?
var currentState: ProminentCellState {
didSet {
if currentState == .collapsed {
collapseCell {}
} else {
expandCell(completion: {})
}
}
}
let imageStringName = ""
lazy var backgroundImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: imageStringName))
imageView.backgroundColor = .systemOrange
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.adjustsImageWhenAncestorFocused = true
return imageView
}()
let numberLabel: UILabel = {
let label = UILabel()
...
return label
}()
private let expandedWidth = 1035.0
private let collapsedWidth = 325.0
private let animationDuration = 0.33
var closedConstraint: NSLayoutConstraint? = nil
var openConstraint: NSLayoutConstraint? = nil
override init(frame: CGRect) {
currentState = .collapsed
super.init(frame: frame)
contentView.addSubview(backgroundImageView)
contentView.addSubview(numberLabel)
contentView.translatesAutoresizingMaskIntoConstraints = false
let padding = 10.0
NSLayoutConstraint.activate([
contentView.topAnchor.constraint(equalTo: topAnchor),
contentView.leadingAnchor.constraint(equalTo: leadingAnchor),
contentView.trailingAnchor.constraint(equalTo: trailingAnchor),
contentView.bottomAnchor.constraint(equalTo: bottomAnchor),
backgroundImageView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: padding),
backgroundImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: padding),
backgroundImageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -padding),
backgroundImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -padding),
numberLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
numberLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
])
closedConstraint = contentView.widthAnchor.constraint(equalToConstant: collapsedWidth)
closedConstraint?.isActive = true
openConstraint = contentView.widthAnchor.constraint(equalToConstant: expandedWidth)
}
private func expandCell(completion: () -> Void) {
closedConstraint?.isActive = false
openConstraint?.isActive = true
let curFrame = frame.origin
let expandedSize = CGRect(x: curFrame.x, y: curFrame.y, width: expandedWidth, height: frame.size.height)
UIViewPropertyAnimator.runningPropertyAnimator(withDuration: animationDuration, delay: 0, options: [.curveLinear]) {
self.frame = expandedSize
self.layoutIfNeeded()
self.contentView.layoutIfNeeded()
}
delegate?.expandCell(cell: self)
}
private func collapseCell(completion: () -> Void) {
openConstraint?.isActive = false
closedConstraint?.isActive = true
let curFrame = frame.origin
let collapsedSize = CGRect(x: curFrame.x, y: curFrame.y, width: collapsedWidth, height: frame.size.height)
UIViewPropertyAnimator.runningPropertyAnimator(withDuration: animationDuration, delay: 0, options: [.beginFromCurrentState]) {
self.frame = collapsedSize
self.contentView.layoutIfNeeded()
self.layoutIfNeeded()
}
delegate?.collapseCell(cell: self)
}
...
}
解决方案是将正交滚动视图的 isScrollEnabled 设置为 false。这会删除滚动视图内容大小更改时发生的本机滚动动画。