以编程方式将 collectionView 置于视图中

Programmatically placing collectionView in view

我想以编程方式在底部的导航栏、标签和选项卡栏之间放置一个 collectionView。

我不希望不同部分之间有任何间距,如何正确地将视图添加到视图中?最好的方法是什么?

import UIKit

private let reUseIdentifierCollectionView = "CollectionViewCell1"

class CVControllerViewController: UIViewController {


    var heightNavigationBarTop: CGFloat{
        get{
            let barHeight = self.navigationController?.navigationBar.frame.height ?? 0
            let statusBarHeight = UIApplication.shared.isStatusBarHidden ? CGFloat(0) : UIApplication.shared.statusBarFrame.height
            return barHeight + statusBarHeight
        }
    }
    //Here I am getting the SafeArea above the NavigationBar
    var topSafeArea: CGFloat{
        get{
            var topSafeArea: CGFloat
            if #available(iOS 11.0, *) {
                topSafeArea = view.safeAreaInsets.top
            } else {
                topSafeArea = topLayoutGuide.length

            }
            return topSafeArea
        }
    }

    var tabBarHeight: CGFloat{ get{ return (self.tabBarController?.tabBar.frame.height)! } }

    lazy var label: UILabel = {
        let lb = UILabel()
        lb.frame = CGRect(x: 0, y: heightNavigationBarTop + topSafeArea, width: view.frame.width, height: 50)
        lb.backgroundColor = .red
        return lb
    }()

    var collectionView: UICollectionView!
    var content = [Int: Any]()

    override func viewDidLoad() {
        super.viewDidLoad()
        loadContent()
        loadCollectionView()
        view.addSubview(label)
        view.addSubview(collectionView)
    }


    func loadCollectionView() {
        let layout = UICollectionViewFlowLayout()
        let frame = CGRect(x: 0, y: heightNavigationBarTop + label.frame.height + topSafeArea, width: self.view.frame.height, height: self.view.frame.height - (heightNavigationBarTop + label.frame.height + tabBarHeight + topSafeArea))
        collectionView = UICollectionView(frame: frame, collectionViewLayout: layout)
        collectionView.backgroundColor = .white
        collectionView.register(CoinCVCCell.self, forCellWithReuseIdentifier: reUseIdentifierCollectionView)
        if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
            flowLayout.scrollDirection = .horizontal
            flowLayout.minimumLineSpacing = 0
        }
        collectionView.delegate   = self
        collectionView.dataSource = self
    }

    func loadContent() {
        content[0] = Content(name: "Blau", color: .blue)
        content[1] = Content(name: "Grün", color: .green)
        content[2] = Content(name: "Gelb", color: .yellow)
        content[3] = Content(name: "brown", color: .brown)
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

}

extension CVControllerViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 4
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let outputCell: UICollectionViewCell
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reUseIdentifierCollectionView, for: indexPath) as! CoinCVCCell
        cell.content = (content[indexPath.item] as! Content)
        outputCell = cell
        return outputCell
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let width = view.frame.width
        let height = view.frame.height - (heightNavigationBarTop + label.frame.height + topSafeArea + tabBarHeight)

        let output = Utility.shared.CGSizeMake(width, height)
        return output
    }
}


