草庐IT

ios - UICollectionView 的弹性 header

coder 2023-09-10 原文

我正在尝试使用 collectionview 部分标题创建一个可伸缩标题。有很多不同的方法可以做到这一点,但我只是在寻找最简单、最直接的方法,并明确说明要做什么。有没有人看过关于在 Swift 3 中执行此操作的简单指南,或者您能解释一下如何在此处完成此操作吗?

我觉得应该没有那么难。我想使用 UICollectionViewDelegateFlowLayoutScrollviewDelegate 因为我认为这是完成它的最简单方法。

当用户滚动时,我可以使用 scrollViewDidScroll 方法来控制页眉的高度吗?我将如何手动更改标题的高度?我知道在 Storyboard 上我可以在 UICollectionView header 大小设置中更改它,但我将如何在代码中调整它?

最佳答案

使用 Swift 5/iOS 12.3,您可以覆盖 shouldInvalidateLayout(forBoundsChange:)layoutAttributesForElements(in:) UICollectionViewFlowLayout 子类中的方法,以便在 UICollectionView 中创建可伸缩 header 。以下示例代码展示了如何实现这些方法:

CollectionViewController.swift

import UIKit

class CollectionViewController: UICollectionViewController {

    let flowLayout = CustomFlowLayout()

    override func viewDidLoad() {
        super.viewDidLoad()

        guard let collectionView = collectionView else { fatalError() }

        collectionView.alwaysBounceVertical = true
        collectionView.contentInsetAdjustmentBehavior = .always
        collectionView.collectionViewLayout = flowLayout
        collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
        collectionView.register(HeaderReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "HeaderView")
    }

    override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "HeaderView", for: indexPath) as! HeaderReusableView
        return headerView
    }

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 20
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        return collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
    }

}

CustomFlowLayout.swift

import UIKit

class CustomFlowLayout: UICollectionViewFlowLayout {

    let idealCellWidth: CGFloat = 100
    let margin: CGFloat = 10

    override init() {
        super.init()

        sectionInsetReference = .fromSafeArea
        sectionInset = UIEdgeInsets(top: margin, left: margin, bottom: margin, right: margin)
        headerReferenceSize = CGSize(width: 0, height: 80)
        sectionHeadersPinToVisibleBounds = false
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func prepare() {
        super.prepare()

        guard let collectionView = collectionView else { return }
        let availableWidth = collectionView.frame.width - collectionView.safeAreaInsets.left - collectionView.safeAreaInsets.right - sectionInset.left - sectionInset.right
        let idealNumberOfCells = (availableWidth + minimumInteritemSpacing) / (idealCellWidth + minimumInteritemSpacing)
        let numberOfCells = idealNumberOfCells.rounded(.down)
        let cellWidth = (availableWidth + minimumInteritemSpacing) / numberOfCells - minimumInteritemSpacing
        itemSize = CGSize(width: cellWidth, height: cellWidth)
    }

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

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        guard let collectionView = collectionView else { return nil }
        guard let rectAttributes = super.layoutAttributesForElements(in: rect) else { return nil }

        let offsetY = collectionView.contentOffset.y + collectionView.safeAreaInsets.top
        if let firstHeader = rectAttributes.first(where: { $0.representedElementKind == UICollectionView.elementKindSectionHeader && offsetY < 0}) {
            let origin = CGPoint(x: firstHeader.frame.origin.x, y: firstHeader.frame.minY - offsetY.magnitude)
            let size = CGSize(width: firstHeader.frame.width, height: max(0, headerReferenceSize.height + offsetY.magnitude))
            firstHeader.frame = CGRect(origin: origin, size: size)
        }

        return rectAttributes
    }

}

CollectionViewCell.swift

import UIKit

class CollectionViewCell: UICollectionViewCell {

    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .cyan
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

HeaderReusableView.swift

import UIKit

class HeaderReusableView: UICollectionReusableView {

    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .magenta
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

预期结果:

关于ios - UICollectionView 的弹性 header ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44446059/

有关ios - UICollectionView 的弹性 header的更多相关文章

