gpt4 book ai didi

ios - UICollectionView水平分页布局

转载 作者:行者123 更新时间:2023-11-28 13:48:05 25 4
gpt4 key购买 nike

嘿,我正在尝试实现这个 UI 元素,它(对我来说)就像一个水平的 UIPickerView。这是在 iOS 上创建“memoji”时的 GIF 示例:

Example GIF

Example Picture

我一直在尝试使用 UICollectionView 和自定义 UICollectionViewFlowLayout 来完成此操作。但运气不佳。

到目前为止我尝试的是使用

func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint

停止在每个单元格上滚动,从而给它一种分页的感觉。但为了做到这一点,我实际上必须做

var collectionViewContentSize: CGSize

返回比实际存在的内容更大的内容大小,否则它只会弹回 collectionView,无论我在上一个函数中返回什么,都不会卡入到位。

我也试过用

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>)

要设置 collectionView.contentOffset 但这会导致动画中出现奇怪的跳跃,而且它又没有正确更改它。除了每个单元格的分页之​​外,我还想在该 UI 元素上实现什么,通过元素时每个滚动条上的小触觉反馈以及边框上左右元素的淡入淡出。如果有人能指出我正确的方向,也许 UICollectionView 不是要走的路?我将不胜感激。谢谢

最佳答案

我能够使用自定义 UICollectionViewFlowLayout 实现此目的

final class PaginatedCollectionViewFlow: UICollectionViewFlowLayout {

/* Distance from the midle to the other side of the screen */
var availableDistance: CGFloat = 0.0


var midX: CGFloat = 0
var lastElementIndex = 0

let maxAngle = CGFloat(-60.0.degree2Rad)

override func prepare () {
minimumInteritemSpacing = 40.0
scrollDirection = .horizontal
}

/* This should be cached */
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard
let layoutAttributes = super.layoutAttributesForElements(in: rect),
let cv = collectionView else { return nil }

/* Size of the collectionView */
let visibleRect = CGRect(origin: cv.contentOffset, size: cv.bounds.size)

let attributes: [UICollectionViewLayoutAttributes] = layoutAttributes.compactMap { attribute in
guard let copy = attribute.copy() as? UICollectionViewLayoutAttributes else { return nil }

/* Distance from the middle of the screen to the middle of the cell attributes */
let distance = visibleRect.midX - attribute.center.x

/* Normalize the distance between [0, 1] */
let normalizedDistance = abs(distance / availableDistance)

/* Rotate the cell and apply alpha accordingly to the maximum distance from the center */
copy.alpha = 1.0 - normalizedDistance
copy.transform3D = CATransform3DMakeRotation(maxAngle * normalizedDistance, 0, 1, 0)

return copy
}

return attributes
}

override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
}

确保在添加 UICollectionView 后设置 UICollectionViewFlowLayout 参数:

guard let flow = collectionView.collectionViewLayout as? PaginatedCollectionViewFlow else { return }

/* Distance from the middle to the other side of the screen */
flow.availableDistance = floor(view.bounds.width / 2.0)

/* Middle of the screen */
flow.midX = ceil(view.bounds.midX)

/* Index of the last element in the collectionView */
flow.lastElementIndex = vm.numberOfItems - 1

/* Left and Right Insets */
flow.sectionInset.left = flow.midX - 30.0
flow.sectionInset.right = flow.midX - 30.0

最后在符合 UICollectionViewDelegate 以获取 UIScrollView 委托(delegate)方法之后:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
scrollToPosition(scrollView: scrollView)
}

func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
guard !decelerate else { return }

scrollToPosition(scrollView: scrollView)
}

internal func scrollToPosition(scrollView: UIScrollView) {
guard let ip = indexPathForCenterCell else { return }

scrollToIndex(ip.row, animated: true)
}

internal func scrollToIndex(_ index: Int, animated: Bool) {
let ip = IndexPath(item: index, section: 0)

guard let attributes = collectionView.layoutAttributesForItem(at: ip) else { return }

let halfWidth = collectionView.frame.width / CGFloat(2.0)
let offset = CGPoint(x: attributes.frame.midX - halfWidth, y: 0)

collectionView.setContentOffset(offset, animated: animated)

guard let cell = collectionView.cellForItem(at: ip) else { return }

feedbackGenerator.selectionChanged()
cell.isHighlighted = true
collectionView.visibleCells.filter { $0 != cell }.forEach { $0.isHighlighted = false }
}

internal var indexPathForCenterCell: IndexPath? {
let point = collectionView.convert(collectionView.center, from: collectionView.superview)

guard let indexPath = collectionView.indexPathForItem(at: point) else { return collectionView.indexPathsForVisibleItems.first }

return indexPath
}

/* Gets the CGSize based of a maximum size available for the provided String */
func sizeFor(text: String) -> CGSize {
guard let font = UIFont(font: .sanFranciscoSemiBold, size: 15.0) else { return .zero }

let textNS = text as NSString
let maxSize = CGSize(width: collectionView.frame.width / 2, height: collectionView.frame.height)
let frame = textNS.boundingRect(with: maxSize, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font : font], context: nil)

return frame.size
}

这提供了 UICollectionViewCells 的分页,同时将其“对齐”到最近的单元格,还使用 ​​UISelectionFeedbackGenerator 生成触觉反馈。希望这可以帮助遇到同样问题的人。

关于ios - UICollectionView水平分页布局,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55137218/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com