CollectionView FlowLayout自定义单元格渲染问题

如何解决CollectionView FlowLayout自定义单元格渲染问题

我有一个带有自定义流程图和一个自定义collectionview单元(没有情节提要)的collectionview。自定义单元在背景视图上具有CAGradientLayer。从暂停状态或特征收集更改返回时,此层的渲染不正确(请参见图像:

enter image description here

),它应该是单元的完整宽度。 另外,当滚动到下面的屏幕外项目时,渐变层根本不会渲染吗?

旋转设备一次,或滚动即可解决问题... 我不确定这是否可以在自定义单元格类或collectionview viewcontroller中解决。重用问题? 任何帮助,不胜感激!

注意:ipad和iphone的通用应用程序也兼容分屏。

单元格类

class NormalProjectCell: UICollectionViewCell,SelfConfiguringProjectCell {
    //MARK: - Properties
    let titleLabel = ProjectTitleLabel(withTextAlignment: .center,andFont: UIFont.preferredFont(forTextStyle: .title3),andColor: .label)
    let lastEditedLabel = ProjectTitleLabel(withTextAlignment: .center,andFont: UIFont.preferredFont(forTextStyle: .caption1),andColor: .secondaryLabel)
    let imageView = ProjectImageView(frame: .zero)
    var stackView = UIStackView()
    var backgroundMaskedView = UIView()
    
    //MARK: - Init
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.layer.cornerRadius = 35
        
        let seperator = Separator(frame: .zero)
        
        stackView = UIStackView(arrangedSubviews: [seperator,titleLabel,lastEditedLabel,imageView])
        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.axis = .vertical
        stackView.distribution = .fillProportionally
        stackView.spacing = 5
        stackView.setCustomSpacing(10,after: lastEditedLabel)
        stackView.insertSubview(backgroundMaskedView,at: 0)
        contentView.addSubview(stackView)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    //MARK: - Layout
    override func layoutSubviews() {
        super.layoutSubviews()
        
        NSLayoutConstraint.activate([
            titleLabel.heightAnchor.constraint(greaterThanOrEqualToConstant: 20),stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),stackView.topAnchor.constraint(equalTo: contentView.topAnchor),stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
        ])
        
        backgroundMaskedView.translatesAutoresizingMaskIntoConstraints = false
        backgroundMaskedView.backgroundColor = .tertiarySystemBackground
        backgroundMaskedView.pinToEdges(of: stackView)
        
        let gradientMaskLayer = CAGradientLayer()
        gradientMaskLayer.frame = backgroundMaskedView.bounds
        gradientMaskLayer.colors = [UIColor.systemPurple.cgColor,UIColor.clear.cgColor]
        gradientMaskLayer.locations = [0,0.4]

        backgroundMaskedView.layer.mask = gradientMaskLayer
    }
    
    //MARK: - Configure
    func configure(with project: ProjectsController.Project) {
        titleLabel.text = project.title
        lastEditedLabel.text = project.lastEdited.customMediumToString
        
        imageView.image = Bundle.getProjectImage(project: project)
    }
}

和带有collectionView的viewcontroller:

class ProjectsViewController: UIViewController {
    //MARK: - Types
    enum Section: CaseIterable {
        case normal
    }
    
    //MARK: - Properties
    let projectsController = ProjectsController()
    
    var collectionView: UICollectionView!
    var dataSource: UICollectionViewDiffableDataSource<Section,Project>!
    
    var lastScrollPosition: CGFloat = 0
    var isSearching = false
    
    let searchController = UISearchController()
    
    //MARK: - ViewController Methods
    override func viewDidLoad() {
        super.viewDidLoad()
        
        configureViewController()
        configureSearchController()
        configureCollectionView()
        createDataSource()
        updateData(on: projectsController.filteredProjects())
        
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        if isSearching {
            isSearching.toggle()
            searchController.searchBar.text = ""
            searchController.resignFirstResponder()
        }
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        searchController.searchBar.searchTextField.attributedPlaceholder = NSAttributedString(string: "Title or details text ...",attributes: [NSAttributedString.Key.foregroundColor: UIColor.secondaryLabel])
    }
    
    
    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
        super.traitCollectionDidChange(previousTraitCollection)
        
