Can´t add items to UICollectionView inside UIView xib - ios

Objective
I wanna place my (BusinessViewTableHeader: UIView) as tableView header:
tableView.tableHeaderView = BusinessViewTableHeader.instanceFromNib() as! BusinessViewTableHeader
Inside BusinessViewTableHeader there is a UICollectionView which are supposed to display images when swiped, much like the Tinder app.
This is my UIView subclass:
class BusinessViewTableHeader: UIView {
#IBOutlet var collectionView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
self.collectionView.delegate = self
self.collectionView.registerNib(UINib(nibName: "BusinessImageCollectionCell", bundle: nil), forCellWithReuseIdentifier: "BusinessImageCollectionCell")
}
class func instanceFromNib() -> UIView {
return UINib(nibName: "BusinessViewTableHeader", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as! UIView
}
....
}
extension BusinessViewTableHeader: UICollectionViewDelegate, UICollectionViewDataSource {
....
}
Problem
I have a custom UIView xib containing a UICollectionView. The problem is that I can´t add any cells (items) to the UICollectionView. I can add items to my other UICollectionView which are placed inside a UIViewController. The first image is showing the properties for the UICollectionView inside a UIViewController, the second image is showing the UICollectionView inside a UIView xib.
[![UICollectionView in UIViewController][1]][1]
[1]: http://i.stack.imgur.com/zFCeG.png
[![UICollectionView in UIView xib][2][2]
[2]: http://i.stack.imgur.com/jKU6z.png
Question
Why am I not able to add items to the UICollectionView inside the UIView xib? How?

You can't have UICollectionViewCell when the UICollectionView is on a Nib. What you need to do is to create the UICollectionViewCell as another nib and get it registered in the class that you are using for your CollectionView.
Create a new nib, drag a UICollectionViewCell inside it, and do something like this in the class that works with your UICollectionView.
override func awakeFromNib() {
let nibName = UINib(nibName: "ClassCollectionCell", bundle:nil)
collectionView.registerNib(nibName, forCellWithReuseIdentifier: "collectionCell")
}
Remember you can add a custom class to the UICollectionViewCell so you can pass dynamic data to it.

Adding cells in a xib is not supported. If you must use a xib file, then you will need a separate xib which contains the UICollectionView cell. Storyboards may be a better solution.
It is not clear what you are trying to achieve. UICollectionView has specific means for creating headers which uses the datasource and delegate. Collection views are good for displaying items in a grid layout or other complex arrangements.
If all you need is to display a list of rows, then a UITableViewController might be an easier alternative.
Whatever the case, it is probably better to use a storyboard instead of a xib, and to subclass the UICollectionViewController or UITableViewController, rather than a subview.
Your custom class name can be entered in the identity inspector for the UIViewController or UIView:

In Swift 3.0 register nib with following method-
let nibName = UINib(nibName: "FruitCell", bundle:nil)
collectionView.register(nibName, forCellWithReuseIdentifier: "CellIdentifier")

In Swift 3.0
first Create Xibfile of UIView
import UIKit
class SubCatagoryListView:UIView , UICollectionViewDelegate , UICollectionViewDataSource
{
#IBOutlet weak var mainView: UIView!
#IBOutlet weak var btnClose: UIButton!
#IBOutlet weak var subCategoryListCollectionView: UICollectionView!
#IBOutlet weak var lblTitle: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
subCategoryListCollectionView.register(UINib(nibName: "SubcatagoryListCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "SubcatagoryListCollectionViewCell")
mainView.layer.cornerRadius = 10.0
mainView.clipsToBounds = true
}
static func subCatagoryListView() -> SubCatagoryListView? {
let arr = Bundle.main.loadNibNamed("SubCatagoryListView", owner: self, options: nil)
if arr != nil {
if arr!.count > 0 {
if let view = arr![0] as? SubCatagoryListView {
return view;
}
}
}
return nil;
}
#IBAction func btnBackgroundTapped(_ sender: UIButton)
{
self.removeFromSuperview()
}
#IBAction func btnCloseTapped(_ sender: UIButton)
{
self.removeFromSuperview()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SubcatagoryListCollectionViewCell", for: indexPath) as! SubcatagoryListCollectionViewCell
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
let cellsize = CGSize(width: (subCategoryListCollectionView.bounds.size.width/3) - 10, height: 50)
return cellsize
}
}
After Create new CollectionViewCell Xib file
import UIKit
class SubcatagoryListCollectionViewCell: UICollectionViewCell {
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
after create both file i am load xib on my storybord
var subcatagoryXib:SubCatagoryListView?
override func awakeFromNib() {
super.awakeFromNib()
if let subcategoryView = SubCatagoryListView.subCatagoryListView()
{
subcatagoryXib = subcategoryView
}
}
#IBAction func btnAddInterestTapped(_ sender: UIButton) {
if subcatagoryXib != nil
{
self.subcatagoryXib!.frame = CGRect(x:0, y: 0, width: self.view.frame.width , height: self.view.frame.height)
self.view.addSubview(self.subcatagoryXib!)
}
}

Same as #Pato's answer, but here is a more thorough tutorial for how to add a customized UICollectionViewCell inside a Xib file from #aestusLabs on Medium. It's a 3-5 min reading, I personally find it very helpful. It basically tells you to create another customized UICollectionViewCell with .xib, and register it in your "level 1" cell's awakeFromNib().
https://medium.com/#aestusLabs/adding-a-uicollectionviews-to-a-custom-uitableviewcell-xib-tutorial-swift-4-xcode-9-2-1ec9ce4095d3

Related

Swift 4: Xib with collection view that reuse cell from storyboard, inner outlets are nils

I have a cell that was used in main storyboard collection view:
class TaskCell: UICollectionViewCell {
#IBOutlet weak var exerciseTitle: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
self.layer.borderWidth = 1
self.layer.borderColor = #colorLiteral(red: 0.6666666865, green: 0.6666666865, blue: 0.6666666865, alpha: 1)
}
}
I created a XIB with collection view inside and I am trying to use this cell inside XIB`s collection view. The cell is dequeued, but all outlets inside it are nils.
class AddTaskToSessionVC: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var collectionView: UICollectionView!
public var allTasks = [TaskCodableEntity]()
override func awakeFromNib() {
super.awakeFromNib()
}
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView.delegate = self
self.collectionView.dataSource = self
self.collectionView.register(TaskCell.self, forCellWithReuseIdentifier: "TaskCell")
self.getCurrentPatientTasks()
// Do any additional setup after loading the view.
}
public func getCurrentPatientTasks() {
self.allTasks = API.getTasks()
}
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return self.collectionView.showNoDataLabel(array: self.allTasks)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.allTasks.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = self.collectionView.dequeueReusableCell(withReuseIdentifier: "TaskCell", for: indexPath) as? TaskCell {
let taskEntity = self.allTasks[indexPath.row]
cell.exerciseTitle.text = taskEntity.title
***(Here exerciseTitle is nil)***
return cell
}
return UICollectionViewCell()
}
}
My XIB is look like:
I tried to instantiate label inside cell, but it still stay as nil
Is there is a way to reuse cells in such way or I should create XIB cell despite of already exists cell?
For your easily work with view with xib and it's reuse, I recommend you to see framework Reusable
I think in this quote you can find answer on you question. I think you need to make file owner(not view) for your class and you can use NibOwnerLoadable and Reusable protocols to reuse and load your view more easily.
Declare your views to conform to NibLoadable or NibOwnerLoadable
Use the NibLoadable protocol if the XIB you're using don't use its
"File's Owner" and the reusable view you're designing is the root view
of the XIB
Use the NibOwnerLoadable protocol if you used a "File's
Owner" of the XIB being of the class of your reusable view, and the
root view(s) of the XIB is to be set as a subview providing its
content.
Hope it help you!
You need to register the xib like this
collectionView.register(UINib(nibName: "TaskCell", bundle: nil), forCellWithReuseIdentifier: "TaskCell")
as this
TaskCell.self
used when the cell is created programmatically

iOS Swift: Loading a XIB file containing a UICollectionView causing nil error?

I created a UICollectionView in an .xib file like so:
Based on this post: Can´t add items to UICollectionView inside UIView xib
I understand that you cannot directly add a UICollectionViewCell within a UICollectionView that is contained in a .xib, but instead have to create another .xib with only the UICollectionViewCell, which is what I did:
I create a GridViewCell class and add it as the Custom Class for the UICollectionViewCell:
class GridViewCell: UICollectionViewCell
{
#IBOutlet var clothingImageView: UIImageView!
override func awakeFromNib()
{
super.awakeFromNib()
// Initialization code
}
}
I also created a GridViewGallery class and associated with my UICollectionView .xib file:
extension GridViewGallery: UICollectionViewDelegate
{
}
extension GridViewGallery: UICollectionViewDataSource
{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return clothingImages.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "GridViewCell", for: indexPath) as! GridViewCell
cell.clothingImageView.image = clothingImages[indexPath.row].image
return cell
}
}
class GridViewGallery: UIView
{
#IBOutlet var gridLayoutCollectionView: UICollectionView!
var clothingImages = [INSPhotoViewable]()
override init(frame: CGRect)
{
super.init(frame: frame)
}
required public init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
let gridViewCell = UINib(nibName: "GridViewCell", bundle: nil)
gridLayoutCollectionView.register(gridViewCell, forCellWithReuseIdentifier: "GridViewCell")
}
func storeGridViewImages(photos: [INSPhotoViewable])
{
clothingImages = photos
gridLayoutCollectionView.dataSource = self
gridLayoutCollectionView.delegate = self
}
}
However, I get a unexpectedly found nil while unwrapping an Optional value error when I tried to load the XIB file to a UIView in another class:
#IBAction func gridViewLayout(_ sender: UIBarButtonItem)
{
if let gridView = Bundle.main.loadNibNamed("GridViewGallery", owner: self, options: nil)?.first as? GridViewGallery
{
print("GRID VIEW FOUND")
self.addSubview(gridView)
}
}
The GridViewGallery File Owner's Custom Class is empty, as I set the Custom Class on the UIView property instead as seen in the first screenshot:
However, if I instead set the File Owner's Custom Class to GridViewGallery and leave the Custom Class as blank from the UIView itself, then I get a crash with error this class is not key value coding-compliant for the key gridLayoutCollectionView.
I'm not sure I understand what is causing this, and which way I should be setting the Custom Class.
Can someone assist me?
Thanks.

didSelectRowAtIndexPath not called in TableView defined in SubView defined in xib

I have been trying to create a custom view loaded through xib that contains a button and tableview. The table view is shown or hidden when button is pressed.
This interaction works and the table is created / shown. The problem I have is that I can not click on the table rows.
I have been looking all over and haven't found a solution that works.
I made sure that delegate and dataSource are set. I also do not have a GestureRecognizer for the ViewController it is used in that could absorb the touch.
Does anybody have an idea what I am missing?
Here is the code of this custom view:
class SubUnitSpinner : UIView, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var spinnerTableView: UITableView!
let subUnitNames: [String] = ["World", "Add/Remove"]
override init( frame: CGRect ) {
super.init(frame: frame)
loadViewFromNib()
setupTableView()
}
required init?( coder aDecoder: NSCoder ) {
super.init(coder: aDecoder)
loadViewFromNib()
setupTableView()
}
func setupTableView() {
spinnerTableView.delegate = self
spinnerTableView.dataSource = self
spinnerTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
spinnerTableView.rowHeight = 30
spinnerTableView.userInteractionEnabled = true
spinnerTableView.allowsSelection = true
spinnerTableView.hidden = true
}
func loadViewFromNib() {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "SubUnitSpinner", bundle: bundle)
let xibView = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
xibView.frame = bounds
xibView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
self.addSubview(xibView)
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = spinnerTableView.dequeueReusableCellWithIdentifier("Cell")! as UITableViewCell;
cell.userInteractionEnabled = true
cell.textLabel?.text = subUnitNames[indexPath.row]
cell.tag = indexPath.row
return cell;
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//Interaction
print("cell with path: \(indexPath.row)")
}
#IBAction func spinnerTabbed(sender: AnyObject) {
spinnerTableView.hidden = !spinnerTableView.hidden
} }
Update:
View Layout creation:
The xib view layout has been defined in the "storyboard" and the File's Owner set to SubUnitSpinner. The IBOutlet and IBAction where created by ctrl drag and drop.
Usage in UIViewController:
I use it as part of a UIViewController which also has been defined in the storyboard. I added a UIView and declared the Custom Class to be SubUnitSpinner.
The SubUnitSpinner with layout as defined in xib shows up when running it and the button is clickable, the UITableView is shown / hidden when button is shown. The only thing not working is clicking on the tableView cells.
Is something wrong with the setup?
I just took your code and added to a dummy project to check. I did this for your method
required init?( coder aDecoder: NSCoder ) {
super.init(coder: aDecoder)
// removed the load from nib and setup tableview
}
In my case I did the following to add the SubUnitSpinner view to my parent view. Hope you did the same
let aTestView = SubUnitSpinner(frame: CGRectMake(50, 200, 200, 200))
view.addSubview(aTestView)
Also double check if you have connected any delegate and datasource from xib. All in all, your code looks fine and I was able to click on it properly. The below are my results that I achieved.

UICollectionViewCell awakeFromNib never called even though registerNib is called

The code below attempts to create a UICollectionView within a custom view. Since the UICollectionView is not created in Storyboard, it means creating the custom UICollectionViewCells in a XIB.
However, the awakeFromNib function is never called for the custom UICollectionViewCell, which causes issues in rendering the UICollectionView.
How to fix this?
CustomView.swift:
class CustomView : UIView, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var collectionView: UICollectionView!
private func doInit() {
if let nibsView = NSBundle.mainBundle().loadNibNamed("CustomCellView", owner: self, options: nil) as? [UIView] {
let nibRoot = nibsView[0]
self.addSubview(nibRoot)
nibRoot.frame = self.bounds
// Register UICollectionViewCell NIB
collectionView.registerNib(UINib(nibName: CustomCellNib, bundle: nil), forCellWithReuseIdentifier: CustomCellID)
}
}
}
CustomCell.swift:
class CustomCell: UICollectionViewCell {
override func awakeFromNib() {
super.awakeFromNib()
doInit(frame)
}
}
change you line of code to be
collectionView.register(UINib(nibName: CustomCellNib, bundle: nil), forCellWithReuseIdentifier: CustomCellID)

UICollectionView + custom cell

please help me with custom cell in collection
i have an error:
..this class is not key value coding-compliant for the key cell_label..
you can download full project here:
https://drive.google.com/file/d/0B1nXU0xmeKg8WTRYRXJVbWdMVlU/view?usp=sharing
i want to show collection with custom cell in modalview
PS: i use xcode 7 beta and swift 2 and separate xib for every view
import UIKit
class ModalController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
#IBOutlet weak var myCollection: UICollectionView!
#IBOutlet weak var myCell: colywCell!
var tableData: [String] = ["XXX", "YYY", "ZZZ"]
override func viewDidLoad() {
super.viewDidLoad()
self.title="modal W"
myCollection.dataSource = self
myCollection.delegate = self
let nipName=UINib(nibName: "colywCell", bundle:nil)
myCollection.registerNib(nipName, forCellWithReuseIdentifier: "cell1")
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return tableData.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
self.myCell = myCollection.dequeueReusableCellWithReuseIdentifier("cell1", forIndexPath: indexPath) as! colywCell
self.myCell.Cell_label.text = tableData[indexPath.row]
return self.myCell
}
}
and custom cell file
import UIKit
class colywCell: UICollectionViewCell {
#IBOutlet weak var Cell_label: UILabel!
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
you need to aware of who the owner is, and which class should be loaded with the nib.
in nutshell, you need to aware of those in your colywCell.nib:
1a.
make sure that the File's Owner is not a custom class.
1b.
make sure to disconnect every outlet from File's Owner.
2a.
make sure that the actual UICollectionViewCell is your custom class.
2b.
optionally connect the outlets to your custom class.
This is a common error, it usually means that you have an Outlet set on your view that is not binded to any IBOutlet on your Cell.
This happens when you bind a view with some var in your class and then you remove that var and not it's bind on your xib.

Resources