Mosaic布局(类似于瀑布流)
发布日期:2022-03-18 08:27:47 浏览次数:23 分类:技术文章

本文共 7226 字,大约阅读时间需要 24 分钟。

在讲到了一个mosaic布局,类似于常见的瀑布流

最终的效果如下:

最终效果

Mosaic布局(类似于瀑布流)

文档摘录

官方文档内容

理解核心布局的过程

在布局处理的过程中,collectionview调用布局对象指定方法,其调用的顺序如下:

1.使用方法执行提供布局信息所需的前期计算

2.使用方法返回内容区域的大小
3.使用方法方法返回指定矩形中的单元格和视图的属性

这里写图片描述

创建布局属性

布局属性是的实例。使用如下的方法创建:

如果标准属性类不符合您的应用需求,则可以将其子类化并扩展,以存储有关每个视图的其他信息。当对布局属性进行子类化时,需要实现用于比较自定义属性的isEqual:方法,因为collectionview在其某些操作中使用此方法。另外还需要遵守NSCopying协议,实现copyWithZone:方法。

除了定义attributes子类之外,你的UICollectionReusableView对象还需要实现apply(_:)方法,以便它们可以在布局时应用任何自定义属性。

类中的包含的属性:

  • var frame: CGRect
  • var bounds: CGRect
  • var center: CGPoint
  • var size: CGSize
  • var transform3D: CATransform3D
  • var transform: CGAffineTransform
  • var alpha: CGFloat
  • var zIndex: Int
  • var isHidden: Bool

为给定矩形中的item提供布局属性

在布局过程的最后一步,集合视图调用您的布局对象的layoutAttributesForElementsInRect:方法。该方法的目的是为每个cell以及与指定矩形相交的每个supplementary或decoration视图提供布局属性。对于大的可滚动内容区域,集合视图可能只是要求当前可见的该内容区域的部分中的item的属性。

这里写图片描述

由于layoutAttributesForElementsInRect: 方法在prepareLayout方法后调用,所以你应该已拥有创建或return 所需attributes的信息。layoutAttributesForElementsInRect: 方法的实现遵循如下的步骤:

  1. 遍历由prepareLayout方法生成的数据,以访问缓存的attributes或创建新的attributes。
  2. 检查每个item的frame,看是否与layoutAttributesForElementsInRect:中的矩形相交
  3. 对于每个相交item,将相应的UICollectionViewLayoutAttributes对象添加到数组。
  4. 将布局属性数组返回

根据你如何管理布局信息,你可以在prepareLayout方法中创建UICollectionViewLayoutAttributes对象,或者等到在layoutAttributesForElementsInRect:方法中创建。但需要记住的是,缓存布局信息有许多的好处,为cell重复计算新的布局属性是一项昂贵的操作,可能会对应用程序的性能造成明显的不利影响。

根据需求提供布局的属性

collection view定期询问你的布局对象为正式布局流程之外的单个item提供属性。例如,在为项目配置插入和删除动画时,collection view会询问此信息。你的布局对象必须准备好为每个cell,supplementary view和decoration view提供布局属性。你可以通过覆盖以下方法来执行此操作:

每个自定义的布局类都被期望实现layoutAttributesForItemAtIndexPath:方法

Mosaic布局实现过程

为什么要自定义布局,而不使用UICollectionViewFlowLayout

UICollectionViewFlowLayout的行高按一行最大的item计算,其余的cell则居中,而且还要注意line space。所以自定义布局,继承自UICollectionViewLayout

自定义布局

类似于上一章中将的多列布局

在上面的效果图中,图片的高度和描述文字的高度是动态,可通过代理对象来获取

定义一个代理对象MosaicLayoutDelegate

