Manually add delegate & datasource to UICollectionView of xib file - ios

I put the UICollectionView on the UIView in the xib file.
UICollectionView is installed (GUI) in the xib file, and IBOutlet is connected.
In this case, how should we write delegate and dataSource?
(Conjecture: Perhaps, I have to write it manually as a code on my own)
Also, we could not attach Cell to UICollectionView in xib.
Does that mean i do not have to put it?
Collection.swift
//Collection.swift
import UIKit
class Home: UIView, UICollectionViewDelegate, UICollectionViewDataSource{
#IBOutlet weak var propCollectionView: UICollectionView!
let photos = ["sierra"] //Already put to AssetsFolder
//Just instance. plz do not care about this.
class func instance() -> Home {
return UINib(nibName: "Home", bundle: nil).instantiate(withOwner: self, options: nil)[0] as! Home
}
func initValue(){
propCollectionView.register(UINib(nibName: "PropCell", bundle: nil), forCellWithReuseIdentifier: "PropCell")
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = propCollectionView.dequeueReusableCell(withReuseIdentifier: "PropCell", for: indexPath) as! PropCell
let propImage = cell.contentView.viewWithTag(1) as! UIImageView
let img = UIImage(named: photos[indexPath.row])
propImage.image = img
return cell
}
}
PropCell.swift
//PropCell.swift
//yeah, nothing. plz don't care this too.
import UIKit
class PropCell: UICollectionViewCell {
override func awakeFromNib() {
super.awakeFromNib()
}
}
Xib of CollectionSwift
Xib of PropCell
(If you do not understand the meaning of the question, please comment.)

import UIKit
class Home: UIView, UICollectionViewDelegate, UICollectionViewDataSource{
#IBOutlet weak var propCollectionView: UICollectionView!
let photos = ["sierra"] //Already put to AssetsFolder
//Just instance. plz do not care about this.
class func instance() -> Home {
return UINib(nibName: "Home", bundle: nil).instantiate(withOwner: self, options: nil)[0] as! Home
}
func configureView() {
propCollectionView.delagate = self
propCollectionView.dataSource = self
}
func initValue(){
propCollectionView.register(UINib(nibName: "PropCell", bundle: nil), forCellWithReuseIdentifier: "PropCell")
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = propCollectionView.dequeueReusableCell(withReuseIdentifier: "PropCell", for: indexPath) as! PropCell
let propImage = cell.contentView.viewWithTag(1) as! UIImageView
let img = UIImage(named: photos[indexPath.row])
propImage.image = img
return cell
}
}
USES
let home = Home.instance()
home.configureView() //called configureView() with the home instance

Related

how to pass data from cell to another ViewController

#TusharMordiya Please check this image
I have created a tableView inside a CollectionView.The contents of the view are UIImage and a UILabel.I want to design the cell in which when I click on a cell the image and label must go to another ViewController.
import UIKit
class ExploreTableViewCell: UITableViewCell, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
#IBOutlet var collectionView: UICollectionView!
var namearr = ["images-1", "images-2", "images-3", "images-4", "images-5"]
override func awakeFromNib() {
super.awakeFromNib()
collectionView.delegate = self
collectionView.dataSource = self
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 132, height: 134)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = UIStoryboard(name: "DetailViewController", bundle: nil).instantiateViewController(withIdentifier:"DetailViewController") as? DetailViewController
vc?.name = namearr[indexPath.row]
vc?.img.image = UIImage(named: namearr[indexPath.row])
//self.navigationController?.pushViewController(vc, animated: true)
// showing error in here and while declaring object of viewcontroller
}
}
Please try this.
Add function for get top most view controller
extension UIApplication {
class func topViewController(_ viewController: UIViewController? = UIApplication.shared.connectedScenes
.filter({$0.activationState == .foregroundActive})
.compactMap({$0 as? UIWindowScene})
.first?.windows
.filter({$0.isKeyWindow}).first?.rootViewController) -> UIViewController? {
if let nav = viewController as? UINavigationController {
return topViewController(nav.visibleViewController)
}
if let tab = viewController as? UITabBarController {
if let selected = tab.selectedViewController {
return topViewController(selected)
}
}
if let presented = viewController?.presentedViewController {
return topViewController(presented)
}
return viewController
}
}
Use this code in your didSelectItemAt method
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
if let vc = storyBoard.instantiateViewController(withIdentifier:"DetailViewController") as? DetailViewController {
vc.name = namearr[indexPath.row]
print("You tapped the cell\(indexPath) with car name \(namearr[indexPath.row]) ")
UIApplication.topViewController()?.navigationController?.pushViewController(vc, animated: true)
}
}
In your Detail Screen
class DetailViewController: UIViewController {
#IBOutlet var lbl: UILabel!
#IBOutlet var img: UIImageView!
var name = ""
override func viewDidLoad() {
super.viewDidLoad()
lbl.text = name
img.image = UIImage(named: name)
}
}
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier:"DetailViewController") as? DetailViewController
change storyBoard name to Main

