How to push a UIViewController from a modal UIView? - 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)

Related

Setting delegate to self for the collection view after present action

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

How to perform navigation from UIView to ViewController

In my home screen have multiple sections of UICollectionView so I used separate - separate views(xib) for each section(UICollectionViews) now I have to perform navigation(didSelectItemAt) I am unable to perform it even no error is there
I user below code for navigation(didSelectItemAt)
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let vc = UIStoryboard.init(name: "ProductListing", bundle: Bundle.main).instantiateViewController(withIdentifier: "ProductListingViewController") as? ProductListingViewController
(superview?.next as? ProductListingViewController)?.navigationController?.pushViewController(vc!, animated: true)}
You can use callbacks for this.
In your collectionView View add this callBack :
var didSelectCallback: (() -> Void)?
Then in didSelectItem delegate method of collectionView write this :
if let callBack = didSelectCallback {
callBack()
}
Then in controller when you add this collectionView View add this callBack :
yourView.didSelectCallback = { [weak self] in
guard let weakSelf = self else {return}
let vc = UIStoryboard.init(name: "ProductListing", bundle: Bundle.main).instantiateViewController(withIdentifier: "ProductListingViewController") as? ProductListingViewController
weakSelf.navigationController?.pushViewController(vc!, animated: true)}
}
Try this instead:
let storyboard = UIStoryboard(name: "ProductListing", bundle: nil)
let productListingVC = storyboard.instantiateViewController(withIdentifier: "ProductListingViewController")
self.present(productListingVC, animated: true, completion: nil)
Better way will send callback from view to ViewController and perform navigation from it like this:
class View: UIView {
var didTapSomething: () -> Void
}
and in ViewController:
class ViewController: UIViewController {
#IBOutlet var view: View!
override func viewDidload() {
super.viewDidLoad()
view.didTapSomething = {
//perform navigation
}
}
}

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

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

Resources