Split UICollectionView & UICollectionViewDataSource shows black screen - ios

I'm starting a new project with a collection view that will be downloading a lot of data from a REST api. I've split the collection view and datasource into two files, but when I run the application, all I get is a black screen. I've seen a few questions and tried changing the background, adding the collection view as a subview, and nothing seems to be working. I don't run into any errors and the debug view hierarchy and the views are listed (back to front) as UIWindoW -> MainSearchVC -> UICollectionView.
I originally thought no cells were being filled in, but they should be since I set a UIImage in the cells. I'm not sure where else to look for this. My code is below - if anyone has experience with splitting the collection view from it's datasource or why things aren't working, please help:)
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = MainSearchVC()
return true
}
}
class MainSearchVC: UICollectionViewController {
init(){
super.init(collectionViewLayout: UICollectionViewFlowLayout())
self.collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: UICollectionViewFlowLayout())
self.collectionView?.dataSource = MainSearchDataSource()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
collectionView?.register(ImageCell.self, forCellWithReuseIdentifier: "imagecell")
}
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView?.backgroundColor = UIColor.clear
self.collectionView?.tintColor = UIColor.blue
NSLog("Visible Cells: " + String(describing: self.collectionView?.visibleCells.count))
self.view.addSubview(self.collectionView!)
}
}
class MainSearchDataSource: NSObject, UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1;
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "imagecell", for: indexPath)
cell.backgroundColor = UIColor.red
return cell
}
}
class ImageCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
let imageView: UIImageView = {
let iv = UIImageView()
iv.image = UIImage(contentsOfFile: "SR71")
iv.contentMode = .scaleAspectFill
return iv
}()
func setupViews(){
addSubview(imageView)
imageView.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.width)
}
}

Problem with your MainSearchVC collection view dataSource delegate
First you should understand the Zeroing Weak Reference
dataSource delegate is maintaining a weak reference to your Object of MainSearchDataSource. And your statement is
self.collectionView?.dataSource = MainSearchDataSource()
Just allocated and assigned. It will not retain the datasource object.
You have to create a class variable and assign the MainSearchDataSource object. It will hold the object reference until MainSearchVC deallocate from memory.
class MainSearchVC: UICollectionViewController {
var searchDataSource: MainSearchDataSource?
init(){
super.init(collectionViewLayout: UICollectionViewFlowLayout())
self.collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: UICollectionViewFlowLayout())
searchDataSource = MainSearchDataSource()
self.collectionView?.dataSource = searchDataSource
}
// Remaining code of your `MainSearchVC`
}

Related

UICollectionView - When the keyboard appears the entire collection view is shifted with the keyboard