class BaseCell: UICollectionViewCell {
    override init(frame: CGRect) {
        super .init(frame: frame)
        setUpViews()
    }
    func setUpViews() {

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

class CoinCVCCell: BaseCell {
    var content: Content? {
        didSet{
            backgroundColor = content?.color
        }
    }
}

class Content {
    var name: String?
    var color: UIColor?
    init(name: String, color: UIColor) {
        self.name = name
        self.color = color
    }
}

final class Utility: NSObject {
    private override init() { }
    static let shared = Utility()

    func CGRectMake(_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) -> CGRect {
        return CGRect(x: x, y: y, width: width, height: height)
    }

    func CGSizeMake( _ width:CGFloat, _ height:CGFloat) -> CGSize{
        return CGSize(width: width, height: height)
    }
}

我不明白为什么 collectionView 不适合 space,以及为什么会出现边距,因为我从视图的总高度中减去了视图上不同元素的高度。

编辑:

private let reUseIdentifierCollectionView = "CollectionViewCell1"

class CVControllerViewController: UIViewController {


    var heightNavigationBarTop: CGFloat{
        get{
            let barHeight = self.navigationController?.navigationBar.frame.height ?? 0
            let statusBarHeight = UIApplication.shared.isStatusBarHidden ? CGFloat(0) : UIApplication.shared.statusBarFrame.height
            return barHeight + statusBarHeight
        }
    }
    //Here I am getting the SafeArea above the NavigationBar
    var topSafeArea: CGFloat{
        get{
            var topSafeArea: CGFloat
            if #available(iOS 11.0, *) {
                topSafeArea = view.safeAreaInsets.top
            } else {
                topSafeArea = topLayoutGuide.length
            }
            return topSafeArea
        }
    }

    var tabBarHeight: CGFloat{ get{ return (self.tabBarController?.tabBar.frame.height)! } }

    lazy var label: UILabel = {
        let lb = UILabel()
        //lb.frame = CGRect(x: 0, y: heightNavigationBarTop + topSafeArea, width: view.frame.width, height: 50)
        lb.backgroundColor = .red
        return lb
    }()

    var collectionView: UICollectionView!
    var content = [Int: Any]()

    override func viewDidLoad() {
        super.viewDidLoad()
        loadContent()
        loadCollectionView()
        setLayout()
//        view.addSubview(label)
//        view.addSubview(collectionView)
    }

    func setLayout(){
        view.sv(label, collectionView)
        view.layout(
            heightNavigationBarTop,
            |-label-| ~ 80,
            0,
            |collectionView|,
            tabBarHeight
        )
    }

    func loadCollectionView() {
        let layout = UICollectionViewFlowLayout()
        let frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height)
        collectionView = UICollectionView(frame: frame, collectionViewLayout: layout)
        collectionView.backgroundColor = .white
        collectionView.register(CoinCVCCell.self, forCellWithReuseIdentifier: reUseIdentifierCollectionView)
        collectionView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
        if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
            flowLayout.scrollDirection = .horizontal
            flowLayout.minimumLineSpacing = 0
        }
        collectionView.delegate   = self
        collectionView.dataSource = self
    }


    func loadContent() {
        content[0] = Content(name: "Blau", color: .blue)
        content[1] = Content(name: "Grün", color: .green)
        content[2] = Content(name: "Gelb", color: .yellow)
        content[3] = Content(name: "brown", color: .brown)
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

}

extension CVControllerViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 4
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let outputCell: UICollectionViewCell
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reUseIdentifierCollectionView, for: indexPath) as! CoinCVCCell
        cell.content = (content[indexPath.item] as! Content)
        outputCell = cell
        return outputCell
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let width = view.frame.width
        let height = view.frame.height - (tabBarHeight + label.frame.height + heightNavigationBarTop)

        let output = Utility.shared.CGSizeMake(width, height)
        return output
    }
}

我正在使用 Stevia 框架,基本上 sv 所做的是设置子视图并将 "translatesAutoresizingMaskIntoConstraints" 设置为 false。 通过布局我正在设置约束,解决方案现在可以满足我的要求,但这是您推荐的方式吗?

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) ->

这里我还是用frame,有道理吗?

以编程方式: 设置 sectionInset 并使布局无效

在集合视图尺寸检查器中将底部和顶部的部分插入设置为0

ColectionView Section Inset

because I subtract the height of the different elements on the view from the total height of the view

但是您不知道视图的总高度。那就是问题所在。

您正在 viewDidLoad 中呼叫 loadCollectionView。那还为时过早,因为还没有正确的 frame。因此,您根据其他事物的 frame 计算集合视图的 frame 的所有计算都是不正确的。

您可以等到 viewDidLayoutSubviews 后再调用 loadCollectionView 来解决此问题,但请不要这样做。你根本不应该计算帧!相反,使用自动布局完成所有操作,让自动布局引擎为您完成布局。这样,您可以在 viewDidLoad 中进行构造,而无需任何 frame 值发挥作用。