Question : How can I align UICollectionViewCell from top to bottom then left to right?
Current Situation : I have left to right and top to bottom which is normal behavior of it.
Check image :
MyCell.swift :
import UIKit
class MyCell: UICollectionViewCell {
#IBOutlet weak var lblCell: UILabel!
}
myCollectionVC.swift :
import UIKit
private let reuseIdentifier = "Cell"
class myCollectionVC: UICollectionViewController {
#IBOutlet weak var simpleCell: UICollectionViewCell!
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView!.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: UICollectionViewDataSource
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1 }
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//Test behavior with static number
return 100
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: MyCell = collectionView.dequeueReusableCellWithReuseIdentifier("My_Cell", forIndexPath: indexPath) as! MyCell;
cell.backgroundColor = UIColor.greenColor();
cell.lblCell.text = String(indexPath.row + 1);
return cell
}
}
How can I cells from top to bottom then left to right?
[1][5][9][13]
[2][6][10][14]
[3][7][11][15]
[4][8][12][16]
...
...
In worst case I will try static method alignment of cells.
In my example is just a sample. Actually it must be a dynamic variable.
Just change scroll direction to Horizontal from Attribute Inspector as shown into below image:
And your result will be:
Related
I'm trying to create a simple collection view. I've got my custom cell "SectionCell" and the my custom class "Section", which contains two #IBOutlet properties: titleLabel and imageView. Both of these properties are hooked up to their respective storyboard views.
In storyboard, the collectionView scene has been linked to the MenuVC.swift file, which inherits from UICollectionView. The Cell view is linked to SectionCell. And I've set the cell's Identifier to "Section".
For debugging purposes I've set the view.backgroundColor to black and the cell's contentView background color to teal. Yet when I run the the simulator neither show. I get a white background and all that appears is the view title. Any ideas on what the fix is?
class MenuVC: UICollectionViewController {
var sections = [Section]()
override func viewDidLoad() {
super.viewDidLoad()
title = "Begin Learning"
view.backgroundColor = .black
}
// MARK:- CollectionView Methods
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Section", for: indexPath) as? SectionCell else {
fatalError("Unable to dequeue SectionCell ")
}
let section = sections[indexPath.item]
cell.titleLabel.text = section.title
cell.imageView.image = UIImage(named: section.image)
return cell
}
}
class SectionCell: UICollectionViewCell {
#IBOutlet var imageView: UIImageView!
#IBOutlet var titleLabel: UILabel!
}
class Section: NSObject {
var title: String
var image: String
init(title: String, image: String) {
self.title = title
self.image = image
}
}
Simulator
I'm also quite new to posting questions on SO. If you have any tips on how to better format questions, I'm all ears!
make outlet of your collectionview and give it to the delegate and data source...
make outlet like this in your view controller:
#IBOutlet weak var collectionView: UICollectionview!
then put this code in viewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
collectionView.relaodData()
}
have you implemented the following protocol?
UICollectionViewDelegateFlowLayout
extension MenuVC: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return .init(width: 300.0, height: 300.0) // and set size for each item inside this function.
}
}
SOLVED
LOL you really do need eagle eyes as a developer. This:
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3
}
is supposed to be:
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
And voila!
My purpose is to show the header string in each label of collectionview cell.But when I run the top cells which have already loaded are showing correct values in labels while after scrolling the reused cells are showing incorrect values.
Note : The data is fetched from a local file.
My data is as follows:
Trending : 20 items
Mustwatch : 3 items
Newly Added : 8 Items
Popular : 2 items
My code:
ViewController.swift
import UIKit
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
var videosList:[VideosJSONElement]?
var videosListInstance:VideosDataAccessModel?
#IBOutlet weak var categoriesTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
videosListInstance = VideosDataAccessModel()
videosList = videosListInstance?.videosList
categoriesTableView.dataSource = self
categoriesTableView.delegate = self
categoriesTableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return videosList!.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "categoriesCell") as! CategoriesVideoCell
cell.videos = videosList![indexPath.row].nodes
cell.position = indexPath.row
cell.categoryTitle.text = videosList![indexPath.row].title
cell.videosCollectionView.reloadData()
cell.sceneContext = self
return cell
}
}
CategoriesVideoCell.swift
import UIKit
class CategoriesVideoCell: UITableViewCell,UICollectionViewDelegate,UICollectionViewDataSource {
#IBOutlet weak var categoryTitle: UILabel!
var videos:[Node]?
var position:Int!
var sceneContext:UIViewController?
#IBOutlet weak var videosCollectionView: UICollectionView!
override func didMoveToSuperview() {
super.didMoveToSuperview()
videosCollectionView.dataSource = self
videosCollectionView.delegate = self
}
override class func awakeFromNib() {
super.awakeFromNib()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return videos!.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
var cell = collectionView.dequeueReusableCell(withReuseIdentifier: "videoCell", for:indexPath) as! VideoCVCell
cell.node = "\(categoryTitle.text!)"
return cell
}}
The first two cells have correct label text
The reused cells have incorrect data in the label
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 am new to iOS development and cannot seem to figure out why my collection view is spacing out. It almost seems as though a "margin-right" as in CSS has been applied to each cell. I have set the "Min Spacing" for cells and for lines to zero but the following is still occurring...
Screen shot of issue that is occurring
I have purposely set the collection view's background color to blue, the cell's background color to red and the label's border color to black to try to see what is what. The objective is to not see any of the 'blue' (collection view background), at all. Upon configuring the collection view, I resize the cell's width to fit the label's intrinsic width but it seems that the other cells' x-axis location stays the same... I need all the cells to come together. Any help would be greatly appreciated!
Current CollectionViewController code...
import Foundation
import UIKit
class HomeViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var menu = [
("NEWS"),
("NEIGHBORHOODS"),
("CULTURE"),
("DEVELOPMENT"),
("TRANSPORTATION"),
("CITIES")
]
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Testing"
self.navigationController?.navigationBarHidden = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return menu.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("UnderMenuCell", forIndexPath: indexPath) as! UnderMenuCollectionViewCell
cell.underMenuLabel.text = menu[indexPath.row]
let label_width = cell.underMenuLabel.intrinsicContentSize().width + 23
cell.underMenuLabel.layer.zPosition = 1000000
cell.underMenuLabel.layer.borderWidth = 2
cell.underMenuLabel.layer.borderColor = UIColor.blackColor().CGColor
cell.frame.size.width = label_width
return cell
}
}
You might need to set up layout attributes for the cells to specify their width:
override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
let attributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)
attributes.frame = yourFrameHere
attributes.size = yourSizeHere
return attributes
}
Also have you tried setting a breakpoint at cell.frame.size.width = label_width to see what width is actually being set?
I ended up figuring out a way that worked perfectly:
import Foundation
import UIKit
class HomeViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var underMenuCollection: UICollectionView!
var menu = [
("NEWS"),
("NEIGHBORHOODS"),
("CULTURE"),
("DEVELOPMENT"),
("TRANSPORTATION"),
("CITIES")
]
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Testing"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(true)
let scrollToIndexPath = NSIndexPath(forItem: 2, inSection: 0)
self.underMenuCollection.scrollToItemAtIndexPath(scrollToIndexPath, atScrollPosition: UICollectionViewScrollPosition.CenteredHorizontally, animated: true)
}
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return menu.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("UnderMenuCell", forIndexPath: indexPath) as! UnderMenuCollectionViewCell
cell.underMenuLabel.text = menu[indexPath.row]
return cell
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
let size: CGSize!
let testLabel = UILabel()
let labelText = menu[indexPath.row]
testLabel.text = labelText
testLabel.font = UIFont(name: "Avenir Next Condensed", size: 17)
testLabel.hidden = true
size = CGSize(width: testLabel.intrinsicContentSize().width + 13, height: self.underMenuCollection.frame.height)
return size
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
self.underMenuCollection.scrollToItemAtIndexPath(indexPath, atScrollPosition: UICollectionViewScrollPosition.CenteredHorizontally, animated: true)
}
}
So what happens is I create a hidden label that never evens gets added to the superview, populate it with the text to calculate the intrinsic width and then set the CGSize. It works perfectly...
Picture of collection view to the left
Picture of collection view to the right
The most important part in all this was the have equal spacing between all the cells dynamically based on the width of the string.
I want to create a screen layout . A main UICollectionView class scrolling horizontally on the top 1/3 of the screen, followed by a table on the bottom 2/3, but in the table I would like to slide multiple images horizontally, so I thought of putting another collection view in the table cell. I've connected the datasource and delegate for the main collection view to the main view controller and that works. the table view is connected up to the main view controller as well.
Screenshot of IB shows what I want to do, only able to do what is shown on left. Can only implement main collectionview and underneath table.
want to create main collectionview and table with collection view in cells with headers on top of each row.
When I connect the second collectionview( the one in the table) either to the main viewcontroller or to the custom tablecell, i get various different runtime errors.
When connected to the tableview I get this error:
UITableView collectionView:numberOfItemsInSection:]: unrecognized
selector sent to instance 0x7fb009064800
My code is attached. Unfortunately, I cannot get the UICollectionview to appear in the Table.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 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.
}
}
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate {
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("MainCell", forIndexPath: indexPath) as! CollectionViewCell
return cell
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("TableCell", forIndexPath: indexPath) as! TableViewCell
return cell
}
}
TableViewCell class
import UIKit
class TableViewCell: UITableViewCell {
#IBOutlet weak var subCollectionViewObject: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
extension TableViewCell: UICollectionViewDataSource, UICollectionViewDelegate {
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 15
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let subCell = collectionView.dequeueReusableCellWithReuseIdentifier("SubCell", forIndexPath: indexPath) as! SubCollectionViewCell
return subCell
}
}
import UIKit
class CollectionViewCell: UICollectionViewCell {
#IBOutlet weak var mainImage: UIImageView!
}
import UIKit
class SubCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var subImage: UIImageView!
}
Solved the issue using a scrollview inside a tableview. Easier for me to conceptualize and implement.