Setting delegate to self for the collection view after present action - ios

There are 2 pages in Main.storyboard: FirstViewController and SecondViewController
There is a button in FirstViewController. Here is the button click scope:
let controller = SecondViewController()
present(controller, animated: true, completion: nil)
And there is a collection view component in SecondViewController.
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var collectionViewOutlet: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
collectionViewOutlet.delegate = self
collectionViewOutlet.dataSource = self
let xib = UINib(nibName: "ViewCell", bundle: nil)
self.collectionViewOutlet.register(xib, forCellWithReuseIdentifier: "ViewCellID")
let v0 = self.storyboard?.instantiateViewController(withIdentifier: "subView") as! SubViewController
temp_containerViewController = v0
}
}
extension MainPageViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
{
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.myPageList.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionViewOutlet.dequeueReusableCell(withReuseIdentifier: "ViewCellID", for: indexPath) as! ViewCell
let viewController = self.temp_containerViewController
cell.contentViewOutlet = viewController?.view
cell.contentViewOutlet.backgroundColor = .brown
cell.lblOutlet.text = self.myPageList[indexPath.row]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return collectionViewOutlet.frame.size
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let index = self.collectionViewOutlet.contentOffset.x / self.collectionViewOutlet.frame.size.width
self.pageControlOutlet.currentPage = Int(index)
self.pageControlOutlet.numberOfPages = myPageList.count
}
}
but below code gives an error:
collectionViewOutlet.delegate = self
How can I fix this problem? I have created an outlet for the collection view component as you see above.
Here is the error sentence:
Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value

You need to load this line
let controller = SecondViewController()
From storyboard ( Give it identifier Second )
let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Second") as! SecondViewController
As you have outlets without XIB

Add a condition in numberOfItemsInSection func
Step 1.
if self.myPageList.count > 0 {
return self.myPageList.count
}else {
return 0
}
Step2. Set identifier
let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Second") as! SecondViewController
Hope Its Help

Related

UICollectionView not displaying, cellForItemAt and numberOfItemsInSection not called

I'm trying to make an app which will display a popup with of devices with "right" or "left" names depending on whether left or right button was pressed. However, after I press left or right button, the popup appears with empty CollectionView. I colored it green to make sure that it's size is not {0, 0} and it isn't.
Popup screen
Also, I tried to make sure that cells are not bigger than the CollectionView, and it seems, that they are not. I can't put size restraints on them, though, so maybe they get resized or something.
Storyboard
I tried understanding and using solution for that question, but it seems that my situation is somewhat different. In this question cellForItemMethod was called, but in my case it wasn't and I've got no subclass of UICollectionView, which is, I guess, why I cant override intrinsicContentSize.
Here is the code for PopUpViewController:
import UIKit
protocol PopUpDelegate {
func connect(device: CollectionCell)
}
class PopUpController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
static let identifier: String = "PopUpController"
var parentVC: ViewController?
var left: Bool?
let reuseIdentifier = "cell"
var delegate: PopUpDelegate?
var lastSelectedCell: CollectionCell? = nil
#IBOutlet weak var collectionView: UICollectionView!
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print("number of items in section called")
if (left!) {
return (parentVC!.leftDevices.count)
} else {
return (parentVC!.rightDevices.count)
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
print("cell for item called")
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! CollectionCell
print("left: ", left!)
if (left!) {
cell.displayDevice(device: parentVC!.leftDevices[indexPath.row])
} else {
cell.displayDevice(device: parentVC!.rightDevices[indexPath.row])
}
cell.backgroundColor = UIColor.white
cell.selectButton.setTitleColor(UIColor.black, for: UIControl.State.normal)
cell.selectButton.setImage(UIImage(named: "unselectedButton"), for: UIControl.State.normal)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("You selected cell #\(indexPath.item)!")
let cell = collectionView.cellForItem(at: indexPath) as! CollectionCell
cell.setSelected()
lastSelectedCell?.setUnselected()
lastSelectedCell = cell
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
print("parent has ", parentVC?.rightDevices.count, " items")
collectionView.reloadData()
}
static func showPopup(parentVC: UIViewController, left: Bool){
//creating a reference for the dialogView controller
if let popupViewController = UIStoryboard(name: "PopUp", bundle: nil).instantiateViewController(withIdentifier: "PopUpController") as? PopUpController {
popupViewController.modalPresentationStyle = .custom
popupViewController.modalTransitionStyle = .crossDissolve
//setting the delegate of the dialog box to the parent viewController
popupViewController.delegate = parentVC as? PopUpDelegate
popupViewController.left = left
parentVC = parentVC as? ViewController
print("showing left popup: ", left)
//presenting the pop up viewController from the parent viewController
parentVC.present(popupViewController, animated: true)
}
}
#IBAction func onConnectPressed(_ sender: Any) {
self.delegate?.connect(device: lastSelectedCell!)
self.dismiss(animated: true, completion: nil)
}
#IBAction func onDismissPressed(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
}
Check again leftDevices and rightDevices data passing between ViewController to PopUpController screen and conform delegate and datasources.
collectionView.delegate = self
collectionView.dataSource = self

