I have a view controller which has a UISegmentControl with two buttons. I load this View and when the SegmentValue has changed i'll add a view controller into the view.
This is MainViewController
In the class I define the controllers
var tabNavigation: UISegmentedControl!
let viewController1:ViewController1 = ViewController1()
let viewController2:ViewController2 = ViewController2()
// Create the segment control
self.tabNavigation = UISegmentedControl(items: ["v1", "v2"])
self.tabNavigation.selectedSegmentIndex = 0
self.tabNavigation.frame = CGRectMake(60, 250, 130, 25)
self.tabNavigation.addTarget(self, action: "segmentedValueChanged", forControlEvents: .ValueChanged)
self.navigationItem.titleView = self.tabNavigation
// set first view
self.addChildViewController(viewController1)
self.view.addSubview(viewController1.view)
// Segement changed
func segmentedValueChanged() {
if (self.tabNavigation.selectedSegmentIndex == 0) {
self.addChildViewController(viewController1)
self.view.addSubview(viewController1.view)
} else {
self.addChildViewController(viewController2)
self.view.addSubview(viewController2.view)
In the storyboard i have a segue from MainViewController to ViewController3
Now in ViewController1 i'm trying to create a segue (should be part of a previous navigation controller so swipe across with a back button) from ViewController1 to ViewController3. But fails as the segue does not exist as ViewController1 is made completely from Code.
My code for the segue is below but obviously fails as that is the segue identifier for MainViewController to ViewController3
performSegueWithIdentifier("loadVC3", sender: nil)
What you're looking for is to initialize a viewController programmatically and then show it, for that you need first to set a unique Storyboard ID and then initialize it. Like this:
let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("ViewController3") as! UIViewController
// and then present it modally
presentViewController(viewController, animated: true, completion: nil)
// or push it onto the navigation stack
pushViewController(viewController, animated: true)
You cannot create segues programmatically. Look at this answer:
Creating a segue programmatically
I would probably use a XIB to design all the view controllers that you create programmatically.
Related
I want to create a navigation hierarchy where I want to go to a SecondViewController from FirstViewController using a NavigationController. The FirstViewController contains a button B that I intend to use to go to a SecondViewController. I am employing the navigation controller concept since I want to return to the FirstViewController later. I have done the following steps to achieve it:
I have embedded a NavigationController into a storyboard X containing both the FirstViewController and SecondViewController.
I have created a segue from the button B present in the FirstView (has FirstViewController associated with it) to the SecondView (has SecondViewController).
I set the navigationViewController nvc as following after I have presented the firstViewController:
nvc = UINavigationController.init(rootViewController: firstViewController)
Later on, when the button B gets pressed, I execute the following code to push the secondViewController onto the navigation hierarchy:
self.nvc?.pushViewController(secondViewController, animated: true)
However, the secondView doesn't present itself even after pushing.
I have named the segue between the button B and secondViewController as kSegue, so I tried to perform the segue as an addition to see if the secondViewController presents itself or not:
self.performSegue(withIdentifier: "kSegue", sender: self)
An exception occurs when both the 4th and 5th steps are performed together. The exception states that I'm pushing a viewController that already exists on the navigation hierarchy, but the second view still doesn't open even if I comment the performSegue code.
May I ask what mistake I am making here?
In the storyboard, make sure there is a rootViewController segue from the navigation controller to the first view controller. Also, make sure the navigation controller is marked as the initial view controller.
In the code, change
self.nvc?.pushViewController...
To
self.navigationController?.pushViewController...
1) Take a navigation controller on your storyboard
2) Set navigation controller as initial view controller
3) set view controller A as a root view controller of your navigation controller
4) in "GoToB" method access View controller B's instance and push it in navigation controller
5) On View controller B's "Go Back" method write code to pop it.
6) Dont forget to set storyboard Id on both A & B view controller
class FirstViewController: UIViewController
{
lazy var secondViewController : SecondViewController? =
{
let secondViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController
return secondViewController
}()
override func viewDidLoad()
{
super.viewDidLoad()
}
#IBAction func goToB(sender : UIButton)
{
guard let secondViewController = self.secondViewController else
{
return
}
self.navigationController?.pushViewController(secondViewController, animated: true)
}
}
class SecondViewController: UIViewController
{
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func goBack(sender : UIButton)
{
self.navigationController?.popViewController(animated: true)
}
}
I have two controller in the screen as the pic shows.
now I wanna present the ThirdViewController in the red area when the tableView is selected, I try to use present() but it shows in the whole screen. how Can I fix that?
Try this
Download 3 files from Folder Presentation
Add this to the VC from which you will open Second VC
lazy var slideInTransitioningDelegate = SlideInPresentationManager()
When going to another VC;
let vc = self.storyboard?.instantiateViewController(withIdentifier: "Ident") as? YourViewController
vc?.delegate = self
let navigationController = UINavigationController(rootViewController: vc!)
slideInTransitioningDelegate.direction = .bottom // or .left, .right
slideInTransitioningDelegate.disableCompactHeight = true
navigationController.transitioningDelegate = slideInTransitioningDelegate
navigationController.modalPresentationStyle = .custom
self.present(navigationController, animated: true, completion: nil)
Tutorial - https://www.raywenderlich.com/915-uipresentationcontroller-tutorial-getting-started
Project - https://koenig-media.raywenderlich.com/uploads/2016/08/Medal_Count_Completed.zip
You can do this by first adding your SecondViewController into you MainViewController as child controller and then add SecondViewController's view as subview in UITableViewCell.
var secondViewController : SecondViewController!
override func viewDidLoad() {
super.viewDidLoad()
secondViewController = self.storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
self.addChildViewController(setupProfileViewController)
}
Now somewhere in your code when you want to show that SecondViewController view controller like on didSelectRowAt indexPath method of UITableViewCell or any other button action, add following line of code:
let cell = yourTableView.cellForRow(at: selectedIndexPath)
cell.contentView.addSubview(secondViewController.view)
There is no image attached, so I am not sure what exactly you want; but to show a view controller in area of another view controller, you could use container view, this is a useful tutorial about container view
and to load by some action, you could present your container view when your action done.
I have three viewControllers Main, A, B. Main ViewController holds ContainerView and other content as well does all transactions in containerView. ViewControllerA has ButtonA when pressing it content of container has to change to ViewControllerB
how can I do that? I cannot find any similar examples.
You will need to create create delegate for that.
First create a protocol
protocol ViewControllerADelagate {
func didPressOnButtonA()
}
In ViewControllerA add following delegate variable
class ViewControllerA {
....
var delegate : ViewControllerADelagate?
....
}
In ViewControllerA add following on button press
#IBAction buttonAPressed(sender : UIButton) {
self.delegate?.didPressOnButtonA()
}
In MainViewController assign the delegate of ViewControllerA to self
like
vcA.delegate = self
Implement the delegate method in MainViewController like
func didPressOnButtonA {
let storyboard : UIStoryboard = UIStoryboard(name: storyboard, bundle: nil)
let vcB : UIViewController = storyboard.instantiateViewController(withIdentifier: viewControllerIdentifier) as! ViewControllerB
self.addChildViewController(vcB)
self.containerView.addSubview(vcB.view)
vcB.didMove(toParentViewController: self)
vcB.view.frame = CGRect.init(x: 0, y: 0, width: self.containerView.frame.size.width, height: self.containerView.frame.size.height)
}
While click on ButtonA. Post a notification to mainView. There remove viewController A from Container and add View Controller B .
I have created a storybaord with sample. you can download it from here.
You need to change the embed view to a navigation controller and then you can use segue to show second view on button press. also hide/show navigation bar depends on requirement.
I am not fond of using these but you can get the child view controller from parent controller by accessing an array childViewControllers. On view did load you would need to go through all of these and find the one that can be typecast into your view controller type like
childViewControllers.flatMap { $0 as? AViewController }.first.
Now that you found the correct view controller I suggest you to either assign yourself as a delegate
childViewControllers.flatMap { $0 as? AViewController }.first.delegate = self
or simply add a button target
childViewControllers.flatMap { $0 as? AViewController }.first.button.addTarget...
Now this can easily be done if you simply embed the 2 view controllers at the same time (have 2 content views) and hide one or the other depending on which you show. At least this way you can assign connection straight away. When this is not the case then you will need to iterate again when setting a new controller or assign connections where you initialize a new view controller.
In this case it then seems better to turn the system around: When child view controller is loaded rather call
self.delegate = parentViewController as? AViewControllerDelegate
Although this will work it seems wrong that a child view controller will control who its listener is so I advise you to avoid such coding.
I in general use a custom implementation for container view which you could do the same or maybe at least subclass the native one and target the methods so that your code will look something like:
private onBPressed() {
containerView.setViewController(viewController: BViewController(delegate: self), animation: .fade)
}
When you pressing Button A from View A:
#IBAction func BtnAPress(_ sender: Any)
{
//Moving Storyboard
let Storyboard = UIStoryboard(name: "Main", bundle: nil)
let MainVC : UIViewController = Storyboard.instantiateViewController(withIdentifier: "ViewControllerB")
self.present(MainVC, animated: true, completion: nil)
}
I am trying to create segues between my UIViewControllers but am having some difficulties with creating a segue from a UITableViewCell and a UIButton.
When I create a show detail segue through storyboard from a UITableViewCell to a UIViewController it works perfectly and I get the back button showing up. But when I try to create a show detail segue from a UIButton to a UIViewController it doesn't register the navigation stack and presents the screen modally without the back button.
How can I make a successful show detail segue from a UIButton to a viewcontroller? I am new to iOS and am having trouble determining why the UIButton segue doesn't behave the same as the UITableViewCell segue.
Thanks in advance for any help!
Dont connect a segue manually do it through button action.
This is assuming the viewController that has this button is the root view controller of the navigation controller
#IBAction func myButtonTapped(_ sender: Any) {
let vc = self.storyboard!.instantiateViewController(withIdentifier: "YOUR STORYBOARD IDENTIFIER GOE HERE")
self.show(vc, sender: self)
}
If you want to go to a tab of a tab bar controller you have to specify its index. I think you can set it in storyboard but i just go with 0 is on the left then they go up sequentially. so in the example below i want to go to the second tab of the tab bar controller with a modal transition. I assume you can use show here like example above I've just never done it.
let tbc = self.storyboard!.instantiateViewController(withIdentifier: "MyTabController") as! UITabBarController
tbc.selectedIndex = 1 // this is 2nd tab index.. start at 0
tbc.modalPresentationStyle = .overCurrentContext
tbc.modalTransitionStyle = .coverVertical
self.present(tbc, animated: true, completion: { finished in
// you can also do some completion stuff in here if you require it.
// self.view.removeFromSuperview()
// self.navigationController?.navigationBar.removeFromSuperview()
})
let vc = self.storyboard!.instantiateViewController(withIdentifier: "StoryBoardID")
self.navigationController?.pushViewController(vc, animated: true)
I'm trying to program an iOS App in Swift, where there would be a splash screen before the tab bar screen.
I've looked at:
http://sweettutos.com/2014/01/08/present-a-login-screen-before-the-tab-bar-controller-in-a-uitabbarcontroller-based-app/
Loading a Welcome Screen (Splash Screen) before TabBarController
but both of them require a nib file and I did all my UI in storyboard.
Here's my main question:
The code on the website has something like this:
ControllerName(nibName: "NibName", bundle: nil);
Is there any way I could do the same thing without the nib file and use storyboard?
Assuming that you have the TabBarController already in storyboard, you would add another view controller next to it.
Then you would click on the new view, and in the attributes inspector, check off "Is initial View Controller".
That makes that new view the first thing that comes up when the app is opened. To go to the tabBarController, I would probably have some button on that first view that just has a segue(control drag) to the TabBarController.
What you can do is the following the example in which the tab bar view controller is set as initial viewcontroller you can
Drag and drop another UIViewController from the Object Library into the storyboard object library.
Click on the UIViewController and assign it a StoryboardID
StoryboardID.
In your first view controller class you can access the storyboard and create an instance of the new view controller, that you dragged and dropped before, using the storyboard id, you can do it this in the viewDidAppear override function and present the new view controller modally.
override func viewDidAppear(animated: Bool) {
let myStoryboard = self.storyboard
let modalViewController = myStoryboard?.instantiateViewControllerWithIdentifier("ModalViewControlllerID")
presentViewController(modalViewController!, animated: true, completion: nil)
}
To instantiate programmatically a viewController from a storyboard in a similar way as you would do from a xib with ControllerName(nibName: "NibName", bundle: nil), it's:
UIStoryboard(name: "StoryboardName", bundle: nil)
.instantiateViewController(withIdentifier: "ControllerName")
But you must not forget to set the arbitrary identifier of your viewController in the storyboard under Storyboard ID:
Drag navigationController and viewController to storyboard and make navigationcontroller as initial viewcontroller then push tabbarcontroller from viewcontroller.
Thanks,
Nada Gamal
I just needed to do the same for my project. Here is my solution.
I created a ViewController and made it the Storyboard Entry Point in the Main.storyboard. I previously had a TabViewController with all kinds of setup and needed only 1 change there.
TabBarController should have a StoryboardID, I named my "TabBarID".
I created a new LaunchViewController swift file with that new ViewController added to my Main.storyboard and assigned it in Main.storyboard.
Here is that class. Very simple with a timer that once over the threshold will then present the TabBarViewController in fullscreen.
class LaunchViewController: UIViewController {
var timer = Timer()
var time = 0
var threshold = 2
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundTheme(theme: .orangeWhite)
Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(updateTimer)), userInfo: nil, repeats: true)
}
#objc func updateTimer() {
time += 1
if time > threshold {
timer.invalidate()
guard let storyboard = self.storyboard else {
//failed to find storyboard
return
}
let tabBarController = storyboard.instantiateViewController(identifier: "TabBarID")
tabBarController.modalPresentationStyle = .fullScreen
present(tabBarController, animated: true, completion: nil)
}
}
}