protocol MosaicLayoutDelegate {  //image图片的高度  func collectionView(_ collectionView: UICollectionView, heightForImageAtIndexPath indexPath: IndexPath, withWidth width: CGFloat) -> CGFloat  //描述文字label的高度  func collectionView(_ collectionView: UICollectionView, heightForDescriptionAtIndexPath indexPath: IndexPath, withWidth width: CGFloat) -> CGFloat  }

另外需继承UICollectionViewLayoutAttributes,创建一个MosaicLayoutAttributes自定义属性,在其中添加一个变量imageHeight,来保存图片的动态高度

class MosaicLayoutAttributes: UICollectionViewLayoutAttributes {  //图片的高度  var imageHeight: CGFloat = 0  //重写copy方法  override func copy(with zone: NSZone?) -> Any {    let copy = super.copy(with: zone) as! MosaicLayoutAttributes    copy.imageHeight = imageHeight    return copy  }  //重写isEqual方法  override func isEqual(_ object: Any?) -> Bool {    if let attributes = object as? MosaicLayoutAttributes {      if attributes.imageHeight == imageHeight {        return super.isEqual(object)      }    }    return false  }}

有了图片高度,就要在定义的cell中改变图片高度的约束。

如下在RoundedCharacterCell中,重写apply(_:)方法,使用imageHeight

class RoundedCharacterCell: UICollectionViewCell {    @IBOutlet weak var characterImage: UIImageView!    @IBOutlet weak var characterTitle: UILabel!    @IBOutlet weak var characterInfo: UILabel!    //图片高度约束    @IBOutlet weak var imageViewHeightConstraint: NSLayoutConstraint!    var character: Characters? {        didSet {            if let theCharacter = character {                characterImage.image = UIImage(named: theCharacter.name)                characterTitle.text = theCharacter.title                characterInfo.text = theCharacter.description            }        }    }    required init?(coder aDecoder: NSCoder) {        super.init(coder: aDecoder)        self.layer.cornerRadius = 12.0        self.layer.borderWidth = 3        self.layer.borderColor = UIColor(red: 0.5, green: 0.47, blue: 0.25, alpha: 1.0).cgColor    }    override func prepareForReuse() {        super.prepareForReuse()        characterImage.image = nil        characterTitle.text = ""        characterInfo.text = ""    }    //重写apply方法    override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {        super.apply(layoutAttributes)        let attributes = layoutAttributes as! MosaicLayoutAttributes        imageViewHeightConstraint.constant = attributes.imageHeight    }}

在定义的布局MosaicViewLayout中,重写如下的方法和属性:

1.重写class var layoutAttributesClass,表示使用的attribute为MosaicLayoutAttributes类型
2.重写prepare()方法,做初始化计算
3.重写var collectionViewContentSize,返回内容区域的大小
4.重写layoutAttributesForElements(in rect: CGRect)方法

class MosaicViewLayout: UICollectionViewLayout {    //代理对象    var delegate: MosaicLayoutDelegate!    //几列    var numberOfColumns = 0    var cellPadding: CGFloat = 0    //保存计算的属性    var cache = [MosaicLayoutAttributes]()    //内容区域的高度    fileprivate var contentHeight: CGFloat = 0    //宽度    fileprivate var width: CGFloat {        get {            let insets = collectionView!.contentInset            return collectionView!.bounds.width - (insets.left + insets.right)        }    }    //内容区域的大小    override var collectionViewContentSize : CGSize {        return CGSize(width: width, height: contentHeight)    }    //表示attribute是MosaicLayoutAttributes类    override class var layoutAttributesClass : AnyClass {        return MosaicLayoutAttributes.self    }    //初始化计算    override func prepare() {        if cache.isEmpty {            let columnWidth = width / CGFloat(numberOfColumns)            var xOffsets = [CGFloat]()            for column in 0..
= (numberOfColumns - 1) ? 0 : column + 1 } } } override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { var layoutAttributes = [UICollectionViewLayoutAttributes]() for attributes in cache { if attributes.frame.intersects(rect) {//相交 layoutAttributes.append(attributes) } } return layoutAttributes }}

在控制器中设置layout的代理对象等其它属性

let layout = collectionViewLayout as! MosaicViewLayoutlayout.delegate = selflayout.numberOfColumns = 2layout.cellPadding = 5

实现MosaicLayoutDelegate方法,返回图片和文字的高度

在计算图片的高度时使用了AVFoundationAVMakeRect(aspectRatio: CGSize, insideRect boundingRect: CGRect)方法

extension MasterViewController: MosaicLayoutDelegate {  func collectionView(_ collectionView: UICollectionView, heightForImageAtIndexPath indexPath: IndexPath, withWidth width: CGFloat) -> CGFloat {    let character = charactersData[indexPath.item]    let image = UIImage(named: character.name)    let boundingRect = CGRect(x: 0, y: 0, width: width, height: CGFloat(MAXFLOAT))    let rect = AVMakeRect(aspectRatio: image!.size, insideRect: boundingRect)    return rect.height  }  func collectionView(_ collectionView: UICollectionView, heightForDescriptionAtIndexPath indexPath: IndexPath, withWidth width: CGFloat) -> CGFloat {    let character = charactersData[indexPath.item]    let descriptionHeight = heightForText(character.description, width: width-24)    let height = 4 + 17 + 4 + descriptionHeight + 12    return height  }    func heightForText(_ text: String, width: CGFloat) -> CGFloat {        let font = UIFont.systemFont(ofSize: 10)        let rect = NSString(string: text).boundingRect(with: CGSize(width: width, height: CGFloat(MAXFLOAT)), options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)        return ceil(rect.height)    }}

转载地址:https://windzen.blog.csdn.net/article/details/70230998 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:自定义UICollectionView布局
下一篇:Networking with URLSession二 上传&下载

发表评论

最新留言

很好
[***.229.124.182]2024年03月23日 04时35分34秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章