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

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

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

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

How to present a separate View Controller when click on a button inside a custom TableViewCell Swift 4?

I have 2 view controller,MainVC and PopUpVC. MainVC have a tableView with tableViewCell class.Every cell have a ShowButton.So what I want to do is,when I click on ShowButton,PopUpVC will show on top of MainVc
What I already do:
1) Connect MainVC and PopUpVC with segue of present modally,at the same time set the segue identifier to "popVc".
2) Set PopUpVC Transition Style = Cover Vertical and Presentation = Over Current Context , Background of PopUpVC I set to Clear Color in StoryBoard.
3) In TableViewCell class for the TableView,set up the protocol and delegate like below :
//here is MyTableViewCell class
protocol MyDelegate : class {
func showPopUpVc(itemId: Int)
}
class MyTableCell : UITableViewCell {
weak var delegate : MyDelegate?
#IBAction func showButtonTapped(_ sender: Any) {
self.delegate?.showPopUpVc(itemId : MyItem.id)
}
}
//View Controller
class MainViewController: UIViewController ,UITableViewDataSource, UITableViewDelegate , MyDelegate {
func showPopVc(itemId; Int) {
//here I want to show the PopUpVC
self.performSegue(withIdentifier: "popVc", sender: self)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyTableCell", for: indexPath) as! MyTableCell
cell.items = self.items[indexPath.row]
cell.delegate = self
return cell
}
}
4) So inside showPopVc() function in MainViewController I tried this 2 solution in order to show the popUpVC
//1st solution
func showPopVc(itemId; Int) {
self.performSegue(withIdentifier: "popVc", sender: self)
}
//2nd solution
func showPopVc(itemId; Int) {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let PopUpVc = storyBoard.instantiateViewController(withIdentifier: "PopUpVc")
self.present(PopupVc, animated: true, completion: nil)
}
Output that I get now :
Whenever I tap the ShowButton, the tap is detected(Cause I set print("Clicked") in showPopUpVc()), but the whole screen become black.None of the content in PopUpVC is shown on the screen.Just black.Cant go back to previous screen,cause is absolutely black and blank screen
Both solution in section 4 also produce the same output.
So what I missing here? Or can somebody show me a complete example to solve this problem?
Try this
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "popVc")
{
let newVC = segue.destination;
self.setPresentationStyleForSelfController(selfController: self,presentingController: newVC)
}
}
func setPresentationStyleForSelfController(selfController:UIViewController,presentingController:UIViewController)
{
if #available(iOS 8.0, *)
{
//iOS 8.0 and above
presentingController.providesPresentationContextTransitionStyle = true;
presentingController.definesPresentationContext = true;
presentingController.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
}
else
{
presentingController.modalPresentationStyle = UIModalPresentationStyle.currentContext
selfController.navigationController?.modalPresentationStyle = UIModalPresentationStyle.currentContext
}
}
Set modal presentation style for view controller.
Try this and see (may this would work, programatically that one is not working with storyboard segue.):
func showPopVc(itemId; Int) {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let PopUpVc = storyBoard.instantiateViewController(withIdentifier: "PopUpVc")
// Try anyone of these according to your requirement.
PopUpVc.modalPresentationStyle = .overCurrentContext
//PopUpVc.modalPresentationStyle = .overFullScreen
self.present(PopupVc, 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).

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