swift 根据可见性百分比缩放单元格

6qftjkof  于 2023-08-02  发布在  Swift
关注(0)|答案(1)|浏览(150)

当一个单元格在集合视图的中心完全可见时,我希望它看起来比侧面的单元格大。
目前,我使用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
    }
}

fykwrbwg

fykwrbwg1#

你很接近...
首先,不需要UIView.animate块。每个布局周期都会调用.visibleItemsInvalidationHandler,因此当我们滚动该部分时,它会(有效地)连续触发。
第二,你的身高比例计算不太正确。。
您将得到单元格框与视图框的交集(CGRect),因此我们希望高度比例与相交矩形宽度的百分比匹配。在这种情况下,0.81.0之间的范围的百分比。
如果您将.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 percent: CGFloat = inter.width / frame.width
            let scale = 0.8 + (0.2 * percent)
            item.transform = CGAffineTransform(scaleX: 0.98, y: scale)
        }
    }

字符串

相关问题