Error: Could not cast value of type 'UINavigationController'

I want to present a view controller in FavouriteButtonHandler on a button press.
When I press the button, I get the following error:
Could not cast value of type 'UINavigationController' (0x11177fed8)
I searched for the cause of this error but do not know how to fix it.
View controller code follows:
import UIKit
#available(iOS 11.0, *)
class FirstARViewController: UIViewController , UICollectionViewDelegate , UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
#IBOutlet weak var collectionView: UICollectionView!
var imagescv = ["ar1","ar2" ]
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imagescv.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! cellimagesar
cell.layer.cornerRadius = 5
cell.layer.borderWidth = 1
cell.myImages.image = UIImage(named: imagescv [indexPath.row])
cell.myImages.contentMode = .scaleToFill
cell.buttonMove.tag = indexPath.item
cell.buttonMove.addTarget(self, action: #selector(self.FavouriteButtonHandler), for: .touchUpInside)
//Declaring cell
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: 171, height: 250)
}
#objc func FavouriteButtonHandler (sender: UIButton)
{
let VcToPresent = self.storyboard?.instantiateViewController(withIdentifier: "PDFViewController") as! PDFViewController
switch sender.tag {
case 0:
//here you selected Button with tag 0
//Time to update value of Global Struct
ButtonSelected.Tag = 0
//Time to present controller that will handle your pdf file and view it
self.navigationController?.pushViewController(VcToPresent, animated: true)
break
case 1:
//here you selected Button with tag 1
//Time to update value of Global Struct
ButtonSelected.Tag = 1
//Time to present controller that will handle your pdf file and view it
self.navigationController?.pushViewController(VcToPresent, animated: true)
break
default:
print("Error case")
}
}
}
And I get the error in this line :
let VcToPresent = self.storyboard?.instantiateViewController(withIdentifier: "PDFViewController") as! PDFViewController
It's working perfectly for me :
Pushing the PDFViewController into Navigation Stack :
if let presentVC = self.storyboard?.instantiateViewController(withIdentifier: "PDFViewController") as? PDFViewController{
self.navigationController?.pushViewController(presentVC, animated: true)
}
OR
if let presentVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PDFViewController") as? PDFViewController {
self.navigationController?.pushViewController(presentVC, animated: true)
}
Please, make sure that your current viewController should belong to the top of Navigation stack before pushing anything on it.

Opening another view controller using delegate?

I'm new to IOS, creating a custom calendar where if the user selects any date, the app will redirect to another viewController.
I used this link to create a calendar:
https://github.com/Akhilendra/calenderAppiOS
I have done it with delegate but I can't figure out what I did wrong.
My code:
protocol SelectedDateDelagate: class {
func openAppointmentDetails()
}
class CalenderView: UIView, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, MonthViewDelegate {
weak var delegate: SelectedDateDelagate?
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell=collectionView.cellForItem(at: indexPath)
cell?.backgroundColor=Colors.darkRed
let lbl = cell?.subviews[1] as! UILabel
lbl.textColor=UIColor.yellow
let calcDate = indexPath.row-firstWeekDayOfMonth+2
print("u selected date",calcDate,monthName)
delegate?.openAppointmentDetails()
}
}
class ViewController: UIViewController,SelectedDateDelagate {
func openAppointmentDetails() {
let myVC = storyboard?.instantiateViewController(withIdentifier: "appointmentDetailsVC") as! AppointmentDetailsViewController
navigationController?.pushViewController(myVC, animated: true)
}
}
now the problem is when I clicked on date nothing gonna happen.
It's better to use the storyboard in this case, and control-drag your collection view to the second view controller. Since you don't use your delegate for anything else other than presenting the view controller, you might as well just get rid of it.
Delegates are used in other cases. In this situation, it is not necessary. In addition, you do not need to conform UIView to Delegates and DataSource because it contradicts MVC. Instead, put the calendar view (collection view) inside the UIViewController and conform this controller to implement methods.
You should do this:
class CalenderViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var calenderView: UICollectionView!
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath)!
cell.backgroundColor = Colors.darkRed
let lbl = cell.subviews[1] as! UILabel
lbl.textColor = UIColor.yellow
let calcDate = indexPath.row - firstWeekDayOfMonth + 2
openAppointmentDetails()
}
func openAppointmentDetails() {
let appointmentVC = storyboard?.instantiateViewController(withIdentifier: "appointmentDetailsVC") as! AppointmentDetailsViewController
navigationController?.pushViewController(appointmentVC, animated: true)
}
}
I solved this. I forgot to add calenderView.delegate = self
in viewDidLoad() method of ViewController
and also changes in openAppointmentDetails()
here are the changes.
class ViewController: UIViewController,SelectedDateDelagate {
func openAppointmentDetails() {
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "appointmentDetailsVC") as! AppointmentDetailsViewController
self.present(nextViewController, animated:true, completion:nil)
}
override func viewDidLoad() {
calenderView.delegate = self
}
}