        collectionView.collectionViewLayout = UICollectionView.createFlexibleFlowLayout(in: view)
    }
        
    //MARK: - DataSource
    func createDataSource() {
        dataSource = UICollectionViewDiffableDataSource<Section,Project>(collectionView: collectionView) { (collectionView,indexPath,project) in
                return self.configure(NormalProjectCell.self,with: project,for: indexPath)
        }
    }
    
    func updateData(on projects: [Project]) {
        var snapshot = NSDiffableDataSourceSnapshot<Section,Project>()
        snapshot.appendSections([Section.normal])
        snapshot.appendItems(projects)

        //apply() is safe to call from a background queue!
        self.dataSource.apply(snapshot,animatingDifferences: true)
    }
    
    ///Configure any type of cell that conforms to selfConfiguringProjectCell!
    func configure<T: SelfConfiguringProjectCell>(_ cellType: T.Type,with project: Project,for indexPath: IndexPath) -> T {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellType.reuseIdentifier,for: indexPath) as? T else {
            fatalError("Unable to dequeue \(cellType)")
        }
        
        cell.configure(with: project)
        return cell
    }
    
    //MARK: - Actions
    @objc func addButtonTapped() {
        let project = Project()
        let viewController = ProjectDetailsViewController(withProject: project)
        viewController.delegate = self
        navigationController?.pushViewController(viewController,animated: true)
    }
    
    @objc private func tapAndHoldCell(recognizer: UILongPressGestureRecognizer) {
        if recognizer.state == .ended {
            guard let indexPath = collectionView.indexPathForItem(at: recognizer.location(in: self.collectionView)),let project = dataSource?.itemIdentifier(for: indexPath) else {
                    return
            }
            
            let viewController = ProjectDetailsViewController(withProject: project)
            viewController.delegate = self
            navigationController?.pushViewController(viewController,animated: true)
        }
    }
    
    @objc private func swipeFromRightOnCell(recognizer: UISwipeGestureRecognizer) {
        if recognizer.state == .ended {
            guard let indexPath = collectionView.indexPathForItem(at: recognizer.location(in: self.collectionView)),let cell = collectionView.cellForItem(at: indexPath),let project = dataSource?.itemIdentifier(for: indexPath) else {
                    return
            }
            
            let overlay = ProjectCellDeletionOverlay(frame: CGRect(x: cell.bounds.width,y: 0,width: 0,height: cell.bounds.height))
            cell.addSubview(overlay)
            
            UIView.animate(withDuration: 0.70,animations: {
                overlay.backgroundColor = UIColor.red.withAlphaComponent(0.60)
                overlay.frame = CGRect(x: cell.bounds.width / 2,width: cell.bounds.width / 2,height: cell.bounds.height)
            }) { _ in
                self.presentProjectAlertOnMainThread(withTitle: "Delete this Project?",andMessage: "Are you sure?\nThis cannot be undone!\nAll associated notes will also be deleted!",andDismissButtonTitle: "Cancel",andConfirmButtonTitle: "Delete!",completion: { success in
                                                        if success {
                                                            UIView.animate(withDuration: 1.40,animations: {
                                                                overlay.frame = CGRect(x: 0,width: cell.bounds.width,height: cell.bounds.height)
                                                                cell.alpha = 0
                                                            }) { _ in
                                                                self.delete(project)
                                                                overlay.removeFromSuperview()
                                                            }
                                                        } else {
                                                            UIView.animate(withDuration: 1.5,animations: {
                                                                overlay.frame = CGRect(x: cell.bounds.width,height: cell.bounds.height)
                                                                overlay.alpha = 0
                                                            }) { _ in
                                                                overlay.removeFromSuperview()
                                                            }
                                                        }
                })
            }
        }
    }
    
    ///Will show an overlay view with help text on the app
    @objc private func showHelpView() {
        let helpViewController = AppHelpViewController(with: HelpViewDisplayTextFor.projects)
        helpViewController.modalTransitionStyle = .flipHorizontal
        helpViewController.modalPresentationStyle = .fullScreen
        present(helpViewController,animated: true)
    }
    
    ///Will show a menu with several options
    @objc private func showMenu() {
        
    }

    //MARK: - UI & Layout
    private func configureViewController() {
        view.backgroundColor = .systemPurple
        title = "Projects"
        
        navigationController?.navigationBar.prefersLargeTitles = false
        
        let menu =  UIBarButtonItem(image: ProjectImages.BarButton.menu,style: .plain,target: self,action: #selector(showMenu))
        let add =  UIBarButtonItem(barButtonSystemItem: .add,action: #selector(addButtonTapped))
        navigationItem.leftBarButtonItems = [menu,add]
        let questionMark = UIBarButtonItem(image: ProjectImages.BarButton.questionmark,action: #selector(showHelpView))
        navigationItem.rightBarButtonItem = questionMark
    }

    private func configureCollectionView() {
        collectionView = UICollectionView(frame: view.bounds,collectionViewLayout: UICollectionView.createFlexibleFlowLayout(in: view))
        collectionView.delegate = self
        collectionView.autoresizingMask = [.flexibleWidth,.flexibleHeight]
        collectionView.backgroundColor = .clear
        view.addSubview(collectionView)
        
        collectionView.register(NormalProjectCell.self,forCellWithReuseIdentifier: NormalProjectCell.reuseIdentifier)
        
        let tapAndHold = UILongPressGestureRecognizer(target: self,action: #selector(tapAndHoldCell))
        tapAndHold.minimumPressDuration = 0.3
        collectionView.addGestureRecognizer(tapAndHold)
        
        let swipeFromRight = UISwipeGestureRecognizer(target: self,action: #selector(swipeFromRightOnCell) )
        swipeFromRight.direction = UISwipeGestureRecognizer.Direction.left
        collectionView.addGestureRecognizer(swipeFromRight)
    }
    
    private func configureSearchController() {
        searchController.searchResultsUpdater = self
        searchController.obscuresBackgroundDuringPresentation = false
        navigationItem.searchController = searchController
        
        //CollectionView under searchbar fix ???
        searchController.extendedLayoutIncludesOpaqueBars = true
//        searchController.edgesForExtendedLayout = .top
    }
    
}

//MARK: - Ext CollectionView Delegate
extension ProjectsViewController: UICollectionViewDelegate  {
    
    func collectionView(_ collectionView: UICollectionView,didSelectItemAt indexPath: IndexPath) {
        guard let project = dataSource?.itemIdentifier(for: indexPath) else { return }
        ProjectsController.activeProject = project
        
        let loadingView = showLoadingView(for: project)

        let viewController = SplitOrFlipContainerController()
        UIView.animate(withDuration: 1.5,animations: {
            loadingView.alpha = 1
        }) { (complete) in
            self.dismiss(animated: false) {
                self.present(viewController,animated: false)
            }
        }
    }
}

//MARK: - Ext Search Results & Bar
extension ProjectsViewController: UISearchResultsUpdating {
    func updateSearchResults(for searchController: UISearchController) {
        guard let filter = searchController.searchBar.text,filter.isNotEmpty else {
            isSearching = false
            updateData(on: projectsController.filteredProjects())
            return
        }

        isSearching = true
        updateData(on: projectsController.filteredProjects(with: filter.lowercased()))
    }
    
    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        lastScrollPosition = scrollView.contentOffset.y
    }

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if lastScrollPosition < scrollView.contentOffset.y {
            navigationItem.hidesSearchBarWhenScrolling = true
        } else if lastScrollPosition > scrollView.contentOffset.y {
            navigationItem.hidesSearchBarWhenScrolling = false
        }
    }
}

//MARK: - ProjectHandler
extension ProjectsViewController: ProjectHandler {
    internal func save(_ project: Project,withImage image: UIImage?) {
        //call save and update the snapshot
        projectsController.save(project,withImage: image)
        updateData(on: projectsController.filteredProjects())
        collectionView.reloadData()
    }
    
    internal func delete(_ project: Project) {
        //call delete and update the snapshot
        projectsController.delete(project)
        updateData(on: projectsController.filteredProjects())
    }
}

以及流程布局:

extension UICollectionView {
    ///Flow layout with minimum 2 items across,with padding and spacing
    static func createFlexibleFlowLayout(in view: UIView) -> UICollectionViewFlowLayout {
        let width = view.bounds.width
        let padding: CGFloat
        let minimumItemSpacing: CGFloat
        let availableWidth: CGFloat
        let itemWidth: CGFloat
        
        if view.traitCollection.verticalSizeClass == .compact {
            print("//iPhones landscape")
            padding = 12
            minimumItemSpacing = 12
            availableWidth = width - (padding * 2) - (minimumItemSpacing * 3)
            itemWidth = availableWidth / 4
        } else if view.traitCollection.horizontalSizeClass == .compact && view.traitCollection.verticalSizeClass == .regular {
            print("//iPhones portrait")
            padding = 12
            minimumItemSpacing = 12
            availableWidth = width - (padding * 2) - (minimumItemSpacing)
            itemWidth = availableWidth / 2
        } else {
            print("//iPads")
            padding = 24
            minimumItemSpacing = 24
            availableWidth = width - (padding * 2) - (minimumItemSpacing * 3)
            itemWidth = availableWidth / 4
        }
        
        let flowLayout = UICollectionViewFlowLayout()
        flowLayout.sectionInset = UIEdgeInsets(top: padding,left: padding,bottom: padding,right: padding)
        flowLayout.itemSize = CGSize(width: itemWidth,height: itemWidth + 40)
        
        flowLayout.sectionHeadersPinToVisibleBounds = true
        
        return flowLayout
    }
}

解决方法

感谢HWS论坛上@nemecek_filip的帮助,我解决了它! 使用自定义渐变视图的不同渐变方法!

在此更改并工作的代码:

collectionView单元格:

//
//  NormalProjectCell.swift
//

import UIKit

class NormalProjectCell: UICollectionViewCell,SelfConfiguringProjectCell {
    //MARK: - Properties
    let titleLabel = ProjectTitleLabel(withTextAlignment: .center,andFont: UIFont.preferredFont(forTextStyle: .title3),andColor: .label)
    let lastEditedLabel = ProjectTitleLabel(withTextAlignment: .center,andFont: UIFont.preferredFont(forTextStyle: .caption1),andColor: .secondaryLabel)
    let imageView = ProjectImageView(frame: .zero)
    var stackView = UIStackView()
    var backgroundMaskedView = GradientView()
    
    //MARK: - Init
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.layer.cornerRadius = 35
        
        let seperator = Separator(frame: .zero)
        
        stackView = UIStackView(arrangedSubviews: [seperator,titleLabel,lastEditedLabel,imageView])
        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.axis = .vertical
        stackView.distribution = .fillProportionally
        stackView.spacing = 5
        stackView.setCustomSpacing(10,after: lastEditedLabel)
        stackView.insertSubview(backgroundMaskedView,at: 0)
        contentView.addSubview(stackView)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
        
    //MARK: - Layout
    override func layoutSubviews() {
        super.layoutSubviews()
        
        NSLayoutConstraint.activate([
            titleLabel.heightAnchor.constraint(greaterThanOrEqualToConstant: 20),stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),stackView.topAnchor.constraint(equalTo: contentView.topAnchor),stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
        ])
        
        backgroundMaskedView.translatesAutoresizingMaskIntoConstraints = false
        backgroundMaskedView.pinToEdges(of: stackView)
    }
    
    //MARK: - Configure
    func configure(with project: ProjectsController.Project) {
        titleLabel.text = project.title
        lastEditedLabel.text = project.lastEdited.customMediumToString
        
        imageView.image = Bundle.getProjectImage(project: project)
    }
}

和GradientView:

//
//  GradientView.swift
//

import UIKit

class GradientView: UIView {
    var topColor: UIColor = UIColor.tertiarySystemBackground
    var bottomColor: UIColor = UIColor.systemPurple

    override class var layerClass: AnyClass {
        return CAGradientLayer.self
    }

    override func layoutSubviews() {
        (layer as! CAGradientLayer).colors = [topColor.cgColor,bottomColor.cgColor]
        (layer as! CAGradientLayer).locations = [0.0,0.40]
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res