Opening another view controller using delegate? - ios

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
}
}

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

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 Data using a pushViewController?

Using this code I am able to 'segue' to the same instance of my view controller
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "DetailVC")
self.navigationController?.pushViewController(vc, animated: true)
}
However, how do I pass data over? I only know how to pass data using the segue option. When I run the code with this, I get nil errors because the new instantiated view controller cannot read the data.
for example I add here, for detail description you can get the tutorial from here
class SecondViewController: UIViewController {
var myStringValue:String?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
// We will simply print out the value here
print("The value of myStringValue is: \(myStringValue!)")
}
and send the string as
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "DetailVC") as! SecondViewController
vc.myStringValue = "yourvalue"
self.navigationController?.pushViewController(vc, animated: true)
}
First off. This isn't a segue. This is just pushing another view to the stack. And (like Ashley Mills says) this is not the same instance you are currently in. This is a NEW instance of a view controller.
But all you need to do is populate the data. You already have the controller here...
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
// you need to cast this next line to the type of VC.
let vc = storyboard.instantiateViewController(withIdentifier: "DetailVC") as! DetailVC // or whatever it is
// vc is the controller. Just put the properties in it.
vc.thePropertyYouWantToSet = theValue
self.navigationController?.pushViewController(vc, animated: true)
}
Then in your second view controller catch the value like this
class DetailVC: UIViewController {
var thePropertyYouWantToSet = String()
override func viewDidLoad() {
print(thePropertyYouWantToSet)
}
}
What you're using isn't a segue. This is just pushing a NEW instance (not the same one) of view controller onto the nav stack.
To segue, in your storyboard, you can just drag a link from the collection view cell to the view controller, then assign data in the prepareForSegue method…
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let viewController = segue.destinationViewController as? DetailVC {
viewController.someProperty = self.someProperty
}
}
In DetailVC, Create a variable and assign value while you create an object. Check example below:
class DetailVC {
var dataString: String?
}
Pass data like below:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "DetailVC") as! DetailVC
vc.dataString = "PASS THE DATA LIKE THIS"
self.navigationController?.pushViewController(vc, animated: true)
}
If you're following the no storyboard pattern you can do it like this
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let viewController = NextViewController()
viewController.dataInsideNextViewController = "Data to be passed"
navigationController?.pushViewController(viewController, animated: true)
}
In your ViewController1 launch your ViewController2 using these code
Class ViewController1: UIViewController {
var dataFromVC2 = ""
func loadVC2() {
let vc2 = ViewController2()
vc2.dataFromVC1 = "Hi VC2"
vc2delegate = self
navigationController?.pushViewController(vc2, animated: true)
}
}
In your ViewController2, add this code. You can use the delegate property to pass back data from ViewContoller2.
Class ViewController2: UIViewController {
var dataFromVC1: String = ""
weak var delegate: ViewController1!
func passData_toVC1() {
delegate.dataFromVC2 = "How's it going VC1?"
}
}

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)
}
}

iOS Swift Custom Cell NavigationController

I have CollectionView in TableView. everything ok but. when I want to navigate my cell to another viewController I got error
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath){
let storyboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let bookView: BookSingleController = storyboard.instantiateViewControllerWithIdentifier("BookSingle") as! BookSingleController
self.navigationController?.pushViewController(bookView, animated: true)
bookView.bookID = "\(self.books[indexPath.row].id)"
print(self.books[indexPath.row].id)
}
Xcode show me error on self.navigationController?.pushViewController(bookView, animated: true) line. this is error description:
Value of 'RelatedBookTableViewCell' has no member 'navigationController'
RelatedBookTableViewCell is my custom cell class:
class RelatedBookTableViewCell: UITableViewCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout
Where is my problem?
thanks.
You will either need to:
Use a delegate pattern like some have suggested.
Where you pass the call from the collectionView delegate method didSelectItemAtIndexPath to your own custom delegate/protocol on the cell that the UITableViewController which displays the cell is a delegate to. This can be set in the cellForRowAtIndexPath.
swift
Custom Protocol
protocol DisplayBookDelegate {
func displayBook(bookId: String)
}
In the tableViewController
class tableViewController: UITableViewController, DisplayBookDelegate {
...
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.delegate = self
return UITableViewCell()
}
func displayBook(let bookId) {
self.navigationController?.pushViewController(bookView, animated: true)
}
}
In the cell
var delegate: DisplayBookDelegate?
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
self.delegate.displayBook("1")
}
Use notifications and pass the bookId in a dictionary on the notification object
objc: [[NSNotificationCenter defaultCenter] postNotificationName:"kDisplayBookView" object:#{"bookId":"1"}];
swift NSNotificationCenter.defaultCenter().postNotificationName("kDisplayBookView", object: ["bookId":"1"])
Using below code you can get the Top viewcontroller and using that view controller you can perform your push operation
let objViewController = UIApplication.topViewController()!
Use this extension
extension UIApplication {
class func topViewController(base: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(nav.visibleViewController)
}
if let tab = base as? UITabBarController {
if let selected = tab.selectedViewController {
return topViewController(selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(presented)
}
return base
}
}
Try pushing the VC from the root controller in your application.
UIApplication.sharedApplication().keyWindow?.rootViewController

Resources