IBOutelt found nil when UICollectionViewCell is load

I am studying how to create a simple app without storyboard and I have a problem:
I created a XIB called HomeViewController, inside it has a UICollectioView and made the registration of the cell in the viewDidLoad().
after that, I created a cell called HomeCollectionViewCell, put the identifier as "cell", in the cell it has a UILabelOutlet, that is referenced in the .swift file.
After setting the delegate and the datasource, it has the mandatory methods of the datasource, when making the cell configuration and calling the label by setting any text it returns nil. I don't know why this is happening, I've seen several videos on YouTube and none has solved the problem. Can anybody help me?
HomeViewController
class HomeViewController: UIViewController {
#IBOutlet weak var collectionView: UICollectionView!
var info = ["Image"]
override func viewDidLoad() {
super.viewDidLoad()
collectionView.register(HomeCollectionViewCell.self, forCellWithReuseIdentifier:
"cell")
collectionView.delegate = self
collectionView.dataSource = self
}
}
extension HomeViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection
section: Int) -> Int {
return info.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath:
IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for:
indexPath) as? HomeCollectionViewCell
cell?.teste.text = "test"
return cell!
}
}
HomeCollectionViewCell
class HomeCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var teste: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
}
That's because you have to register cell's nib not just class:
collectionView.register(UINib(nibName: "HomeCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "cell")

Why is my UICollectionView not showing at all?

I am using storyboards, and my UICollectionView with a basic custom cell is not showing at all? My simulator is constantly running a version of my storyboard when my "Next" button was at the middle of the screen, obviously now it is at the bottom as shown here along with all the identities and links created in the storyboard: https://imgur.com/a/R8iTm9n
import UIKit
class CategoryViewController: UIViewController{
#IBOutlet weak var categoryCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
categoryCollectionView.delegate = self
categoryCollectionView.dataSource = self
NetworkingClient.fetchRecipeCategories{ (recipeCategories) in
//print(recipeCategories)
}
categoryCollectionView.reloadData()
}
}
extension CategoryViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
collectionView.deselectItem(at: indexPath, animated: true)
print("Taptaptap")
}
}
extension CategoryViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 12
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var cell = UICollectionViewCell()
if let testCell = collectionView.dequeueReusableCell(withReuseIdentifier: "categoryCell", for: indexPath) as? CategoryCollectionViewCell{
testCell.configure(with: indexPath.row)
cell = testCell
print("test1")
}
print("test2")
return cell
}
}
/*
extension CategoryViewController: UICollectionViewDelegateFlowLayout {
}
*/
And my my custom view cell :
class CategoryCollectionViewCell: UICollectionViewCell {
static let identifier: String = "CategoryCollectionViewCell"
#IBOutlet weak var categoryImageView: UIImageView!
#IBOutlet private weak var testLabel: UILabel!
func configure(with id: Int){
testLabel.text = String(id)
categoryImageView.clipsToBounds = true
categoryImageView.contentMode = .scaleAspectFit
}
}
Sorry for the formatting...
Register your custom collectionview cell named "categoryCell" in viewDidLoad()
categoryCollectionView.register(UINib(nibName: "categoryCell", bundle:
nil),forCellWithReuseIdentifier: "categoryCell")
categoryCollectionView.delegate = self
categoryCollectionView.dataSource = self
categoryCollectionView.reloadData()

Choose cell in collection view to new controller

