How to perform navigation from UIView to ViewController - ios

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

Related

Transferring fetched data from tableview cell to another viewcontroller without segue

here's my tableview function where i try to pass a reference for video object(which i'm fetching using youtube api) to DetailsViewController using navigationController
when i try to print an object inside a tableview, it shows me data i need, but only thing i get in detailsPageController is nil
here's the code for tableView Function
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Target viewcontroller to pass data
let selectedVideo = videos[indexPath.row]
let targetVC = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: DetailsViewController.identifier) as! DetailsViewController
targetVC.video = selectedVideo
print(targetVC.video?.title)
// Navigate to the page
NavigationManager.changeScene(from: self, to: .DetailsPage, with: .present)
}
code for DetailsViewController where i'm trying to print value of vide instance
class DetailsViewController: UIViewController{
// Self identifier
private(set) static var identifier = "DetailsViewController"
// MARK: - IBOutlets
// MARK: - Instances
public var video: Video?
// MARK: - Initialization
override func viewDidLoad() {
super.viewDidLoad()
addSubViews()
initializeStackView()
initializeConstraints()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print(video?.title)
}
and this is just a static helper function to change scenes using navigation controller:
class NavigationManager {
enum TargetView {
case mainPage
case DetailsPage
}
enum TransitionStyle {
case push, present
}
static func changeScene(from currentViewController: UIViewController, to chosenViewController: TargetView, with transitionStyle: TransitionStyle) {
var targetVC: UIViewController!
switch chosenViewController {
case .mainPage:
targetVC = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: MainViewController.identifier) as! MainViewController
case .DetailsPage:
targetVC = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: DetailsViewController.identifier) as! DetailsViewController
}
switch transitionStyle {
case .push:
currentViewController.navigationController?.pushViewController(targetVC, animated: true)
case .present:
currentViewController.navigationController?.present(targetVC, animated: true)
}
}
}

present ViewController when image inside a cell was pressed [duplicate]

This question already has answers here:
presentViewController from TableViewCell
(4 answers)
Closed 3 years ago.
I don't know how to present the viewController programmatically
I already finished creating a function for tapping of image. My only problem now is how to present the viewController. The image is inside the cell.
ItemsCell.swift
let tapGesture = UITapGestureRecognizer()
if let imageStr = obj.image {
item.optionImage.kf.setImage(with:URL(string:imageStr))
item.optionImage.isUserInteractionEnabled = true
tapGesture.addTarget(self, action: #selector(tappedImage))
item.optionImage.addGestureRecognizer(tapGesture)
} else {
item.optionImage.image = nil
}
The Function:
#objc func tappedImage() {
print("image tapped")
// let storyboard = UIStoryboard(name: "Main", bundle: nil)
// let controller = storyboard.instantiateViewController(withIdentifier: "AssetsViewController")
// controller.present(controller, animated: true, completion: nil)
}
Use Post Notification
class VC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(presentVC(_:)), name: NSNotification.Name("PresentVCNotificationName"), object: nil)
}
#objc
func presentVC(_ notification: Notification) {
let cell = notification.object as? YourCell
let storyboard = UIStoryboard(name: "Your Storyboard Name", bundle: Bundle.main)
let vc = storyboard.instantiateViewController(withIdentifier: "Your Storyboard ID")
self.present(vc, animated: true, completion: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
class Cell: UITableViewCell {
#objc func tappedImage() {
NotificationCenter.default.post(name: NSNotification.Name("PresentVCNotificationName"), object: self)
}
}
In the interface builder tap on the viewController you want to show and make sure you defined a Storyboard Id. Create a class for that view controller, and make sure you have an outlet to the UIImageView that's supposed to show that image. Also make sure your class has a member of type UIImage.
// Assumed this class has Storyboard Id of: ImageViewerViewController
class ImageViewerViewController: UIViewController {
var imageView: UIImageView! // outlet from IB
var image: UIImage!
override func viewDidLoad() {
imageView.image = image;
}
}
In the view controller which contains the table:
class MainViewController: UIViewController {
.
.
.
// UITableViewDelegate
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier: ImageCell) as! YourC ImageCell ellClass
cell.containerVc = self
return cell
}
}
In the cell view:
class ImageCell: UITableViewCell {
weak var containerVc: UIViewController!
#objc func tappedImage() {
print("image tapped")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "ImageViewerViewController") as! ImageViewerViewController
controller.image = item.optionImage.image
containerVc.present(controller, animated: true, completion: nil)
}
}

