问题
I want to place a collectionView programmatically between a navigationBar, a label and the tabBar on the bottom.
I do not want to have any spacing between the different parts, how do I correctly ad the views to the view? Whats the best way to do it?
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)
}
}
I do not understand why the collectionView does not fit in to the space, and why the margins occur, because I subtract the height of the different elements on the view from the total height of the view.
EDIT:
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
}
}
I am using the framework Stevia, basically what sv does is setting an subview and setting "translatesAutoresizingMaskIntoConstraints" to false. With layout I am setting the constraints, the solution now does what I want but is that the way you recommended?
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) ->
Here I still use the frame, does that make sense?
回答1:
because I subtract the height of the different elements on the view from the total height of the view
But you don’t know the total height of the view. That’s the problem.
You are calling loadCollectionView
in viewDidLoad
. That is way too early, because nothing has its correct frame
yet. Therefore all your calculations to calculate the frame
of the collection view, based on the frame
of other things, are incorrect.
You could solve this by waiting until viewDidLayoutSubviews
to call loadCollectionView
, but don't. You shouldn't be calculating frames at all! Instead, do everything with autolayout and let the autolayout engine do the layout for you. That way, you can do the construction in viewDidLoad
without any frame
values coming into play.
回答2:
Programmatically : set sectionInset and invalidate layout
https://stackoverflow.com/a/35975792/6948184
OR
set the section insets to 0 for Bottom and Top in the size inspector of your colection view
ColectionView Section Inset
来源:https://stackoverflow.com/questions/49742156/programmatically-placing-collectionview-in-view