This isn't my desired effect. This only happens when the collection view is set to horizontal flow layout. I've seen a few other posts regarding this very same issue but none of the provided answers have worked. Has anyone found a solution?
I've provided two screenshots showing a UITextField before the keyboard is triggered and after. As you can see the UITextField along with the entire collection view (which can't be seen) is pushed up along with the keyboard. Usually the keyboard is overlayed having no effect on the views.
Before
After
Update
Code provided. The method I used to implement doesn't involve a Storyboard.
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow()
window?.makeKeyAndVisible()
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let rvc = ViewController(collectionViewLayout: layout)
window?.rootViewController = rvc
return true
}
// .... other boilerplate code provided by Apple when you make a new App
}
ViewController.swift
class ViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, UITextFieldDelegate {
let homeCellId = "homeCellId"
let worldCellId = "worldCellId"
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
collectionView?.register(HomeCell.self, forCellWithReuseIdentifier: homeCellId)
collectionView?.register(WorldCell.self, forCellWithReuseIdentifier: worldCellId)
collectionView?.isPagingEnabled = true
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: view.frame.height)
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
print(indexPath.item)
let identifier: String
if indexPath.item == 0 {
identifier = homeCellId
} else if indexPath.item == 1 {
identifier = worldCellId
}
else {
identifier = homeCellId
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath)
cell.backgroundColor = UIColor.blue
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
}
MyCell.swift
class MyCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
self.setupView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupView() {
}
}
HomeCell.swift
class HomeCell: MyCell {
let weightField: UITextField = UITextField(frame: .zero)
override func setupView() {
super.setupView()
print("HomeCell")
weightField.backgroundColor = UIColor.white
weightField.translatesAutoresizingMaskIntoConstraints = false
weightField.keyboardType = UIKeyboardType.default
self.addSubview(weightField)
weightField.topAnchor.constraint(equalTo: self.topAnchor, constant: 200).isActive = true
weightField.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 50).isActive = true
}
}
WorldCell.swift
// same as HomeCell
class WorldCell: MyCell {
override func setupView() {
super.setupView()
print("worldcell")
}
}
Okay so I've found a solution to this problem. I came across this in a couple of other threads on stackoverflow regarding a similar incident, in one case the Answer had no votes attributed to it and a comment left to the answer said it didn't work..
Though after all this I'm still not crystal clear on why the other implementation causes the collection view to shift up. Though there is some correlation between the window, root view controller and it's subviews along with the keyboard. Why this happens I don't know.
Now on to the code and fix..
The main different between the method in the question above and here is the way the collection view is initialised. I'll only post what I changed because the rest is just the same.
AppDelegate.swift
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let rvc = ViewController()
window? = UIWindow()
window?.makeKeyAndVisible()
window?.rootViewController = rvc
return true
}
}
The striking difference here is the root view controller is no longer initialised with the Collection View Controller. We use a standard View Controller.
ViewController.swift
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
lazy var collView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let view = UICollectionView(frame: .zero, collectionViewLayout: layout)
view.dataSource = self
view.delegate = self
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.darkGray
collView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cellId")
collView.register(HomeCell.self, forCellWithReuseIdentifier: "homeCellId")
self.view.addSubview(collView)
collView.backgroundColor = UIColor.blue
collView.translatesAutoresizingMaskIntoConstraints = false
collView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
collView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
collView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
collView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
collView.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 1)
// Do any additional setup after loading the view, typically from a nib.
}
}
We initialise the View Controller with a collectionview as a subview and apply the same code we would normally to the cells

UICollectionView didSelectItemAt never triggers

In my VC I have a view that pulls up from the bottom. I setup and add a UICollectionView in the viewDidLoad():
//Add and setup the collectionView
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: flowLayout)
collectionView?.register(PhotoCell.self, forCellWithReuseIdentifier: "photoCell")
collectionView?.delegate = self
collectionView?.dataSource = self
collectionView?.backgroundColor = #colorLiteral(red: 0.9771530032, green: 0.7062081099, blue: 0.1748393774, alpha: 1)
collectionView?.allowsMultipleSelection = false
collectionView?.allowsSelection = true
pullUpView.addSubview(collectionView!)
pullUpView.bringSubview(toFront: collectionView!)
The UICollectionView Delegate methods are in an extension, for now in the same codefile as the VC:
//MARK: - Extension CollectionView
extension MapVC: UICollectionViewDelegate, UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imagesArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoCell", for: indexPath) as? PhotoCell {
let imageFromIndex = imagesArray[indexPath.item]
let imageView = UIImageView(image: imageFromIndex )
cell.addSubview(imageView)
return cell
} else {
return PhotoCell()
}
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("selected")
//Create PopVC instance, using storyboard id set in storyboard
guard let popVC = storyboard?.instantiateViewController(withIdentifier: "popVC") as? PopVC else { return }
popVC.passedImage = imagesArray[indexPath.row]
present(popVC, animated: true, completion: nil)
}
}
The problem is that when I tap on a cell nothing happens. I've put a print statement inside the didSelectItemAt method, but that never gets printed. So, my cells never get selected or at least the didSelectItemAt method never gets triggered!
Been debugging and trying for hours, and I can't see what's wrong. Any help appreciated. Perhaps someone could open mijn project from Github to see what's wrong, if that is allowed ?
UPDATE:
Using Debug View Hierarchy, I see something disturbing: Each photoCell has multiple (many!) UIImageViews. I think that should be just one UIImageView per photoCell. I don't know what is causing this behaviour?
Debug View Hierarchy
I checked your code, there are few problems:
First of all you have to change your PhotoCell implementation, and add your imageView inside the class, only when the cell is created. Your cell is not loading a XIB so you have to add the imageView in init(frame:):
class PhotoCell: UICollectionViewCell {
var photoImageView: UIImageView!
override init(frame: CGRect) {
super.init(frame: frame)
setupCell()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupCell() {
photoImageView = UIImageView()
addSubview(photoImageView)
}
override func layoutSubviews() {
super.layoutSubviews()
photoImageView.frame = bounds // ensure that imageView size is the same of the cell itself
}
}
After this change, in cellForItem method you can do cell.photoImageView.image = imageFromIndex.
The problem of didSelect not called is caused by the fact your pullUpViewis always with height = 1, even if you're able to see the collectionView, it will not receive any touch.
First add an IBOutlet of the height constraint of pullUpView in your MapVc
When creating collection view, ensure the size of collection view is the same of the pullUpView, so it will be able to scroll; collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 300), collectionViewLayout: flowLayout)
Then change animateViewUp and animateViewDown to this
func animateViewUp() {
mapViewBottomConstraint.constant = 300
pullUpViewHeightConstraint.constant = 300 // this will increase the height of pullUpView
UIView.animate(withDuration: 0.5) {
self.view.layoutIfNeeded()
}
}
#objc func animateViewDown() {
cancelAllSessions()
//remove previous loaded images and urls
imagesArray.removeAll()
imageUrlsArray.removeAll()
mapViewBottomConstraint.constant = 0
pullUpViewHeightConstraint.constant = 0 // this will reset height to 0
UIView.animate(withDuration: 0.5) {
self.view.layoutIfNeeded()
}
}
By doing all of this the swipe down gesture will not work anymore because the touch is intercepted and handled by the collection view, you should handle this manually.
However I suggest you to change the online course, there are a lot of things that I don't like about this code.

