I created a CollectionViewController using Storyboards and made the adjustments by code and now I need to add a ImageView at the bottom of the screen that takes the whole width of the view.
I tried using Storyboards to do it, but it didn't work, if I add it programmatically, where and how should I do it?
import UIKit
import Device_swift
class MenuCentralCollectionViewController: UICollectionViewController {
//data source
let deviceType = UIDevice.current.deviceType
// MARK: ViewController life cycle
override func viewDidLoad() {
super.viewDidLoad()
let layout = collectionViewLayout as! UICollectionViewFlowLayout
layout.itemSize = CGSize(width: 310 / 2 - 37.5, height: 150)
}
// MARK: - UICollectionviewDataSource
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 8
}
private struct Storyboard {
static let CellIdentifier = "MenuCell"
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Storyboard.CellIdentifier, for: indexPath) as UICollectionViewCell
return cell
}
Use UIViewController and add UICollectionView as subview yourself. With this approach you can add any subviews with any frames, not only fullscreen `UICollectionView.
Related
(source: uimovement.com)
I want to implement layout like the above(auto line break when screen's width is not enough to accommodate buttons' widths).
But I can't come up with any idea about how to make that image like layout. I just can implement statically, not dynamically.
In Android, there is a layout that can implement the above.
But I don't know what can help me implement the above image in swift.
Please help me.
Following #Matthew Mitchell 's suggestion.
I implemented it like below.
My ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
var hobbyArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
// self.collectionView!.register(CollectionViewCell.self, forCellWithReuseIdentifier: "cell")
hobbyArray.append("test1")
hobbyArray.append("test2")
hobbyArray.append("test3")
hobbyArray.append("test4")
hobbyArray.append("test5")
hobbyArray.append("test5")
hobbyArray.append("test5123123")
collectionView.reloadData()
}
}
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return hobbyArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
cell.title.text = self.hobbyArray[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let text = self.hobbyArray[indexPath.row]
let cellWidth = text.size(withAttributes:[.font: UIFont.systemFont(ofSize:17)]).width + 25
return CGSize(width: cellWidth, height: 35.0)
}
}
Other codes are implemented exactly equal to #Matthew Mitchell's codes.
However, still I can't get what I wanted to implement.
I failed to make what I had wanted.
To do this efficiently you need to have a UICollectionView with a custom FlowLayout. I am going to do a storyboard example. This is quite complicated so I will try my best. All the code will be below the steps.
Step 1: Create a swift file named CollectionViewFlowLayout and use UICollectionViewLayout code in the newly created class.
Step 2: Add a UICollectionView to your ViewController
Step 3: Link new UICollectionView layout with the CollectionViewFlowLayout class
Step 4: Create a UICollectionViewCell inside the UICollectionView, add a label to that cell and constrain it to left and right in the cell and center it vertically. In the attributes inspector of the cell give it a reusable identifier ("cell" for this example)
Step 6: Create a swift file named collectionViewCell and use UICollectionViewCell class that links to your collectionViewCell (same way you linked your flowlayout in step 3).
Step 7: Add ViewController code to your ViewController Class. This code allows you to add cells to your collection view. The sizeForItemAt function will allow you to resize the cells according to the width of the string that you put inside each cell.
Code:
ViewController:
import UIKit
class viewController: UIViewController {
//Outlets
#IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
collectionView.delegate = self
collectionView.dataSource = self
}
}
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return YOUR_ITEM_COUNT
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
self.title.text = YOUR_ITEMS_LIST[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let text = YOUR_ITEMS_LIST[indexPath.row]
let cellWidth = text!.size(withAttributes:[.font: UIFont.systemFont(ofSize:17)]).width + 25
return CGSize(width: cellWidth, height: 35.0)
}
}
UICollectionViewCell:
class CollectionViewCell: UICollectionViewCell {
//Outlets
#IBOutlet weak var title: UILabel!
}
UICollectionViewFlowLayout:
import UIKit
class CollectionViewFlowLayout: UICollectionViewFlowLayout {
var tempCellAttributesArray = [UICollectionViewLayoutAttributes]()
let leftEdgeInset: CGFloat = 0
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let cellAttributesArray = super.layoutAttributesForElements(in: rect)
//Oth position cellAttr is InConvience Emoji Cell, from 1st onwards info cells are there, thats why we start count from 2nd position.
if(cellAttributesArray != nil && cellAttributesArray!.count > 1) {
for i in 1..<(cellAttributesArray!.count) {
let prevLayoutAttributes: UICollectionViewLayoutAttributes = cellAttributesArray![i - 1]
let currentLayoutAttributes: UICollectionViewLayoutAttributes = cellAttributesArray![i]
let maximumSpacing: CGFloat = 8
let prevCellMaxX: CGFloat = prevLayoutAttributes.frame.maxX
//UIEdgeInset 30 from left
let collectionViewSectionWidth = self.collectionViewContentSize.width - leftEdgeInset
let currentCellExpectedMaxX = prevCellMaxX + maximumSpacing + (currentLayoutAttributes.frame.size.width )
if currentCellExpectedMaxX < collectionViewSectionWidth {
var frame: CGRect? = currentLayoutAttributes.frame
frame?.origin.x = prevCellMaxX + maximumSpacing
frame?.origin.y = prevLayoutAttributes.frame.origin.y
currentLayoutAttributes.frame = frame ?? CGRect.zero
} else {
// self.shiftCellsToCenter()
currentLayoutAttributes.frame.origin.x = leftEdgeInset
//To Avoid InConvience Emoji Cell
if (prevLayoutAttributes.frame.origin.x != 0) {
currentLayoutAttributes.frame.origin.y = prevLayoutAttributes.frame.origin.y + prevLayoutAttributes.frame.size.height + 08
}
}
}
}
return cellAttributesArray
}
func shiftCellsToCenter() {
if (tempCellAttributesArray.count == 0) {return}
let lastCellLayoutAttributes = self.tempCellAttributesArray[self.tempCellAttributesArray.count-1]
let lastCellMaxX: CGFloat = lastCellLayoutAttributes.frame.maxX
let collectionViewSectionWidth = self.collectionViewContentSize.width - leftEdgeInset
let xAxisDifference = collectionViewSectionWidth - lastCellMaxX
if xAxisDifference > 0 {
for each in self.tempCellAttributesArray{
each.frame.origin.x += xAxisDifference/2
}
}
}
}
You can use a UICollectionView with custom UICollectionViewFlowLayout or use a fully custom solution with UIView as root and different UIScrollViews with some custom content as lines (cells) here.
I have an example, but it's too huge to post here. Write me if you are inserting in.
I had the same problem and i found a shortest and super easy solution to make the height dynamic by subclassing UICollectionView and assign it to the CollectionView.
Here's the code:
class DynamicHeightCollectionView: UICollectionView {
override func reloadData() {
super.reloadData()
self.invalidateIntrinsicContentSize()
}
override var intrinsicContentSize: CGSize {
return self.collectionViewLayout.collectionViewContentSize
}
}
I am attaching reference link to that solution.
https://stackoverflow.com/a/49297382/9738186
There is a dynamic set of data i have to organise as set of swiped tables. Now I'm using UIPageViewController for this task, but it has some problems with dynamically uploaded data from net - if we swiping pages too fast, we can overtake data loading and program can crashes. Now, to solve this problems, i'm uploading data in advance of 5 pages, but i think it's bad solution and i hope there is a right solving for this task.
I found another idea - using UICollectionView for this task, but i'm not sure, that i can use tables as UICollectionViewCell in this method and i'm not sure, that this decision is correct.
What can you recommend in this situation
here is my approach to such case
first I would have my ViewController containing the collectionView as below -make sure to add your own constraints however you would like-
import UIKit
class ViewController: UIViewController {
// IBOutlets
#IBOutlet var collectionView: UICollectionView!
// MARK: Lifecycle Methods
override func viewDidLoad() {
super.viewDidLoad()
setupCollectionView()
}
// MARK: Private Methods
private func setupCollectionView(){
collectionView.delegate = self
collectionView.dataSource = self
collectionView.separatorStyle = .none
collectionView?.register(UINib(nibName: /* Your cell NibName */, bundle: nil), forCellWithReuseIdentifier: /* Your cell Id*/)
collectionView?.isPagingEnabled = true
}
// MARK: UICollectionView Methods
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return zekr?.subNodes?.count ?? 0
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = zekrCollectionView.dequeueReusableCell(withReuseIdentifier: /* Your cell Id */, for: indexPath) as! /* Your cell Type */
cell.cellData = /* your list that corresponds with the list that table will take */
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// this is to make each cell fill the whole page
return CGSize(width: view.frame.width, height: view.frame.height)
}
}
then you will add tableview inside each collectionViewCell and for sure to fulfill the delegate and datasource for the tableView inside the collectionViewCell as below
import UIKit
class CollectionViewCell: UICollectionViewCell, UITableViewDataSource, UITableViewDelegate {
// MARK: IBOutlets
#IBOutlet weak var pageTable: UITableView!
var cellData: [/*Your List*/]?
// MARK: Properties
var cellData: /*Your List*/?{
didSet{
if let value = cellData {
/* reload your table */
}
}
}
// MARK: Life Cycle Methods
override func awakeFromNib() {
super.awakeFromNib()
setupPageTable()
}
private func setupPageTable(){
pageTable.delegate = self
pageTable.dataSource = self
pageTable.register(UINib(nibName: /* TableView Cell NibName */, bundle: nil), forCellReuseIdentifier: /* CellId */)
}
// MARK: UITableViewDelegate
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellData?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: /* CellId */) as! /* Cell Type */
cell.cellDataModel = cellData?[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let cell: UITableViewCell = cellData?[indexPath.row]
return cell.contentView.frame.size.height
}
}
and finally your tableViewCell will remain as the same however you done it initially passing to it the cellDataModel
in case the horizontal scrolling did't work for the collectionView you can google a solution depending on your swift version
the result you will be having collectionView on your home or whatever VC you are on with horizontal scrolling each cell containing tableView with it's cells with vertical scrolling acting as viewPager in android
I have 12 cells in my CollectionView. I want to show 3 cells on one screen like this:
and scroll right to show another cells.
But now I create CollectionView and CollectionViewCell and edit cell in storyboard and I get this result:
How to fix it?
My code in CollectionView:
import UIKit
private let reuseIdentifier = "Cell"
class MasterViewController: UICollectionViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 12
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! MasterViewCell
cell.label.text = String(indexPath.row)
cell.backgroundColor = UIColor.green
return cell
}
}
My code in CollectionViewCell:
import UIKit
class MasterViewCell: UICollectionViewCell {
#IBOutlet weak var label: UILabel!
}
To achieve that spacing you need to do like so
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
so after each 3 cell in new section it will take that Section Inset and will give that space.
I have added sample screen shot for your reference please check it
and still you have confusion so please let me know.
i'm doing with this, i want to use CollectionView, but i haven't seen prototype cell, and don't know how to use CollectionView in this case, can someone help me ?
I try to use like this way but it take alot of time and hard to manage than UICollectionView
The main way to use UICollectionView is by managing the logic programmatically.
First, create a new class which inherits from UICollectionViewCell. Choose if you want to include a xib to easily design your cell:
Design your cell with Interface Builder or programmatically.
Create your main view controller including a xib (or a storyboard) with the collection view inside and link it to the associated class via Interface Builder. Alternatively you can add a collection view programmatically to your UIViewController
Make the target view controller conform to the UICollectionViewDelegate and UICollectionViewDataSource protocols by declaring them after the father class:
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var collectionView: UICollectionView!
//...
}
Register the associated nib or the class for your cell in the viewDidLoad method and associate the datasource and delegate protocols to the view controller class:
let cellIdentifier = "cellIdentifier"
override func viewDidLoad() {
super.viewDidLoad()
//if you use xibs:
self.collectionView.register(UINib(nibName:"MyCollectionCell", bundle: nil), forCellWithReuseIdentifier: cellIdentifier)
//or if you use class:
self.collectionView.register(MyCollectionCell.self, forCellWithReuseIdentifier: cellIdentifier)
self.collectionView.delegate = self
self.collectionView.dataSource = self
}
Implement the methods declared in the UICollectionViewDelegate and UICollectionViewDataSource protocols :
let objects = ["Cat", "Dog", "Fish"]
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.objects.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellIdentifier, for: indexPath) as! MyCollectionCell
//in this example I added a label named "title" into the MyCollectionCell class
cell.title.text = self.objects[indexPath.item]
return cell
}
Run your app in the simulator (or on a real device) and.. Et voilĂ ! :)
For more info: https://developer.apple.com/reference/uikit/uicollectionview
ok first you must have the IBOutlet of your collection view and implements the methods like this
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource,UICollectionViewDelegateFlowLayout{
#IBOutlet var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
count = 9;
let nib = UINib(nibName: "yourItemView", bundle: nil)
collectionView.registerNib(nib, forCellWithReuseIdentifier: "yourItemView")
self.collectionView.delegate = self
self.collectionView.dataSource = self
}
ok in the function you add a xib file, next you must create that extend from UICollectionViewCell, and when you finish this you must override the next methods
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return count
// the numbers of items
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {//size of your item for screen sizes
let wsize = UIScreen.mainScreen().bounds.size.width
switch(wsize){
case 414:
return CGSize(width: 190, height: 102)
case 375:
return CGSize(width: 190, height: 102)
case 320:
return CGSize(width: 174, height: 102)
default:
return CGSize(width: 174, height: 102)
}
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("yourItemView", forIndexPath: indexPath) as! yourItemView
return cell
}
and this is all, good luck
This is what I am able to make through a tutorial
This is the Screenshot of the app
But I have to make 1st and every 3rd Cell to be full width. How I supposed to do it?
The code is this
class MainViewController: UICollectionViewController
{
// data source
let publishers = Publishers()
private let leftAndRightPaddings: CGFloat = 32.0
private let numberOfItemsPerRow: CGFloat = 2.0
private let heigthAdjustment: CGFloat = 100
// MARK: - View controller life cycle
override func viewDidLoad() {
super.viewDidLoad()
let width = (CGRectGetWidth(collectionView!.frame) - leftAndRightPaddings) / numberOfItemsPerRow
let layout = collectionViewLayout as! UICollectionViewFlowLayout
layout.itemSize = CGSizeMake(width, heigthAdjustment)
print(width.description)
}
// MARK: - UICollectionViewDataSource
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return publishers.numberOfPublishers
}
private struct Storyboard
{
static let CellIdentifier = "PublisherCell"
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(Storyboard.CellIdentifier, forIndexPath: indexPath) as UICollectionViewCell
return cell}
}
You need to implement this func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize method in your UICollectionViewDelegateFlowLayout