  1. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  2. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  3. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

  4. ruby-on-rails - Heroku 吃掉了我的自定义 HTTP header - 2

    我正在使用Heroku(heroku.com)来部署我的Rails应用程序,并且正在构建一个iPhone客户端来与之交互。我的目的是将手机的唯一设备标识符作为HTTPheader传递给应用程序以进行身份​​验证。当我在本地测试时,我的header通过得很好,但在Heroku上它似乎去掉了我的自定义header。我用ruby​​脚本验证:url=URI.parse('http://#{myapp}.heroku.com/')#url=URI.parse('http://localhost:3000/')req=Net::HTTP::Post.new(url.path)#boguspara

  5. ruby - 为什么不能使用类IO的实例方法noecho? - 2

    print"Enteryourpassword:"pass=STDIN.noecho(&:gets)puts"Yourpasswordis#{pass}!"输出:Enteryourpassword:input.rb:2:in`':undefinedmethod`noecho'for#>(NoMethodError) 最佳答案 一开始require'io/console'后来的Ruby1.9.3 关于ruby-为什么不能使用类IO的实例方法noecho?,我们在StackOverflow上

  6. ruby-on-rails - 使用 header 渲染 JSON - 2

    我想在我的Controller中使用以下corsheader呈现JSON:'Access-Control-Allow-Origin'='*'.我试过这个:defmy_actionrender(json:some_params)response.headers['Access-Control-Allow-Origin']='*'end但是我得到了一个AbstractController::DoubleRenderError。有没有办法使用header呈现JSON? 最佳答案 您不能在渲染后设置header,因为已发送响应。所以在没有意

  7. ruby-on-rails - Ruby:如何在 Ruby 中读取包含两个 header 的 CSV 文件? - 2

    我有一个“.CSV”文件,我正尝试在ruby​​中使用CSV对其进行解析。该文件虽然有两行标题,但我以前从未遇到过这种情况,也不知道如何处理。以下是标题和行的示例。第1行"InstitutionID","Institution","GameDate","UniformNumber","LastName","FirstName","Rushing","","","","","Passing","","","","","","TotalOff.","","Receiving","","","PassInt","","","FumbleRet","","","Punting","","Pun

  8. ruby - 从 Ruby 中的 CSV 文件获取 header 的最简单方法是什么? - 2

    我需要做的就是从CSV文件中获取header。file.csv是:"A","B","C""1","2","3"我的代码是:table=CSV.open("file.csv",:headers=>true)putstable.headerstable.eachdo|row|putsrowend这给了我:true"1","2","3"我已经查看RubyCSV文档几个小时了,这让我发疯。我确信必须有一个简单的单行代码可以将header返回给我。有什么想法吗? 最佳答案 看起来CSV.read会让您访问headers方法:headers=C

  9. ruby - 如何从 Rack 获取原始格式的请求 header ? - 2

    我正在尝试使用Ruby从Rack获取原始格式的请求header,但还没有弄清楚。我从request.env得到的散列不是我想要的。在该散列中,header键被大写并使用下划线而不是破折号,如下所示:"CONTENT_TYPE"=>"应用程序/json;字符集=utf-8"我想要的是处理前的header,我正在寻找:"Content_Type"=>"application/json;charset=utf-8"我可以很容易地循环遍历request.env寻找以HTTP_开头的header并将它们拆分,将每个单词和gsub大写以将下划线替换为破折号以使它们成为我想要的格式。在处理标题时,以

  10. ruby - 为 IO::popen 拯救 "command not found" - 2

    当我将IO::popen与不存在的命令一起使用时,我在屏幕上打印了一条错误消息:irb>IO.popen"fakefake"#=>#irb>(irb):1:commandnotfound:fakefake有什么方法可以捕获此错误,以便我可以在脚本中进行检查? 最佳答案 是:升级到ruby​​1.9。如果您在1.9中运行它,则会引发Errno::ENOENT,您将能够拯救它。(编辑)这是在1.8中的一种hackish方式:error=IO.pipe$stderr.reopenerror[1]pipe=IO.popen'qwe'#

随机推荐