Pass variable directly to UICollectionViewCell from another view controller

I'm trying to pass an image from a view controller to a UICollectionViewCell whenever I segue to the UICollectionViewController. Normally, I would just use the following code to pass the variable directly to the UICollectionViewController.
let myCollectionViewController = MyCollectionViewController(collectionViewLayout: UICollectionViewFlowLayout())
myCollectionViewController.selectedImage = myImageView?.image
navigationController?.pushViewController(myCollectionViewController, animated: true)
However, I have subclassed my UICollectionViewCell and have set up the cell as follows:
import UIKit
class myCollectionViewCell: UICollectionViewCell {
let imageView:UIImageView = {
let iv = UIImageView()
iv.backgroundColor = .red
iv.contentMode = .scaleAspectFill
iv.clipsToBounds = true
return iv
}()
var selectedImage: UIImage? {
didSet {
self.imageView.image = selectedImage
}
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
How do I directly pass my image directly to my UICollectionViewCell subclass during the segue?
Hope this helps you-:
import Foundation
class HomeController: UIViewController{
// STORED VARIABLE FOR CollectionView
lazy var CollectionView : UICollectionView = {
var layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 0
var collectionViews = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionViews.translatesAutoresizingMaskIntoConstraints = false
collectionViews.backgroundColor = UIColor.white
collectionViews.showsHorizontalScrollIndicator = false
collectionViews.showsVerticalScrollIndicator = false
collectionViews.dataSource = self
collectionViews.delegate = self
return collectionViews
}()
// APPLY CONSTRAINTS FOR CollectionView
func setUpCollectionView(){
view.addSubview(CollectionView)
CollectionView.register(HomeControllerCell.self, forCellWithReuseIdentifier: "cell")
CollectionView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
CollectionView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
CollectionView.topAnchor.constraint(equalTo: view.topAnchor,constant:92).isActive = true
CollectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor,constant:-50).isActive = true
}
override func viewDidLoad() {
super.viewDidLoad()
setUpCollectionView()
}
}
// EXTENSION FOR COLLECTION VIEW PARENT
extension HomeController:UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout{
// NUMBER OF SECTION IN TABLE
public func numberOfSections(in collectionView: UICollectionView) -> Int{
return 1
}
// NUMBER OF ROWS IN PARENT SECTION
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int{
return 5
}
// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
let Cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! HomeControllerCell
// PASS IMAGE (whatever you have) TO COMPUTED VARIABLE image
Cell.image = pass image here
return Cell
}
// SIZE FOR PARENT CELL COLLECTION VIEW
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize{
return CGSize(width: view.frame.width, height: 220)
}
}
CollectionViewCellClass-:
class HomeControllerCell: UICollectionViewCell {
//INITIALIZER
override init(frame: CGRect) {
super.init(frame: frame)
setUpview()
}
// STORED VARIABLE imageVIEW
let imageView:UIImageView = {
let iv = UIImageView()
iv.backgroundColor = .red
iv.contentMode = .scaleAspectFill
iv.translatesAutoresizingMaskIntoConstraints = false
iv.clipsToBounds = true
return iv
}()
// COMPUTED VARIABLE image
var image: UIImage? {
didSet {
self.imageView.image = image
}
}
// APPLY CONSTRAINTS FOR CELL VIEWS
func setUpview(){
// ADD imageView AS SUBVIEW
addSubview(imageView)
// APPLY CONSTRAINT ON IMAGE VIEW
imageView.leftAnchor.constraint(equalTo: self.leftAnchor,constant:5).isActive = true
//menuHeader.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
imageView.topAnchor.constraint(equalTo: self.topAnchor,constant:12).isActive = true
imageView.heightAnchor.constraint(equalToConstant: 50).isActive = true
imageView.widthAnchor.constraint(equalToConstant: 50).isActive = true
}
// REQUIRED INIT
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Why UIImageView get blank when I swipe back?

I have a UICollectionView in my UIViewController(wrapped in UINavigationContoller). In the UICollectionViewCell, I create a UIImageView with a gif url.(Using Kingfisher)
The imageView works as expected, but it's getting blank whenever I "try to" swipe back to previous UIViewController. And when I cancel the "swipe back gesture", the imageView shows the gif again.
Oddly enough, the imageView is not getting blank when I click the back indicator.
Also, if I set a static image to the imageView, the imageView does not get blank.
Does anyone know how to fix this issue?
Here is a demo:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.pushViewController(ViewController2(), animated: true)
}
}
class ViewController2: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.whiteColor()
let layout = UICollectionViewFlowLayout()
layout.itemSize = CGSize(width: 100, height: 100)
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.backgroundColor = UIColor.clearColor()
collectionView.registerClass(CollectionViewCell.self, forCellWithReuseIdentifier: "cell")
view.addSubview(collectionView)
collectionView.snp_makeConstraints { (make) in
make.top.equalTo(snp_topLayoutGuideBottom)
make.left.right.bottom.equalTo(view)
}
}
}
extension ViewController2: UICollectionViewDataSource {
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 20
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! CollectionViewCell
cell.imageView.animationImages = [UIImage(named: "1")!, UIImage(named: "2")!]
cell.imageView.startAnimating()
return cell
}
}
class CollectionViewCell: UICollectionViewCell {
lazy var imageView: UIImageView = {
let view = UIImageView()
view.clipsToBounds = true
view.contentMode = .ScaleAspectFill
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(imageView)
imageView.snp_makeConstraints { (make) in
make.edges.equalTo(contentView)
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I think this is a UIImageView issue.

why is invalidateLayout is not triggering sizeForItemAtIndexPath in UICollectionView? (code attached)

The issue is the number of columns in the collectionView does NOT stay at 7 (the desired amount) upon rotation. What code change is required to fix this?
It seems the invalidateLayout from the custom UICollectionViewFlowLayout is NOT triggering the sizeForItemAtIndexPath method in the collectionView? Any ideas? I really just want the column resizing to occur via the sizeForItemAtIndexPath upon rotation.
NOTE: I'm not using storyboard here, rather I have a custom view in which I drop in a collectionView and reference my own collectionView classes.
The following code works fine, however upon rotation it does not keep the number of columns to 7 like it should. On running the code initially the console output shows:
GCCalendar - init coder
GCCalendar - commonInit
GCCalendarLayout:invalidateLayout
GCCalendarLayout:invalidateLayout
ViewController:viewWillLayoutSubviews
GCCalendarLayout:invalidateLayout
GCCalendarLayout:prepareLayout
sizeForItemAtIndexPath
.
.
sizeForItemAtIndexPath
minimumInteritemSpacingForSectionAtIndex
minimumLineSpacingForSectionAtIndex
GCCalendarLayout:collectionViewContentSize
GCCalendarLayout:layoutAttributesForElementsInRect
GCCalendarLayout:collectionViewContentSize
ViewController:viewWillLayoutSubviews
GCCalendarCell:drawRect
.
.
GCCalendarCell:drawRect
However then rotating the screen I see the following:
ViewController:viewWillLayoutSubviews
GCCalendarLayout:shouldInvalidateLayoutForBoundsChange
GCCalendarLayout:invalidateLayout
GCCalendarLayout:prepareLayout
GCCalendarLayout:collectionViewContentSize
GCCalendarLayout:layoutAttributesForElementsInRect
GCCalendarLayout:collectionViewContentSize
So the issue is "sizeForItemAtIndexPath" never got called????
Output Code on Rotation
Note: "sizeForItemAtIndexPath" is not triggered even though "invalidateLayout" is
ViewController:viewWillLayoutSubviews
GCCalendarLayout:shouldInvalidateLayoutForBoundsChange
GCCalendarLayout:invalidateLayout
**My Custom View that houses the collection View **
import UIKit
#IBDesignable class GCCalendarView: UIView {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
// Private
private func commonInit() {
if self.subviews.count == 0 {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "GCCalendarView", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
view.frame = bounds
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
addSubview(view)
}
}
}
Custom Collection View
import UIKit
class GCCalendar : UICollectionView, UICollectionViewDataSource, UICollectionViewDelegate {
// Init ---------------
func commonInit(coder aDecoder: NSCoder) {
print("GCCalendar - commonInit")
self.registerClass(GCCalendarCell.self, forCellWithReuseIdentifier: "GCCalendarCell")
self.dataSource = self
self.delegate = self
let layout : GCCalendarLayout = GCCalendarLayout(coder: aDecoder)!
self.setCollectionViewLayout(layout, animated: false)
self.backgroundColor = UIColor.whiteColor()
}
required init?(coder aDecoder: NSCoder) {
print("GCCalendar - init coder")
super.init(coder: aDecoder)
commonInit(coder: aDecoder)
}
// UICollectionViewDelegateFlowLayout ------------
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
print("sizeForItemAtIndexPath")
let w : CGFloat = floor(self.frame.size.width/7)
return CGSize(width: w, height: w)
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) ->
CGFloat {
print("minimumInteritemSpacingForSectionAtIndex")
return 0.0
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
print("minimumLineSpacingForSectionAtIndex")
return 0.0
}
// UICollectionViewDataSource -------------------
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 21
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("GCCalendarCell", forIndexPath: indexPath) as? GCCalendarCell
return cell!
}
}
** Custom CollectionView Cell **
import UIKit
class GCCalendarCell: UICollectionViewCell {
#IBOutlet weak var title : UITextField!
#IBOutlet weak var date: UILabel!
required init?(coder aDecoder: NSCoder) {
print("GCCalendarCell - init:coder")
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
override func drawRect(rect: CGRect) {
print("GCCalendarCell:drawRect")
self.layer.borderWidth = 1
self.layer.borderColor = UIColor.grayColor().CGColor
}
// Private
private func commonInit() {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "GCCalendarCell", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
view.frame = bounds
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
addSubview(view)
}
}
Custom Layout
import UIKit
class GCCalendarLayout : UICollectionViewFlowLayout {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
print("GCCalendarLayout:shouldInvalidateLayoutForBoundsChange")
return true
}
override func invalidateLayout() {
print("GCCalendarLayout:invalidateLayout")
super.invalidateLayout()
}
override func prepareForCollectionViewUpdates(updateItems: [UICollectionViewUpdateItem]) {
print("GCCalendarLayout:prepareForCollectionViewUpdates")
super.prepareForCollectionViewUpdates(updateItems)
}
override func finalizeCollectionViewUpdates() {
print("GCCalendarLayout:finalizeCollectionViewUpdates")
super.finalizeCollectionViewUpdates()
}
}
EDIT : Try this in your viewController :
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)
{
(self.collectionview.collectionViewLayout).setItemSize(CGSizeMake(floor(size.width / 7), floor(size.width / 7)))
self.collectionview.collectionViewLayout.invalidateLayout()
}
if you are using Storyboard Set the CollectionviewFlowLayout Class In storyboard I attached Bellow
Nothing helped for me from the existing answers (iPhone X Simulator iOS 11.2).
I've found that changing of estimatedSize helps for me with this problem:
<...>
//***new line
flowLayout.estimatedItemSize = [self getIconSize];//get a new size for items
[flowLayout invalidateLayout];
<...>
#Kujey's solution can be applied to UIView without accessing UIViewController:
func traitCollectionDidChange(previousTraitCollection : UITraitCollection) {
super.traitCollectionDidChange(previousTraitCollection)
self.collectionView.collectionViewLayout.invalidateLayout()
}

Resources