美文网首页Swift业务处理ui适配
UICollectionView为每一个section分区设置背

UICollectionView为每一个section分区设置背

作者: 北纬357 | 来源:发表于2020-07-16 16:18 被阅读0次

    《代码地址》

    截屏2020-07-16 下午3.27.12.png
    • 一些应用需要如图所示一样在集合视图上让不同分区配置背景图片或颜色。并且背景要占据分区头的高度之类操作,尤其是电商平台类别的应用。但是集合视图本身不支持直接设置背景图片,经过一番研究,可以通过重写UICollectionViewFlowLayout实现这个效果。


      IMG_0736.JPG
    • 自定义UICollectionReusableView类,用来注册section装饰背景View

    class SectionBackgroundReusableView: UICollectionReusableView {
    
        static let BACKGAROUND_CID = "BACKGAROUND_CID"
        
        private lazy var bg_imageView = UIImageView().then {
            addSubview($0)
        }
        
        override init(frame: CGRect) {
            super.init(frame: frame)
            
        }
    
        override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
            super.apply(layoutAttributes)
            bg_imageView.frame = bounds
            guard let att = layoutAttributes as? SectionDecorationViewCollectionViewLayoutAttributes else {
                return
            }
            self.backgroundColor = UIColor.clear
            bg_imageView.layer.cornerRadius = 5
            bg_imageView.clipsToBounds = true
            bg_imageView.backgroundColor = att.backgroundColor
            guard let imageName = att.imageName else {
                self.bg_imageView.image = nil
                return
            }
            guard let image_url = URL(string: imageName) else {
                return
            }
            self.bg_imageView.kf.setImage(with: image_url)
        }
        
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    
    • 自定义UICollectionViewLayoutAttributes,用来保存section的背景图片和颜色数据
    class SectionDecorationViewCollectionViewLayoutAttributes: UICollectionViewLayoutAttributes {
        
        // 装饰背景图片
        var imageName: String?
        // 背景色
        var backgroundColor = UIColor.white
    
        /// 所定义属性的类型需要遵从 NSCopying 协议
        /// - Parameter zone:
        /// - Returns:
        override func copy(with zone: NSZone? = nil) -> Any {
            let copy = super.copy(with: zone) as! SectionDecorationViewCollectionViewLayoutAttributes
            copy.imageName = self.imageName
            copy.backgroundColor = self.backgroundColor
            return copy
        }
        
        /// 所定义属性的类型还要实现相等判断方法(isEqual)
        /// - Parameter object:
        /// - Returns: 是否相等
        override func isEqual(_ object: Any?) -> Bool {
            guard let rhs = object as? SectionDecorationViewCollectionViewLayoutAttributes else {
                return false
            }
            if self.imageName != rhs.imageName {
                return false
            }
            if !self.backgroundColor.isEqual(rhs.backgroundColor) {
                return false
            }
            return super.isEqual(object)
        }
    }
    
    
    • 自定义UICollectionViewFlowLayout类
    1. 先注册背景View
        override init() {
            super.init()
            // 背景View注册
            self.register(SectionBackgroundReusableView.self, forDecorationViewOfKind: SectionBackgroundReusableView.BACKGAROUND_CID)
        }
    
    1. 设置背景的位置和大小
        // 布局配置数据
        override func prepare() {
            super.prepare()
            // 如果collectionView当前没有分区,则直接退出
            guard let numberOfSections = self.collectionView?.numberOfSections
                else {
                    return
            }
            // 不存在cardDecorationDelegate就退出
            guard let delegate = decorationDelegate else {
                return
            }
            if decorationBackgroundAttrs.count > 0 {
                decorationBackgroundAttrs.removeAll()
            }
            for section:Int in 0..<numberOfSections {
                // 获取该section下第一个,以及最后一个item的布局属性
                guard let numberOfItems = self.collectionView?.numberOfItems(inSection: section),
                    numberOfItems > 0,
                    let firstItem = self.layoutAttributesForItem(at:
                        IndexPath(item: 0, section: section)),
                    let lastItem = self.layoutAttributesForItem(at:
                        IndexPath(item: numberOfItems - 1, section: section))
                    else {
                        continue
                }
                var sectionInset:UIEdgeInsets = self.sectionInset
                /// 获取该section的内边距
                let inset:UIEdgeInsets = delegate.collectionView(collectionView: self.collectionView!, layout: self, insetForSectionAt: section)
                if !(inset == .zero) {
                    sectionInset = inset
                }
                
                /// 获取该section header的size
                let headerSize = delegate.collectionView(collectionView: self.collectionView!, layout: self, headerForSectionAt: section)
                var sectionFrame:CGRect = .zero
                if self.scrollDirection == .horizontal {
                    let hx = (firstItem.frame.origin.x) - headerSize.width + sectionInset.left
                    let hy = (firstItem.frame.origin.y) + sectionInset.top
                    let hw = ((lastItem.frame.origin.x) + (lastItem.frame.size.width)) - sectionInset.right
                    let hh = ((lastItem.frame.origin.y) + (lastItem.frame.size.height)) - sectionInset.bottom
                    sectionFrame = CGRect(x:  hx , y: hy, width: hw, height: hh)
                    sectionFrame.origin.y = sectionInset.top
                    sectionFrame.size.width = sectionFrame.size.width-sectionFrame.origin.x
                    sectionFrame.size.height = self.collectionView!.frame.size.height - sectionInset.top - sectionInset.bottom
                    
                } else {
                    let vx = (firstItem.frame.origin.x)
                    let vy = (firstItem.frame.origin.y) - headerSize.height + sectionInset.top
                    let vw = ((lastItem.frame.origin.x) + (lastItem.frame.size.width))
                    let vh = ( (lastItem.frame.origin.y) + (lastItem.frame.size.height) ) - sectionInset.bottom
                    sectionFrame = CGRect(x:  vx , y: vy, width: vw, height: vh + 10)
                    sectionFrame.origin.x = sectionInset.left
                    sectionFrame.size.width = (self.collectionView?.frame.size.width)! - sectionInset.left - sectionInset.right
                    sectionFrame.size.height = sectionFrame.size.height - sectionFrame.origin.y
                }
                
                let attrs = SectionDecorationViewCollectionViewLayoutAttributes(forDecorationViewOfKind: SectionBackgroundReusableView.BACKGAROUND_CID, with: IndexPath(item: 0, section: section))
                let backgroundColor = delegate.collectionView(self.collectionView!, layout: self, decorationColorForSectionAt: section)
                attrs.frame = sectionFrame
                attrs.zIndex = -1
                attrs.backgroundColor = backgroundColor
                /// 优先保存颜色
                self.decorationBackgroundAttrs[section] = attrs
                
                /// 判断背景图片是否可见 不可见跳过
                let backgroundDisplayed = delegate.collectionView(self.collectionView!, layout: self, decorationImgaeDisplayedForSectionAt: section)
                guard backgroundDisplayed == true else {
                    continue
                }
                ///  如果背景图片名称为nil,跳过
                guard let imageName = delegate.collectionView(self.collectionView!, layout: self, decorationImageForSectionAt: section) else {
                    continue
                }
                attrs.imageName = imageName
                
                
                let displayedFillet = delegate.collectionView(self.collectionView!, layout: self, filletDisplayedForSectionAt: section)
                guard displayedFillet == false else {
                    continue
                }
                // 将该section的布局属性保存起来
                self.decorationBackgroundAttrs[section] = attrs
            }
            
        }
    
    1. 这个方法比较重要,collectionView的分区可以设置背景这是重点,重写DecorationView。Decoration View是UICollectionView的装饰视图,这是苹果官方解释,
      // If the layout supports any supplementary or decoration view types, it should also implement the respective atIndexPath: methods for those types.
      //如果布局支持任何补充或装饰视图类型,则还应该为这些类型实现相应的atIndexPath:方法。
        override func layoutAttributesForDecorationView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
            let section = indexPath.section
            if elementKind  == SectionBackgroundReusableView.BACKGAROUND_CID {
                return self.decorationBackgroundAttrs[section]
            }
            return super.layoutAttributesForDecorationView(ofKind: elementKind,
                                                           at: indexPath)
        }
    
    1. 重要的代码都做了解释,如果刚好需要这个效果的,可以下载代码看看,再结合自身的需求进行修改。代码地址在前面已经贴出来了


      7a65dbe6e060de7e6629c85b06e4c82c.jpg

    相关文章

      网友评论

        本文标题:UICollectionView为每一个section分区设置背

        本文链接:https://www.haomeiwen.com/subject/opldictx.html