当一个单元格在集合视图的中心完全可见时,我希望它看起来比侧面的单元格大。
目前,我使用visibleItemsInvalidationHandler
闭包来实现这个效果:
section.visibleItemsInvalidationHandler = { (items, offset, environment) in
items.forEach { item in
let frame = item.frame
let rect = CGRect(x: offset.x, y: offset.y, width: environment.container.contentSize.width, height: frame.height)
let inter = rect.intersection(frame)
let ratio = (inter.width * inter.height) / (frame.width * frame.height)
let scale = ratio > 0.8 ? ratio : 0.8
UIView.animate(withDuration: 0.2) {
item.transform = CGAffineTransform(scaleX: 0.98, y: scale)
}
}
}
字符串
然而,我仍然没有得到我想要的结果。这就是我得到的:
的数据
当滑动时,单元格开始 Flink ,当我试图将一个单元格移到中心时,max(ratio, scale)
的结果值小于之前的值。我希望max(ratio, scale)
的值增加,单元格在屏幕上可见越多。
理想情况下,这就是我的目标:
的
MRE:ViewController,枚举和结构:
struct BannerEntity: Hashable {
private let id = UUID()
let bannerTitle: String
init(bannerTitle: String) {
self.bannerTitle = bannerTitle
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
static func == (lhs: BannerEntity,
rhs: BannerEntity) -> Bool {
return lhs.id == rhs.id
}
}
struct HomePresentationModel {
var section: HomeSection
var items: [HomeSectionItem]
}
enum HomeSectionItem: Hashable {
case banner(BannerEntity)
}
struct HomeSection: Hashable {
var header: HomeSectionHeader
let sectionType: HomeSectionType
}
enum HomeSectionType: Int, Hashable {
case banner
}
enum HomeSectionHeader: Int, Hashable {
case tappableHeader
case empty
}
class ViewController: UIViewController {
// MARK: Subviews
private var collectionView: UICollectionView!
// MARK: Properties
private lazy var dataSource = makeDataSource()
private var sections = [HomePresentationModel(section: .init(header: .empty, sectionType: .banner),
items: [.banner(.init(bannerTitle: "firstBanner")),
.banner(.init(bannerTitle: "secondBanner")),
.banner(.init(bannerTitle: "thirdBanner")),
.banner(.init(bannerTitle: "fourthBanner"))]
)]
// MARK: Value Type
typealias DataSource = UICollectionViewDiffableDataSource<
HomeSection,
HomeSectionItem
>
typealias Snapshot = NSDiffableDataSourceSnapshot<
HomeSection,
HomeSectionItem
>
// MARK: Viewcycle
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
configureCollectionView()
applySnapshot()
}
// MARK: Helpers
private func configureCollectionView(){
collectionView = UICollectionView(frame: view.frame,
collectionViewLayout: generateLayout())
view.addSubview(collectionView)
collectionView.showsHorizontalScrollIndicator = false
collectionView.register(BannerCell.self,
forCellWithReuseIdentifier: "BannerCell")
}
private func generateLayout() -> UICollectionViewLayout {
let layout = UICollectionViewCompositionalLayout {
[self] (sectionIndex: Int,
layoutEnvironment: NSCollectionLayoutEnvironment)
-> NSCollectionLayoutSection? in
let sectionType = HomeSectionType(rawValue: sectionIndex)
guard sectionType == .banner else {return nil}
return self.generateBannersLayout()
}
return layout
}
func generateBannersLayout() -> NSCollectionLayoutSection {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1),
heightDimension: .fractionalHeight(1))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.9),
heightDimension: .fractionalHeight(0.28))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize,
subitems: [item])
group.contentInsets = NSDirectionalEdgeInsets(top: 0,
leading: 0,
bottom: 0,
trailing: 0)
group.interItemSpacing = NSCollectionLayoutSpacing.fixed(0)
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: 10,
leading: 0,
bottom: 10,
trailing: 0)
section.orthogonalScrollingBehavior = .groupPagingCentered
section.visibleItemsInvalidationHandler = { (items, offset, environment) in
items.forEach { item in
let frame = item.frame
let rect = CGRect(x: offset.x, y: offset.y, width: environment.container.contentSize.width, height: frame.height)
let inter = rect.intersection(frame)
let ratio = (inter.width * inter.height) / (frame.width * frame.height)
let scale = ratio > 0.8 ? ratio : 0.8
UIView.animate(withDuration: 0.2) {
item.transform = CGAffineTransform(scaleX: 0.98, y: scale)
}
}
}
return section
}
private func makeDataSource() -> DataSource {
let dataSource = DataSource(
collectionView: collectionView,
cellProvider: { (collectionView, indexPath, item) ->
UICollectionViewCell? in
let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: "BannerCell",
for: indexPath) as? BannerCell
return cell
})
return dataSource
}
private func applySnapshot() {
var snapshot = Snapshot()
snapshot.appendSections(sections.map({$0.section}))
sections.forEach { section in
snapshot.appendItems(section.items, toSection: section.section)
}
dataSource.apply(snapshot, animatingDifferences: false)
}
}
型
BannerCell:
import UIKit
class BannerCell: UICollectionViewCell {
// MARK: Init
override init(frame: CGRect) {
super.init(frame: frame)
setupSubviews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: Helpers
private func setupSubviews(){
backgroundColor = .purple
layer.cornerRadius = 10
}
}
型
1条答案
按热度按时间fykwrbwg1#
你很接近...
首先,不需要
UIView.animate
块。每个布局周期都会调用.visibleItemsInvalidationHandler
,因此当我们滚动该部分时,它会(有效地)连续触发。第二,你的身高比例计算不太正确。。
您将得到单元格框与视图框的交集(
CGRect
),因此我们希望高度比例与相交矩形宽度的百分比匹配。在这种情况下,0.8
和1.0
之间的范围的百分比。如果您将
.visibleItemsInvalidationHandler
更改为以下内容,它应该是您想要的-或者至少足够接近,您可以调整它以满足您的需求:字符串