As I have given auto layout for the collection view but in portrait it was fine:
The problem is that where as coming to landscape the gap between the cells are increasing. How to avoid this?
import UIKit
class ViewController: UIViewController,UICollectionViewDataSource,UICollectionViewDelegate,UICollectionViewDelegateFlowLayout{
@IBOutlet weak var collectionView: UICollectionView!
var images = ["apple","banana","cherry","grapes","kiwifruit","mangoes","orange","papaya","passionfruit","peaches","pineapple","strawberry","sugarapple","watermelon"]
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(orientationChanged(notification:)), name: Notification.Name.UIDeviceOrientationDidChange, object: nil)
collectionView.dataSource = self
collectionView.delegate = self
}
func orientationChanged(notification: Notification) {
collectionView.collectionViewLayout.invalidateLayout()
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return images.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "stridentifier",for:indexPath) as! FruitsCollectionViewCell
cell.image.image = UIImage(named: images[indexPath.row])
cell.nameLabel.text = images[indexPath.row]
return cell
}
class SampleCollectionViewFlowLayout: UICollectionViewFlowLayout {
override init() {
super.init()
setUpLayout()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUpLayout()
}
override var itemSize: CGSize {
set {}
get {
let itemWidth = ((self.collectionView?.bounds.width)! / 3.0) - self.minimumLineSpacing - minimumInteritemSpacing
return CGSize(width: itemWidth, height: itemWidth)
}
}
func setUpLayout() {
minimumInteritemSpacing = 0
minimumLineSpacing = 1.0
scrollDirection = .vertical
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
}
}
If you have subclassed UICollectionViewFlowLayout you need to override itemSize
and setup minimumInteritemSpacing
, minimumLineSpacing
once you have done that when the orientation changes you need to tell the collection view to reset the layout using collectionView.collectionViewLayout.invalidateLayout()
If you haven't subclassed it you need to override
collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
Whenever the device rotates call the collectionView.collectionViewLayout.invalidateLayout()
You will have to add notification for device rotation.
Here is a sample UICollectionViewFlowLayout for 3 columns
class SampleCollectionViewFlowLayout: UICollectionViewFlowLayout {
override init() {
super.init()
setUpLayout()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUpLayout()
}
override var itemSize: CGSize {
set {}
get {
// 2 columns in portrait and 3 in landscape
if UIApplication.shared.statusBarOrientation.isPortrait {
let itemWidth = ((self.collectionView?.bounds.width)! / 2.0) - self.minimumLineSpacing - minimumInteritemSpacing
return CGSize(width: itemWidth, height: itemWidth)
}
let itemWidth = ((self.collectionView?.bounds.width)! / 3.0) - self.minimumLineSpacing - minimumInteritemSpacing
return CGSize(width: itemWidth, height: itemWidth)
}
}
func setUpLayout() {
minimumInteritemSpacing = 0
minimumLineSpacing = 1.0
scrollDirection = .vertical
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
}
Add this line in viewDidLoad
NotificationCenter.default.addObserver(self, selector: #selector(orientationChanged(notification:)), name: Notification.Name.UIDeviceOrientationDidChange, object: nil)
and add the function
func orientationChanged(notification: Notification) {
collectionView.collectionViewLayout.invalidateLayout()
}
Also if you are using customLayout you need to go to the xib->select the collectionView->attributes inspector(4th option)-> Tap Layout and select Custom and put the name of the subclassed UICollectionViewLayout class(SampleCollectionViewFlowLayout) in our case.
You need to set UICollectionViewFlowLayout class here after selecting the UICollectionView in you Storyboard/xib file
Though i am bit late in this thread.But my solution will work in every devices according to your specification. 2 cells in portrait and 3 cells in landscape.But you can increase cell number according to your choice just modify NUMBER_OF_CELLS_IN_PORTRAIT and NUMBER_OF_CELLS_IN_LANDSCAPE
I have made comment in every function.So it may be understandable.
At first i made some default value .
let SCREEN_WIDTH = UIScreen.main.bounds.size.width
let SCREEN_HEIGHT = UIScreen.main.bounds.size.height
let BASE_SCREEN_HEIGHT:CGFloat = 736.0
let SCREEN_MAX_LENGTH = max(SCREEN_WIDTH, SCREEN_HEIGHT)
let ASPECT_RATIO_RESPECT_OF_7P = SCREEN_MAX_LENGTH / BASE_SCREEN_HEIGHT
let MINIMUM_INTERITEM_SPACING:CGFloat = 14.0 //My Default Inter Cell Spacing for iphone 7plus as i have designed in it.
var ITEM_WIDTH:CGFloat = 200 //for iPhone 7 plus.initial value
var ITEM_HEIGHT:CGFloat = 200 //for iphone 7 plus.initial value
let NUMBER_OF_CELLS_IN_PORTRAIT:CGFloat = 2
let NUMBER_OF_CELLS_IN_LANDSCAPE:CGFloat = 3
Then in viewDidLoad i initialize the collection view with the following function
func initialCollectionData() {
let orientation = UIApplication.shared.statusBarOrientation
if orientation == .portrait {
self.calculateCellSize(screenWidth:SCREEN_WIDTH ,cellItem: NUMBER_OF_CELLS_IN_PORTRAIT)
}
else {
self.calculateCellSize(screenWidth:SCREEN_WIDTH ,cellItem: NUMBER_OF_CELLS_IN_LANDSCAPE)
}
}
And there are some helper function. Like this.
//This method calculate cellsize according to cell number.
func calculateCellSize(screenWidth:CGFloat , cellItem:CGFloat) {
ITEM_WIDTH = (screenWidth - MINIMUM_INTERITEM_SPACING * ASPECT_RATIO_RESPECT_OF_7P * (cellItem-1)) / cellItem - 1// This 1 has been subtracted from ITEM_WIDTH to remove mantissa
ITEM_HEIGHT = ITEM_WIDTH
}
//This method calculate cell number according to orientation.
func findCellItem(screenWidth:CGFloat) {
let orientation = UIDevice.current.orientation
if orientation == .portrait {
self.calculateCellSize(screenWidth:screenWidth ,cellItem: NUMBER_OF_CELLS_IN_PORTRAIT) //You have chosen 2 cells to show in portrait
}
else {
self.calculateCellSize(screenWidth:screenWidth ,cellItem: NUMBER_OF_CELLS_IN_LANDSCAPE) ////You have chosen 3 cells to show in portrait
}
}
//During orientation change this method is called everytime.
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { context in
context.viewController(forKey: UITransitionContextViewControllerKey.from)
//Below two methods do the trick
self.findCellItem(screenWidth: size.width)
self.collectionView.collectionViewLayout.invalidateLayout()
}, completion: {
_ in
})
}
//Collection View delegate methods
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let collectionCell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
collectionCell.fruitImage.image = UIImage.init(named: imageArray[indexPath.row])
return collectionCell
}
public func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize.init(width: ITEM_WIDTH , height: ITEM_HEIGHT )
}
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return MINIMUM_INTERITEM_SPACING * ASPECT_RATIO_RESPECT_OF_7P
}
public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return MINIMUM_INTERITEM_SPACING * ASPECT_RATIO_RESPECT_OF_7P
}
Just add the following lines in your ViewController :
override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
self.yourCollectionView.collectionViewLayout.invalidateLayout()
}
And set proper size in this delegate method :
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.