UIButton move to storyboard in Custom Cell

I'm trying to move to a new storyboard when a button is tapped in a custom cell type, but having trouble with the custom class. I currently have this
class submitCell: UITableViewCell {
#IBAction func cancelButton(_ sender: Any) {
}
}
and I need the cancelButton to do this
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "TripList") as! TripTableViewController
self.present(viewController, animated: true , completion: nil) //Move
Except that .present isn't a method of UITableViewCell, only UIViewController. How can I get this functionality to happen?
Rather than trying to call the function in your custom cell, do that in your view controller. Here is what you need
class submitCell: UITableViewCell {
var tapAction: ((UITableViewCell) -> Void)?
#IBAction func cancelButton(_ sender: Any) {
tapAction?(self)
}
}
and then in your view controller
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
cell.tapAction = { [weak self] cell in self?.move()}
}
func move() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "TripList") as! TripTableViewController
self.present(viewController, animated: true , completion: nil) //Move
}
Note: You should capitalise the first letter of your class name. Check out Swift name conventions for more information.
First in the custom cell, bring the button into it and connect.
class submitCell: UITableViewCell {
#IBOutlet weak var cancelButton: UIButton!
}
And now lets do what you want to do in the class where you use the button.
In the class, where cell is created, do
cell.delegate = self
cell.cancelButton.addTarget(self, action: #selector(cancelAction), for: .touchUpInside)
then create one function that does:
private func cancelAction(sender: UIButton){
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "TripList") as! TripTableViewController
self.present(viewController, animated: true , completion: nil) //Move
}
There are a fews ways to do that.
- Create a delegate
- Create a reference for your button, so you can use in your ViewController.
I'll link some cases, so you can choice the best approach for your case:
Reference Button
Get button click inside UI table view cell
Delegate
https://www.codementor.io/leoiphonedev/delegates-in-swift-hvim2n7s1
You can delegate the function cancelButton to the viewController by having a reference to the button in your custom cell class.
Basically, first you need to make an outlet to the button in your custom cell :
#IBOutlet var button: UIButton!
And then in your viewController :
button.addTarget(self, action:#selector(cancelButton(_sender:)), for: .touchUpInside)
Declare the function :
func cancelButton (_sender: UIButton) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "TripList") as! TripTableViewController
self.present(viewController, animated: true , completion: nil) //Move
}
PS : If you have multiple buttons you can add them to the same action and control the behavior by a tag.
button.tag = 0
if ( _sender.tag == 0.) {
// do tag 0
} else ...
I would look at the delegate pattern.
Create a protocol for your UITableViewCell delegate
protocol SubmitCellDelegate: class {
func cancelButtonPressed()
}
Add the delegate to your UITableViewCell subclass
class SubmitCell: UITableViewCell {
weak var delegate: SubmitCellDelegate?
#IBAction func cancelPressed(_ sender: UIButton) {
delegate?.cancelButtonPressed()
}
}
Implement the delegate in your UIViewController subclass
This must be implemented in the class that has the reference to your tableview cell.
class ViewController: UIViewController, SubmitCellDelegate {
// ...
// wherever you create your cell
submitCell.delegate = self
// ...
// Implement the SubmitCellDelegate function (Xcode will force you to)
func cancelButtonPressed() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "TripList") as! TripTableViewController
self.present(viewController, animated: true , completion: nil)
}
}
Side note: it's good practice in most (all?) object-oriented programming language to use title case on class names (e.g. SubmitCell instead of submitCell).

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

