代码之家  ›  专栏  ›  技术社区  ›  Munib

删除每个uiCollectionViewCell之间的间距并将其移到uiCollectionView的外部单元格

  •  1
  • Munib  · 技术社区  · 7 年前

    如果我的问题标题不清楚,但本质上我想制作一个uiCollectionView,如下面的媒体应用程序:

    Medium iOS App Intro

    我做了一个uiCollectionView,到目前为止它是这样的: enter image description here

    我想减少每个单元格之间的间距(红线),以便它们更靠近,并且在CollectionView的两侧和边界上的单元格之间有空间。我用过 minimumInteritemSpacing minimumLineSpacing 但它们对红色空间根本没有影响。

    这是我的代码:

    import UIKit
    
    class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    
        let data = ["Autos", "Cleaning", "Technology", "Business", "Sports", "Childcare", "Airsoft", "Cycling", "Fitness", "Baseball", "Basketball", "Bird Watching", "Bodybuilding", "Camping", "Dowsing", "Driving", "Fishing", "Flying", "Flying Disc", "Foraging", "Freestyle Football", "Gardeing", "Geocaching", "Ghost hunting", "Grafitti", "Handball", "High-power rocketry", "Hooping", "Horseback riding", "Hunting"]
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
    
            let layout = UICollectionViewFlowLayout.init()
            layout.scrollDirection = .vertical
            layout.minimumInteritemSpacing = 5.0
            layout.minimumLineSpacing = 20.0
    
            let collectionView = UICollectionView.init(frame: self.view.bounds, collectionViewLayout: layout)
    
            collectionView.dataSource = self
            collectionView.delegate = self
    
            collectionView.register(collectionViewCell.self, forCellWithReuseIdentifier: "cell")
            collectionView.backgroundColor = .white
            self.view.addSubview(collectionView)
    
            collectionView.translatesAutoresizingMaskIntoConstraints = false
            collectionView.topAnchor.constraint(equalTo: self.view.layoutMarginsGuide.topAnchor).isActive = true
            collectionView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 18.0).isActive = true
            collectionView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -18.0).isActive = true
            collectionView.bottomAnchor.constraint(equalTo: self.view.layoutMarginsGuide.bottomAnchor).isActive = true
    
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return data.count
        }
    
        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! collectionViewCell
    
            cell.textLabel.text = data[indexPath.row]
            return cell
    
        }
    
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    
            let messageText = data[indexPath.row]
            let size = CGSize.init(width: collectionView.frame.size.width, height: 1000)
            let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
    
            let estimatedFrame = NSString.init(string: messageText).boundingRect(with: size, options: options, attributes: [NSAttributedStringKey.font : UIFont.systemFont(ofSize: 15.0, weight: .regular)], context: nil)
    
            return CGSize.init(width: estimatedFrame.width + 20.0, height: estimatedFrame.height + 20.0)
        }
    
    
    }
    
    
    class collectionViewCell: UICollectionViewCell {
    
        var textLabel: UILabel = {
            let label = UILabel.init()
            label.font = UIFont.systemFont(ofSize: 15.0, weight: .regular)
            label.textColor = UIColor.black
            label.textAlignment = .center
            return label
        }()
    
        override init(frame: CGRect) {
            super.init(frame: frame)
    
            self.contentView.addSubview(textLabel)
    
            textLabel.translatesAutoresizingMaskIntoConstraints = false
            textLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
            textLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
    
    
            setupShadow()
    
        }
    
        func setupShadow() {
    
            self.contentView.backgroundColor = .white
            self.contentView.layer.cornerRadius = 2.0
            self.contentView.clipsToBounds = true
    
            let shadowSize : CGFloat = 1.0
            let shadowPath = UIBezierPath(rect: CGRect(x: -shadowSize / 2,
                                                       y: -shadowSize / 2,
                                                       width: self.contentView.frame.size.width + shadowSize,
                                                       height: self.contentView.frame.size.height + shadowSize))
            self.contentView.layer.masksToBounds = false
            self.contentView.layer.shadowColor = UIColor.black.cgColor
            self.contentView.layer.shadowOffset = CGSize(width: 0.0, height: 0.0)
            self.contentView.layer.shadowOpacity = 0.5
            self.contentView.layer.shadowPath = shadowPath.cgPath
        }
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    
    1 回复  |  直到 7 年前
        1
  •  1
  •   AamirR    7 年前

    这可以通过自定义布局实现,子类来自 UICollectionViewFlowLayout . 这是一个布局和演示集合视图/单元实现。

    更新 视图控制器.swift

    class ViewController: UIViewController {
    
        private var collectionView: UICollectionView!
        private let demoLabel = UILabel()
        private let minCellSpacing: CGFloat = 16.0
        private var maxCellWidth: CGFloat!
    
        var data: [String] = ["Tech", "Design", "Humor", "Travel", "Music", "Writing", "Social Media", "Life", "Education", "Edtech", "Education Reform", "Photography", "Startup", "Poetry", "Women In Tech", "Female Founders", "Business", "Fiction", "Love", "Food", "Sports", "Autos", "Cleaning", "Technology", "Business", "Sports", "Childcare", "Airsoft", "Cycling", "Fitness", "Baseball", "Basketball", "Bird Watching", "Bodybuilding", "Camping", "Dowsing", "Driving", "Fishing", "Flying", "Flying Disc", "Foraging", "Freestyle Football", "Gardeing", "Geocaching", "Ghost hunting", "Grafitti", "Handball", "High-power rocketry", "Hooping", "Horseback riding", "Hunting"]
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            self.maxCellWidth = UIScreen.main.bounds.width - (minCellSpacing * 2)
    
            self.view.backgroundColor = .white
            self.demoLabel.font = CollectionViewCell().label.font
    
            let layout = FlowLayout()
            layout.sectionInset = UIEdgeInsets(top: self.minCellSpacing, left: 2.0, bottom: self.minCellSpacing, right: 2.0)
            layout.minimumInteritemSpacing = self.minCellSpacing
            layout.minimumLineSpacing = 16.0
    
            collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout)
            collectionView.backgroundColor = .clear
            collectionView.delegate = self
            collectionView.dataSource = self
            collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "cellId")
            collectionView.translatesAutoresizingMaskIntoConstraints = false
            self.view.addSubview(collectionView)
            collectionView.topAnchor.constraint(equalTo: self.view.layoutMarginsGuide.topAnchor).isActive = true
            collectionView.bottomAnchor.constraint(equalTo: self.view.layoutMarginsGuide.bottomAnchor).isActive = true
    
            // Leading/Trailing gutter CellSpacing+ShadowWidth
            collectionView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: minCellSpacing + layout.sectionInset.left).isActive = true
            collectionView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -(minCellSpacing + layout.sectionInset.right)).isActive = true
        }
    }
    
    extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return self.data.count
        }
    
        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! CollectionViewCell
            cell.label.text = self.data[indexPath.item]
            return cell
        }
    
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
            self.demoLabel.text = self.data[indexPath.item]
            self.demoLabel.sizeToFit()
            return CGSize(width: min(self.demoLabel.frame.width + 16, self.maxCellWidth), height: 36.0)
        }
    
    }
    

    流程布局.swift

    class FlowLayout: UICollectionViewFlowLayout {
    
        private var attribs = [IndexPath: UICollectionViewLayoutAttributes]()
    
        override func prepare() {
            self.attribs.removeAll()
        }
    
        override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
            var updatedAttributes = [UICollectionViewLayoutAttributes]()
    
            let sections = self.collectionView?.numberOfSections ?? 0
            var indexPath = IndexPath(item: 0, section: 0)
            while (indexPath.section < sections) {
                guard let items = self.collectionView?.numberOfItems(inSection: indexPath.section) else { continue }
    
                while (indexPath.item < items) {
                    if let attributes = layoutAttributesForItem(at: indexPath), attributes.frame.intersects(rect) {
                        updatedAttributes.append(attributes)
                    }
    
                    let headerKind = UICollectionElementKindSectionHeader
                    if let headerAttributes = layoutAttributesForSupplementaryView(ofKind: headerKind, at: indexPath) {
                        updatedAttributes.append(headerAttributes)
                    }
    
                    let footerKind = UICollectionElementKindSectionFooter
                    if let footerAttributes = layoutAttributesForSupplementaryView(ofKind: footerKind, at: indexPath) {
                        updatedAttributes.append(footerAttributes)
                    }
                    indexPath.item += 1
                }
                indexPath = IndexPath(item: 0, section: indexPath.section + 1)
            }
    
            return updatedAttributes
        }
    
        override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
            if let attributes = attribs[indexPath] {
                return attributes
            }
    
            var rowCells = [UICollectionViewLayoutAttributes]()
            var collectionViewWidth: CGFloat = 0
            if let collectionView = collectionView {
                collectionViewWidth = collectionView.bounds.width - collectionView.contentInset.left
                    - collectionView.contentInset.right
            }
    
            var rowTestFrame: CGRect = super.layoutAttributesForItem(at: indexPath)?.frame ?? .zero
            rowTestFrame.origin.x = 0
            rowTestFrame.size.width = collectionViewWidth
    
            let totalRows = self.collectionView?.numberOfItems(inSection: indexPath.section) ?? 0
    
            // From this item, work backwards to find the first item in the row
            // Decrement the row index until a) we get to 0, b) we reach a previous row
            var startIndex = indexPath.row
            while true {
                let lastIndex = startIndex - 1
    
                if lastIndex < 0 {
                    break
                }
    
                let prevPath = IndexPath(row: lastIndex, section: indexPath.section)
                let prevFrame: CGRect = super.layoutAttributesForItem(at: prevPath)?.frame ?? .zero
    
                // If the item intersects the test frame, it's in the same row
                if prevFrame.intersects(rowTestFrame) {
                    startIndex = lastIndex
                } else {
                    // Found previous row, escape!
                    break
                }
            }
    
            // Now, work back UP to find the last item in the row
            // For each item in the row, add it's attributes to rowCells
            var cellIndex = startIndex
            while cellIndex < totalRows {
                let cellPath = IndexPath(row: cellIndex, section: indexPath.section)
    
                if let cellAttributes = super.layoutAttributesForItem(at: cellPath),
                    cellAttributes.frame.intersects(rowTestFrame),
                    let cellAttributesCopy = cellAttributes.copy() as? UICollectionViewLayoutAttributes {
                    rowCells.append(cellAttributesCopy)
                    cellIndex += 1
                } else {
                    break
                }
            }
    
            let flowDelegate = self.collectionView?.delegate as? UICollectionViewDelegateFlowLayout
            let selector = #selector(UICollectionViewDelegateFlowLayout.collectionView(_:layout:minimumInteritemSpacingForSectionAt:))
            let delegateSupportsInteritemSpacing = flowDelegate?.responds(to: selector) ?? false
    
            var interitemSpacing = minimumInteritemSpacing
    
            // Check for minimumInteritemSpacingForSectionAtIndex support
            if let collectionView = collectionView, delegateSupportsInteritemSpacing && rowCells.count > 0 {
                interitemSpacing = flowDelegate?.collectionView?(collectionView,
                                                                 layout: self,
                                                                 minimumInteritemSpacingForSectionAt: indexPath.section) ?? 0
            }
    
            let aggregateInteritemSpacing = interitemSpacing * CGFloat(rowCells.count - 1)
    
            var aggregateItemWidths: CGFloat = 0
            for itemAttributes in rowCells {
                aggregateItemWidths += itemAttributes.frame.width
            }
    
            let alignmentWidth = aggregateItemWidths + aggregateInteritemSpacing
            let alignmentXOffset: CGFloat = (collectionViewWidth - alignmentWidth) / 2
    
            var previousFrame: CGRect = .zero
            for itemAttributes in rowCells {
                var itemFrame = itemAttributes.frame
    
                if previousFrame.equalTo(.zero) {
                    itemFrame.origin.x = alignmentXOffset
                } else {
                    itemFrame.origin.x = previousFrame.maxX + interitemSpacing
                }
    
                itemAttributes.frame = itemFrame
                previousFrame = itemFrame
    
                attribs[itemAttributes.indexPath] = itemAttributes
            }
    
            return attribs[indexPath]
        }
    }
    

    集合视图单元格.swift

    class CollectionViewCell: UICollectionViewCell {
    
        let label = UILabel()
    
        override init(frame: CGRect) {
            super.init(frame: frame)
    
            self.label.translatesAutoresizingMaskIntoConstraints = false
            self.contentView.addSubview(label)
            self.contentView.addConstraints([
                NSLayoutConstraint(item: label, attribute: .leading, relatedBy: .equal, toItem: contentView,
                                   attribute: .leading, multiplier: 1.0, constant: 8.0),
                NSLayoutConstraint(item: label, attribute: .top, relatedBy: .equal, toItem: contentView,
                                   attribute: .top, multiplier: 1.0, constant: 8.0),
                NSLayoutConstraint(item: contentView, attribute: .trailing, relatedBy: .equal, toItem: label,
                                   attribute: .trailing, multiplier: 1.0, constant: 8.0),
                NSLayoutConstraint(item: contentView, attribute: .bottom, relatedBy: .equal, toItem: label,
                                   attribute: .bottom, multiplier: 1.0, constant: 8.0)])
    
            self.backgroundColor = .white
            self.label.textColor = UIColor(red: 0.1, green: 0.1, blue: 0.1, alpha: 1)
            self.layer.cornerRadius = 3.0
            self.layer.shadowColor = UIColor.darkGray.cgColor
            self.layer.shadowOffset = CGSize(width: 0.1, height: 0.2)
            self.layer.shadowOpacity = 0.28
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    

    这是最后的用户界面

    enter image description here

    推荐文章