Very new to code and making a project programmatically. I have my collection view all set up but i cant figure out how to tell a specific cell to navigate to a new collection view or detail view. Very confused and frustrated. can anyone help me or at least point me in the right direction. please dumb it down haha.
this is my main View controller
import UIKit
class HomeController: UICollectionViewController,
UICollectionViewDelegateFlowLayout {
var Legends: [Legend] = {
var select1 = Legend()
select1.thumbnailImageName = "select1thumbnail"
var select2 = Legend()
select2.thumbnailImageName = "select2humbnail"
return[select1, select2]
}()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Choose Selection"
collectionView.backgroundView = UIImageView(image: UIImage(named: "backgroundlogo"))
collectionView?.register(VideoCell.self, forCellWithReuseIdentifier: "cellId")
collectionView.dataSource = self
collectionView.delegate = self
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Legends.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! VideoCell
cell.legend = Legends[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width:view.frame.height, height: 150)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let select2VC = UIViewController()
navigationController?.navigationBar.tintColor = UIColor.white
navigationController?.pushViewController(select2VC, animated: true)
print("selcected")
}
}
this is the swift file i have for my collection view
import UIKit
class VideoCell: UICollectionViewCell {
var legend: Legend? {
didSet {
thumbnailImageView.image = UIImage(named: (legend?.thumbnailImageName)!)
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
let thumbnailImageView: UIImageView = {
let imageView = UIImageView()
imageView.backgroundColor = UIColor.darkGray
imageView.image = UIImage(named:"bgcolor")
imageView.contentMode = .scaleAspectFit
imageView.clipsToBounds = true
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
func setupViews() {
addSubview(thumbnailImageView)
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-16-[v0]-16-|", options: NSLayoutConstraint.FormatOptions(), metrics: nil, views: ["v0": thumbnailImageView]))
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-1-[v0]-0-|", options: NSLayoutConstraint.FormatOptions(), metrics: nil, views: ["v0": thumbnailImageView]))
}
}
in a 3rd file i have just this bit of code
import UIKit
class Legend: NSObject {
var thumbnailImageName: String?
}
the bottom code of the main view controller does print out a "selected" but it prints it for every cell... I assume each cell will need its own viewController.swift file? then tell that cell to that swift file to show the contents?? thank you for your time.
No you don't need to create its own viewController.swift file for each cell. You just need to create one detail page screen (one DetailViewController.swift) and you need to pass the detail of selected cell with variables declared on detail view controller.
Like I have done HERE in demo project with your code and result will be:
What I have done is I have added detailViewController in storyboard and also added class file. and didSelectItemAt will look like:
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
controller.selectedIndex = indexPath.row //pass selected cell index to next view.
self.navigationController?.pushViewController(controller, animated: true)
}
Here you can pass data with controller.selectedIndex = indexPath.row (Herer I am passing selected index) because selectedIndex is a property of DetailViewController because I have declared it in DetailViewController.swift like
var selectedIndex = 0
And same way you can pass other data as well which is related with selected index.
For more info refer demo project.
If you want check what is sell pressed. You can check what is type cell pressed:
if let cell = tableView.cellForRow(at: indexPath) as? VideoCell {
//check some property and go to new UIViewController or UICollectionViewController
//and transfer data
}

How to Fix the error: Generic parameter 'T' could not be inferred

I am doing a tutorial, and have one problem with error:
Generic parameter 'T' could not be inferred.
****>> I just update the question follow your advice.
And added 2 line code:
let cell: TacoCell = collectionView.dequeReusableCell(forIndexPath: indexPath)
cell.configureCell(taco: ds.tacoArray[indexPath.row])
return cell
And i also modify this code:
func dequeReusableCell to dequeReusableCell
In my UICollectionView+Ex.swift file.
import UIKit
extension UICollectionView {
func register<T: UICollectionViewCell>(_: T.Type) where T: ReusableView, T: NibLoadableView {
let nib = UINib(nibName: T.nibName, bundle: nil)
register(nib, forCellWithReuseIdentifier: T.reuseIdentifier)
}
func dequeReusableCell<T: UICollectionView>(forIndexPath indexPath: IndexPath) -> T where T: ReusableView{
guard let cell = dequeueReusableCell(withReuseIdentifier: T.reuseIdentifier, for: indexPath as IndexPath) as? T else {
fatalError("Coud not deque cell with Identifier: \(T.reuseIdentifier)")
}
return cell
}
}
extension UICollectionViewCell: ReusableView {}
and in my MainVC.swift, I have the following code:
import UIKit
class MainVC: UIViewController, DataServiceDelegate {
#IBOutlet weak var headerView: HeaderView!
#IBOutlet weak var collectionView: UICollectionView!
var ds: DataService = DataService.instance
override func viewDidLoad() {
super.viewDidLoad()
ds.delegate = self
ds.loadDeliciousTacoData()
collectionView.dataSource = self
collectionView.delegate = self
headerView.addDropShadow()
/*
let nib = UINib(nibName: "TacoCell", bundle: nil)
collectionView.register(nib, forCellWithReuseIdentifier: "TacoCell")
*/
collectionView.register(TacoCell.self)
}
func deliciousTacoDataLoaded() {
print("Delicious Taco Data Loaded!")
}
}
extension MainVC: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return ds.tacoArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TacoCell", for: indexPath) as? TacoCell {
// cell.configureCell(taco: ds.tacoArray[indexPath.row])
// return cell
// }
// return UICollectionViewCell()
let cell: TacoCell = collectionView.dequeReusableCell(forIndexPath: indexPath)
cell.configureCell(taco: ds.tacoArray[indexPath.row])
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 95, height: 95)
}
}
At this line:
let cell = collectionView.dequeReusableCell(forIndexPath: indexPath) as TacoCell
the compiler complains.
Why this error was showed, and how to fix it? Thank in advance.
==> After do like your advice, now i can build success.
My problem is soved. I still implement something, if have more any issue, i will update again. Many thanks!
You should let the generic constraint for dequeReusableCell to be: <T: UICollectionViewCell> instead of <T: UICollectionView>:
func dequeReusableCell<T: UICollectionViewCell>(forIndexPath indexPath: IndexPath) -> T where T: ReusableView{
Obviously, I would assume that TacoCell is type of ReusableView.
Fix the typo(s)
func dequeueReusableCell<T: UICollectionViewCell>( ...
Annotate the type and remove the type cast
let cell : TacoCell = collectionView.dequeueReusableCell(forIndexPath: indexPath)

Resources