How to push a UIViewController from a modal UIView?

I have a modal UIView created like below but I am unable to find a way to push another UIViewController from the UIView. The view's parent UIViewController does have a UINavigationController. All the code attempts below fail. The UIView has a UICollectionView
parent UIViewController
let searchView = SearchView()
view.addSubview(searchView)
modal UIView
class SearchView: UIView,
UICollectionViewDelegateFlowLayout,
UICollectionViewDataSource {
var collectionView: UICollectionView!
----
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
let myViewController: MyViewController = mainStoryBoard.instantiateViewController(withIdentifier: “MyViewController") as! MyViewController
// let currentController = self.getCurrentViewController()
// currentController?.navigationController?.pushViewController(myViewController, animated: true)
// self.parentViewController.navigationController.pushViewController(myViewController, animated: true)
let vc = self.window?.rootViewController
let navController = vc?.navigationController
navController?.pushViewController(myViewController, animated: true)
You can create one instance property of type UIViewController with your SearchView. Now use this property to pushViewController. After that initialized it where you are creating instance of SearchView.
class SearchView: UIView,
UICollectionViewDelegateFlowLayout,
UICollectionViewDataSource {
var collectionView: UICollectionView!
var vc: UIViewController?
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let mainStoryBoard = UIStoryboard(name: "Main", bundle: nil)
let myViewController = mainStoryBoard.instantiateViewController(withIdentifier: "MyViewController") as! MyViewController
vc?.navigationController?.pushViewController(myViewController, animated: true)
}
Now where you are adding this SearchView as subview set the vc property of its to self.
let searchView = SearchView()
searchView.vc = self
view.addSubview(searchView)

Pass info between collection views that have been embedded in different view controllers

So I have 2 view controllers, the one shown on the left has 1 embedded collectionview in it and the one shown on the right has 2 embedded collectionviews in it.
when trying to pass data (packSelected) from one to another, the prepareforsegue method doesn't work as i want it to change view controllers and the information is coming from the UICollectionViewController class. so at the moment I'm using the following code to 'segue' between the 2 view controllers from the UICollectionViewController class when a cell of the collectionview which is embedded in the the left hand viewcontroller is selected:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: PackCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! PackCollectionViewCell
//cell.labelCell.text = tableData[indexPath.row]
//cell.imageCell.image = UIImage(named: tableImages[indexPath.row])
cell.labelCell.text = packsDescription[indexPath.row]
cell.imageCell.image = UIImage(named: packsImage[indexPath.row])
cell.imageCell.layer.masksToBounds = true
cell.imageCell.layer.cornerRadius = cell.imageCell.frame.height/2
cell.imageCell.layer.borderWidth = 3
cell.imageCell.layer.borderColor = UIColorFromHEX(hexValue: 0x62aca2).cgColor
return cell
}
var packSelected: String = ""
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(packsName[indexPath.row])
packSelected = packsName[indexPath.row]
//performSegue(withIdentifier: "packView2Journey", sender: self)
transportMe()
}
func transportMe() {
let storyboard: UIStoryboard = UIStoryboard (name: "Main", bundle: nil)
let vc: JourneyViewController = storyboard.instantiateViewController(withIdentifier: "JourneyViewController") as! JourneyViewController
let currentController = getCurrentViewController()
currentController?.present(vc, animated: false, completion: nil)
}
func getCurrentViewController() -> UIViewController? {
if let rootController = UIApplication.shared.keyWindow?.rootViewController {
var currentController: UIViewController! = rootController
while( currentController.presentedViewController != nil ) {
currentController = currentController.presentedViewController
}
return currentController
}
return nil
}
The problem with this is now that I can't send any data to the other collectionview (s) that are embedded in the right hand viewcontroller.
--------------- EDIT ---------------------------------
solved with delegate method.
in the collectionviewcontroller that will pass the information declare the delegate:
class PackCollectionViewController: UICollectionViewController {
weak var delegate: PackCollectionViewDelegate?
//....
}
then below create delegate class:
protocol PackCollectionViewDelegate : class {
func packSelected (_ selector: PackCollectionViewController, didSelect thePackName: String)
}
and in the function that calls transportMe() as shown in original post define the data to be passed:
let passPackName = packsName[indexPath.row] as String
self.delegate?.packSelected(self, didSelect: passPackName)
transportMe()
in the collectionviewcontroller that will receive the information create extension:
extension PartCollectionViewController : PackCollectionViewDelegate {
func packSelected (_ selector: PackCollectionViewController, didSelect thePackName: String) {
print(thePackName)
}
}

Resources