How to call a controller from a modal popover in Swift?

I have three controllers, and i need this navigation:
| 1stVC | -> | popoverVC | -> | 2ndVC |
The first one show a modal view using a popover segue, and then from the modal view, using a protocol, it should show the second controller.
The protocol calls the method, and then, it should call the second controller, but it doesn't!. I have tried performing segue, and calling the controller but nothing happens, in fact, it reloads the first controller instead call the second. Please help. I think that i must have errors in delegation, but i can't figure what.(poor english, i know)
One important thing, the firstViewcontroller is called from another controller that is in a tabbarcontoller.
Thanks in advance.
This is the code:
PopoOverController:
import UIKit
protocol basketDelegate {
func didSelectValue()
}
class PopoverViewController: UITableViewController {
var delegate: basketDelegate!
let options = ["Filters", "Basket"]
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "reuseIdentifier")
let rdc = storyboard!.instantiateViewControllerWithIdentifier("FirstViewController") as! FirstViewController
self.delegate = rdc
}
// MARK: - Table view data source
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return options.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath)
cell.textLabel?.textAlignment = .Center
cell.textLabel?.textColor = colorWithHexString("#1C7393")
cell.textLabel?.text = options[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
delegate?.didSelectValue()
}
}
FirstController
import UIKit
class FirstViewController: AEAccordionTableViewController,
UIPopoverPresentationControllerDelegate, UISearchBarDelegate,basketDelegate {
override func viewDidLoad() {
super.viewDidLoad()
var Filters = PopoverViewController()
Filters.delegate = self
}
// MARK: - Popup filter call
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "popoverSegue" {
let popoverViewController = segue.destinationViewController as UIViewController
popoverViewController.modalPresentationStyle = UIModalPresentationStyle.Popover
popoverViewController.popoverPresentationController!.delegate = self
}
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.None
}
func didSelectValue() {
//Option 1--Failed!
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil);
let SecondViewControllerObject : UIViewController = storyboard.instantiateViewControllerWithIdentifier("SecondViewController") as! SecondViewController;
self.presentViewController(SecondViewControllerObject, animated: true, completion: nil);
//Option 2--Failed
self.performSegueWithIdentifier("secondSegue", sender: self)
//Option 3--Failed
let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let SecondViewControllerObject : UIViewController = storyboard.instantiateViewControllerWithIdentifier("SecondViewController") as! SecondViewController;
let navigationController = UINavigationController(rootViewController: SecondViewControllerObject)
self.presentViewController(navigationController, animated: true, completion: nil)
//Option 4--Failed
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("SecondViewController") as! SecondViewController
FirstViewController()!.presentViewController(vc, animated: true, completion: nil)
//Option 5--Failed
self.navigationController?.presentViewController(vc, animated: false, completion: nil)
}
}
SecondController
import UIKit
class SecondViewController: AEAccordionTableViewController {
#IBOutlet weak var dismissButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func dismissTap(sender: UIButton) {
self.dismissViewControllerAnimated(true, completion: nil)
}
}
Your first view controller should be a PopoverViewControllerDelegate, not a basketDelegate.
This behavior can be reached with an Action Sheet. Just in the First Controller add a new method to create a Contextual Menu with the same option to call the second controller. This method is called from a button.
Is a simple solution, without protocols or delegations.
In the first controller:
#IBAction func showSheetASction(sender: UIBarButtonItem) {
print("click")
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .ActionSheet)
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (action) in
print(action)
}
alertController.addAction(cancelAction)
let CallSVCAction = UIAlertAction(title: "Filters", style: .Default) { (action) in
// ...
print(action)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("SecondViewController") as! SecondViewController
self.presentViewController(vc, animated: true, completion: nil)
}
alertController.addAction(CallSVCAction)
alertController.view.tintColor = colorWithHexString("#1C7393")
self.presentViewController(alertController, animated: true) {
}
}

Resources