I try to add a collection view to a Today Extension with specifying the items per row of 3 items and also with setting the section insets for with 20 for top, left, bottom and right. When I do this in a Single View Application everything is like expected, but when I do the same programmatically for the Today Extension the collection view looks different, especially the space of the right and bottom seems not to be like in the Single View Application. What's the reason why there is this difference? I expected the same behaviour for the Today Extension like in the Single View Application.
To understand my problem better below is the code of the Single View Application and the Today Extension, both with Screenshots:
ViewController of the Single View Application:
import UIKit
class ViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
var collectionView: UICollectionView!
let sectionInsets = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
let itemsPerRow: CGFloat = 3
override func viewDidLoad() {
super.viewDidLoad()
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = sectionInsets
collectionView = UICollectionView(frame: view.frame, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cellId")
collectionView.backgroundColor = .white
view.addSubview(collectionView)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 6
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath)
cell.backgroundColor = .blue
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let paddingSpace = sectionInsets.left * (itemsPerRow + 1)
let availableWidth = view.frame.width - paddingSpace
let widthPerItem = availableWidth / itemsPerRow
return CGSize(width: widthPerItem, height: widthPerItem)
}
}
Screenshot of the Single View Application:
TodayViewController of the Today Extension:
import UIKit
import NotificationCenter
class TodayViewController: UIViewController, NCWidgetProviding, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
var collectionView: UICollectionView!
let sectionInsets = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
let itemsPerRow: CGFloat = 3
override func viewDidLoad() {
super.viewDidLoad()
extensionContext?.widgetLargestAvailableDisplayMode = .expanded
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = sectionInsets
collectionView = UICollectionView(frame: view.frame, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cellId")
collectionView.backgroundColor = .white
view.addSubview(collectionView)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 6
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath)
cell.backgroundColor = .blue
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let paddingSpace = sectionInsets.left * (itemsPerRow + 1)
let availableWidth = view.frame.width - paddingSpace
let widthPerItem = availableWidth / itemsPerRow
return CGSize(width: widthPerItem, height: widthPerItem)
}
func widgetActiveDisplayModeDidChange(_ activeDisplayMode: NCWidgetDisplayMode, withMaximumSize maxSize: CGSize) {
// toggle height in case of more/less button event
if activeDisplayMode == .compact {
self.preferredContentSize = CGSize(width: 0, height: 110)
} else {
self.preferredContentSize = CGSize(width: 0, height: 220)
}
}
}
Screenshot of the Today Extension:
Tableiw is a simple list, which displays single-dimensional rows of data. It's smooth because of cell reuse and other magic. 2. UICollectionView is the model for displaying multidimensional data .
Select the Main storyboard from the file browser. Add a CollectionView by pressing command shift L to open the storyboard widget window. Drag the collectionView onto the main view controller. Add constraints to the UICollectionView widget to ensure that the widget fills the screen on all devices.
I solved my own problem with the help of this answer. The solution so far is that I just setup the collection view in viewDidAppear(_:)
instead of viewDidLoad()
in the TodayViewController of the Today Extension (The space to the bottom currently looks like that, because I set a fix height of 220):
import UIKit
import NotificationCenter
class TodayViewController: UIViewController, NCWidgetProviding, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
var collectionView: UICollectionView!
let sectionInsets = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
let itemsPerRow: CGFloat = 3
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
extensionContext?.widgetLargestAvailableDisplayMode = .expanded
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = sectionInsets
collectionView = UICollectionView(frame: view.frame, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cellId")
collectionView.backgroundColor = .white
view.addSubview(collectionView)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 6
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath)
cell.backgroundColor = .blue
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let paddingSpace = sectionInsets.left * (itemsPerRow + 1)
let availableWidth = view.frame.width - paddingSpace
let widthPerItem = availableWidth / itemsPerRow
return CGSize(width: widthPerItem, height: widthPerItem)
}
func widgetActiveDisplayModeDidChange(_ activeDisplayMode: NCWidgetDisplayMode, withMaximumSize maxSize: CGSize) {
// toggle height in case of more/less button event
if activeDisplayMode == .compact {
self.preferredContentSize = CGSize(width: 0, height: 110)
} else {
self.preferredContentSize = CGSize(width: 0, height: 220)
}
}
}
Now I get the same result as in the Single View Application:
Update:
Unfortunately the solution from above had still a not expected behaviour. When the Today Extension was in an expanded state before the build, it looks like the screenshot from above, like expected. The problem is when it's in a collapsed state before the build and I want to expand the extension, the result looks like this (the expanded part of the collection view is just cutted off):
The solution for this problem is so far to set the initial frame of the collection view to .zero
and set the frame in viewWillLayoutSubviews()
like this:
import UIKit
import NotificationCenter
class TodayViewController: UIViewController, NCWidgetProviding, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
var collectionView: UICollectionView!
let sectionInsets = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
let itemsPerRow: CGFloat = 3
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
extensionContext?.widgetLargestAvailableDisplayMode = .expanded
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = sectionInsets
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cellId")
collectionView.backgroundColor = .white
view.addSubview(collectionView)
}
override func viewWillLayoutSubviews() {
let frame = view.frame
collectionView?.frame = CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.size.width, height: frame.size.height)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 6
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath)
cell.backgroundColor = .blue
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let paddingSpace = sectionInsets.left * (itemsPerRow + 1)
let availableWidth = view.frame.width - paddingSpace
let widthPerItem = availableWidth / itemsPerRow
return CGSize(width: widthPerItem, height: widthPerItem)
}
func widgetActiveDisplayModeDidChange(_ activeDisplayMode: NCWidgetDisplayMode, withMaximumSize maxSize: CGSize) {
// toggle height in case of more/less button event
if activeDisplayMode == .compact {
self.preferredContentSize = CGSize(width: 0, height: 110)
} else {
self.preferredContentSize = CGSize(width: 0, height: 220)
}
}
}
Now the behaviour is like expected, like shown in the first result screenshot even if the Today Extension was first collapsed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With