How to use UINavigationController in UITabBarController - ios

I have a UITabBarController that each tab has an UINavigationController. One of them has a blank rootViewController that pushed another viewController, in viewDidLoad and viewDidAppear functions (I use this presenterVC because I need to reset the AddVC when user uses another tab)
class PresenterVC: UIViewController { // This is root viewController
override func viewDidLoad() {
super.viewDidLoad()
self.PresentCheckAddVC()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.PresentCheckAddVC()
}
func PresentCheckAddVC() {
if self.navigationController?.viewControllers.count == 1 {
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "AddVC")
self.navigationController?.pushViewController(vc, animated: false)
}
}
}
The problem is that I need to navigate to another viewController in AddVC, but it automatically navigates back to AddVC.
I tried both segue and manually pushing viewController, and they have same results.
class AddVC: UIViewController {
#IBAction func btnPayeeSelect_Pressed(_ sender: Any) {
PresentPayeeAddVC()
}
func PresentPayeeAddVC() {
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PayeeAddVC")
self.navigationController?.pushViewController(vc, animated: true)
}
}

Related

How to pass delegate thru Navtigation Controller?

I have 2 VC's, and one navigation controller between them. How can i set First screen as a delegate of Second?
What i tried:
Present SecondVC from frist (it presents it without navigation)
Setting delegate in NavVC viewDidLoad()
FirstVC:
class MainVC: UIViewController, SecondVCDelegate {
func passData(text: String) {
// do stuff
}
#IBAction func openNextVC(_ sender: Any) {
let nextVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NavVC") as! NavVC
present(nextVC, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Navigation Controller:
class NavVC: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
SecondVC:
protocol SecondVCDelegate {
func passData(text: String)
}
class SecondVC: UIViewController {
var delegate: SecondVCDelegate?
#IBAction func save(_ sender: Any) {
// do stuff
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Main task here would be to access second view controller instance from navigation controller instance(navC). And to achieve this, you have to first access the rootViewController from your navigation controller instance(nextVC) as below:
let nextVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NavVC") as! NavVC
let secondVC = nextVC?.viewControllers.first as? SecondVC
secondVC.delegate = self
present(nextVC, animated: true, completion: nil)
The above code is applicable when second view controller is the root of your navigation controller. If second view controller is not the root controller to navC, then you have to iterate the controllers to get second view controller instance. Once second controller instance is there, then you can assign the delegate to the same as below.
for controller in navC.viewControllers {
if controller.isKind(of: SecondVC.self) {
if let secondVc = controller as? SecondVC {
secondVc.delegate = self
}
break
}
}
I handled the same thing. Try this on MainVC:
class MainVC: UIViewController, SecondVCDelegate {
func passData(text: String) {
// do stuff
}
#IBAction func openNextVC(_ sender: Any) {
guard let secondController = self.storyboard?.instantiateViewController(withIdentifier: "AddGroupVC") as? AddGroupVC else { return }
secondController.delegate = { [weak self] in
print("call your delegates here")
}
self.navigationController?.present(secondController, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
On second View Controller, call the delegates in viewWillDisappear :
class SecondVC: UIViewController {
var delegate: SecondVCDelegate?
#IBAction func save(_ sender: Any) {
// do stuff
self.dismiss(animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillDisappear(_ animated: Bool) {
self.delegate
}
}

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

Swift pass data through navigation controller

One of my segues transitions from a view controller to a tableview controller. I want to pass an array between the two, but with a navigation controller before the tableview controller, I can't figure out how to pass the array. How do you pass data through a navigatiom controller to a tableview controller?
Override prepareForSegue and set whatever value on the tableview you'd like. You can grab the tableview from the UINavigation viewControllers property.
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!){
let navVC = segue.destinationViewController as UINavigationController
let tableVC = navVC.viewControllers.first as YourTableViewControllerClass
tableVC.yourTableViewArray = localArrayValue
}
For Swift 3 :
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let navVC = segue.destination as? UINavigationController
let tableVC = navVC?.viewControllers.first as! YourTableViewControllerClass
tableVC.yourTableViewArray = localArrayValue
}
#Tommy Devoy's answer is correct but here is the same thing in swift 3
Updated Swift 3
// MARK: - Navigation
//In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
if segue.identifier == "yourSegueIdentifier" {
if let navController = segue.destination as? UINavigationController {
if let chidVC = navController.topViewController as? YourViewController {
//TODO: access here chid VC like childVC.yourTableViewArray = localArrayValue
}
}
}
}
I was getting this error to Horatio's solution.
ERROR: Value of type 'UINavigationController' has no member 'yourTableViewArray'
So this might help others like me looking for a code only solution. I wanted to redirect to a Navigation controller, yet pass data to root view controller within that Nav Controller.
if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "yourNavigationControllerID") as? UINavigationController,
let yourViewController = controller.viewControllers.first as? YourViewController {
yourViewController.yourVariableName = "value"
self.window?.rootViewController = controller // if presented from AppDelegate
// present(controller, animated: true, completion: nil) // if presented from ViewController
}
SWIFT 3 (tested solution) - Passing data between VC - Segue with NavigationController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let navigationContoller = segue.destination as! UINavigationController
let receiverViewController = navigationContoller?.topViewController as ReceiverViewController
receiverViewController.reveivedObject = sentObject
}
Tommy's solution requires you to set up the Segue in Storyboard.
Here is a code only solution:
func functionToPassAsAction() {
var controller: UINavigationController
controller = self.storyboard?.instantiateViewControllerWithIdentifier("NavigationVCIdentifierFromStoryboard") as! UINavigationController
controller.yourTableViewArray = localArrayValue
self.presentViewController(controller, animated: true, completion: nil)
}
Swift 5
class MyNavigationController: UINavigationController {
var myData: String!
override func viewDidLoad() {
if let vc = self.topViewController as? MyViewcontoller{
vc.data = self.myData
}
}
}
let navigation = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MyNavigationController") as! MyNavigationController
navigation.myData = "Data that you want to pass"
present(navigation, animated: true, completion: nil)
FIXED syntax for SWIFT 3
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let navVC = segue.destination as! UINavigationController
let tableVC = navVC.viewControllers.first as! YourTableViewControllerClass
tableVC.yourTableViewArray = localArrayValue
}
passing a text in tableview swift 4
**or just pass data but change the function **
First View Controller
class
let name_array = ["BILL GATES", "MARK ZUKERBERG", "DULKAR SALMAN", "TOVINO" , "SMITH"]
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
let story: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let new = story.instantiateViewController(withIdentifier: "SecondViewController")as! SecondViewController
new.str1 = name_array[indexPath.row]
self.navigationController?.pushViewController(new, animated: true)
Second View Controller
class SecondViewController: UIViewController {
#IBOutlet weak var lbl2: UILabel!
var str1 = String()
override func viewDidLoad() {
super.viewDidLoad()
lbl2.text = str1
}

Resources