I want to move the position of a UIView boardGame: UICollectionView
to be on the center of the screen by modifying the constraints properties such as topAnchor
, but anything I do to it seems to affect the content inside of it. I have also defined a UICollectionViewCell
model so I can be register the cells in boardGame
as "uinique".
import UIKit
class BoardViewController: UIViewController {
private let boardGame: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let board = UICollectionView(frame: .zero, collectionViewLayout: layout)
board.allowsSelection = true
board.register(CustomCollectionViewCell.self, forCellWithReuseIdentifier: "unique")
board.translatesAutoresizingMaskIntoConstraints = false
return board
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(boardGame)
boardGame.backgroundColor = .systemGray2
boardGame.frame = view.bounds
let margin: CGFloat = 20
NSLayoutConstraint.activate([
boardGame.topAnchor.constraint(equalTo: view.topAnchor, constant: margin),
boardGame.centerXAnchor.constraint(equalTo: view.centerXAnchor),
boardGame.centerYAnchor.constraint(equalTo: view.centerYAnchor),
boardGame.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -20),
boardGame.heightAnchor.constraint(equalTo: view.widthAnchor, constant: -20),
])
boardGame.delegate = self
boardGame.dataSource = self
}
init() {
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension BoardViewController: UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 9
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "unique", for: indexPath) as! CustomCollectionViewCell
let chessRow = indexPath.row / 3
if chessRow % 2 == 0 {
if indexPath.row % 2 == 0 {
cell.backgroundColor = UIColor.white
}else{
cell.backgroundColor = UIColor.black
}
} else{
if indexPath.row % 2 == 0 {
cell.backgroundColor = UIColor.black
}else{
cell.backgroundColor = UIColor.white
}
}
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// Divide the width by 3 to create a 3x3 grid
let width = collectionView.frame.size.width / 4.0
let height = width
return CGSize(width: width, height: height)
}
}
Here is the CustomCollectionViewCell: UICollectionViewCell
model:
class CustomCollectionViewCell: UICollectionViewCell {
let titleLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(titleLabel)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
titleLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And the parent view that receives the instance of the BoardViewController
import UIKit
class MainViewController: UIViewController {
let boardView = BoardViewController()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .blue
addBoardView()
}
func addBoardView(){
addChild(boardView)
view.addSubview(boardView.view)
boardView.didMove(toParent: self)
}
}
I have tried playing around with the properties of the constant: CGFloat
in topAnchor
in boardGame
. To give some context here is an example of what I get with boardGame.topAnchor.constraint(equalTo: view.topAnchor, constant: 20)
,
If I change the CGFloat
to 200 to make it more centered the properties of the CustomCollectionViewCell
inside it moved to the edged afecting the top margin like so:
I already tried to mess with the titleLabel
top margin constraint
inside of CustomCollectionViewCell
but does not seem to do anything.
How could I keep the boardGame
view in the center without affecting the margin of CustomCollectionViewCell
?
The cells are positioned at the top edge of the collection view, because that's how collection views work. This is by design. The cells only appear "centred", when the collection view is at the top of the screen, because the cells are laid out to avoid the safe area (the dynamic island in this case) by default.
In any case, your constraints are contradictory. You are saying that the boardGame
view should have a fixed width and height (assuming the superview's width and height are fixed), and its centre point should be the same as the superview's centre point (centerX = view.centerX
and centerY = view.centerY
), and its top is only 20 points away from the superview's top.
These cannot all be satisfied simultaneously. As you can see in the first screenshot, the centerY = view.centerY
constraint is broken, to satisfy the other constraints.
If you want the collection view to be centred both vertically and horizontally, just remove the top margin constraint:
boardGame.topAnchor.constraint(equalTo: view.topAnchor, constant: margin)
If you also want the cells to be centred vertically, you can set contentOffset
:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
boardGame.contentOffset.y = (boardGame.contentSize.height - boardGame.bounds.height) / 2
}
That said, if boardGame
is not supposed to be scrollable, consider using a series of UIStackView
s for this layout instead. Example:
class BoardViewController: UIViewController {
var boardGame: UIView!
override func viewDidLoad() {
super.viewDidLoad()
func makeCell(color: UIColor) -> Cell {
let cell = Cell(frame: .zero)
cell.backgroundColor = color
return cell
}
func makeColumn(_ views: [UIView]) -> UIStackView {
let col = UIStackView(arrangedSubviews: views)
col.axis = .vertical
col.spacing = 8
col.alignment = .center
col.distribution = .fillEqually
return col
}
let col1 = makeColumn([makeCell(color: .black), makeCell(color: .black), makeCell(color: .black)])
let col2 = makeColumn([makeCell(color: .white), makeCell(color: .white), makeCell(color: .white)])
let col3 = makeColumn([makeCell(color: .black), makeCell(color: .black), makeCell(color: .black)])
let columns = UIStackView(arrangedSubviews: [col1, col2, col3])
columns.axis = .horizontal
columns.spacing = 16
columns.distribution = .fillEqually
columns.translatesAutoresizingMaskIntoConstraints = false
boardGame = UIView()
boardGame.backgroundColor = .gray
boardGame.addSubview(columns)
boardGame.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(boardGame)
NSLayoutConstraint.activate([
columns.topAnchor.constraint(equalTo: boardGame.topAnchor, constant: 20),
columns.bottomAnchor.constraint(equalTo: boardGame.bottomAnchor, constant: -20),
columns.leftAnchor.constraint(equalTo: boardGame.leftAnchor, constant: 8),
columns.rightAnchor.constraint(equalTo: boardGame.rightAnchor, constant: -8),
boardGame.centerXAnchor.constraint(equalTo: view.centerXAnchor),
boardGame.centerYAnchor.constraint(equalTo: view.centerYAnchor),
boardGame.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -20),
boardGame.heightAnchor.constraint(equalTo: view.widthAnchor, constant: -20),
])
}
}
class Cell: UIView {
let titleLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(titleLabel)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
titleLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
titleLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
titleLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
self.translatesAutoresizingMaskIntoConstraints = false
self.widthAnchor.constraint(equalTo: self.heightAnchor).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}