Hello all!
I'm pretty new to iOS development and have been mostly working with Interface Builder. However, I've recently started learning how to create UI elements programmatically but I'm have found very few tutorials on doing things without IB.
I was able to piece together the code at the bottom of this post using YouTube tutorials but, while it compiles and runs, I have a few questions about how things work and why:
1) I noticed that the collection view is declared using this format:
let myCollectionView: UICollectionView = {}()
After reading the Properties page of Swift.org the format looks similar to a computed property. Is that right? Why are there parentheses after the brackets? Are we "calling" the collection view or instantiating it?
2) Is UICollectionViewFlowLayout
used when one does not need a custom layout for the collection view? Meaning, is this the "default" layout? And do you always "have" to define a layout object for all collection views?
3) What does frame: .zero
mean in the line let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
?
4) How do I add elements, such as a title, within a collection view cell?
Any advice or guidance is greatly appreciated! 😀
import UIKit
class FirstVC: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(collectionView)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(UICollectionViewCell.self,
forCellWithReuseIdentifier: "cell")
setupCollectionConstraints()
}
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.backgroundColor = .lightGray
return cv
}()
func setupCollectionConstraints() {
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
collectionView.heightAnchor.constraint(equalToConstant: 200).isActive = true
collectionView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
collectionView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
cell.backgroundColor = .white
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 250, height: 100)
}
}
Top comments (4)
It's been a while since I've done this. I'll try to help.
1) I haven't seen that syntax before, but its allowing you to set properties for initialization using a closure/function.
2) I'm not sure how many collectionviewlayouts exist, but even if there is a default, you'll need to be aware of it. This is a case where IB is attempts to make things less complicated by removing your need to know all these little things while allowing you to override or customize
3) Ah the magic. Ok, knowing this part (And this is something to nail for interviews) is key.
CollectionViews and TableViews use a pattern called the Flyweight pattern. Essentially this pattern recycles objects. So your CollectionView has CollectionViewCells. Under the covers it has a small pool of them and it continually recycles them as they scroll on and off the screen. All of those collectionview methods you have to implement as a part of the datasource and vc delegate orchestrate this.
In your initializer you registered a specific cell with the collection view controller. That's basically telling the collectionview to get a few reay for recycling. You also gave it an identifier, "cell" this is how you'll get access to one of those recycled cells. You could register different kinds of cells using this mechanism.
Then there is a method called dequecellwithreuseidentifier. This is the magic. This method gets called as the need to show another cell comes up. You then ask it to give you a recycled cell based on the reuse identifier. iOS obliges by giving you one. At this point it still has everything it had the last time it was viewed.
Now, in that method (There is a more performant one to use called willDisplayCell [I think]) you can modify that cell. So your question about changing a title and whatnot happens here.
If your cell doesn't have those elements then too bad. If you built a custom cell, for instance, you'd deque the cell and maybe cast it back to the correct type so you can access its elements. The default cell, depending on how you've set it up will have titles and whatnot. So where you're setting the background to white, change the title to something else there too.
Hi @recursivefaults thank you so much for such a detailed response! I really appreciate it 😀. Is there a more common or better syntax for creating a collection view than the one I mentioned here? How would you do it?
I think you're two choices are going to be a classic version and the on you used. The classic one would look like this.
I'm assuming there aren't required parameters.
Generally speaking I don't use closures for things when I can make it more explicit otherwise. I find they read poorly and depending on the language and situation you have to know that the closure executes on a seperate thread or execution context.
I've not used the closure based way before, so I can't speak to its long term usefulness. My first reaction to it is that I'd probably not use it unless I was doing something very complex that wrapping it up a function wouldn't cover.
Thank you so much!