I am learning iOS with few sample projects. I have two view controllers in that first VC has few buttons and a mapview and the second VC has tableview showing a set of results. I have embed the both viewcontrollers in navigationViewController.By clicking a button from First VC i am able to show the tableview (using show segue) and able to go back to first VC through navigation. Now my query is I want to display the tableview (second VC) in place of one view object (map view) defined in firstVC rather than padding the tableview entirely in full screen. My problem is when showing another Viewcontroller i still want to see the few viewobjects from firstVC so I am trying to display the secondVC on top of mapview when i click on a button which triggers the segue.I have to use the single interface, so I need to load the tablview results from SecondVC into firstVC by replacing mapView's view with tableview.Please let me know your ideas if it is possible and any other ideas to achieve the same are most welcomed.
Sreekanth Gundlapalli,
All you need to do is to add the TableView controller's view as subview to your view Controller. In order to simplify the process I personally prefer using the ContainerView,
Step 1 : Add a ContainerView to your View Controller and add the auto layout constraints to it, because your tableView will be loaded inside this container view you donate have to apply any auto layout constraint to your tableView to keep it in place :)
ex :
Step 2 : Drag an IBOutlet to the container view.lets call it as containerView :)
Step 3 : Now its up to you to have two view controller 1 for loading map and 1 for loading tableView, or you will have map view as your view controller subview and you will either hide it or remove it and add container view but I personally prefer having code clean n neat so I prefer having two different VCs
so lets create 2 VCs lets call them as viewController1 & viewController2 Savy ??
Step 4 :
lets write a method which actually loads VC and adds its view as subview to your ViewController
func changeEmbededVC(for status : Int) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if status == 0 {
mehereButton.tag = 1
let vc = storyboard.instantiateViewController(withIdentifier: "viewController1")
vc.willMove(toParentViewController: self)
containerView.addSubview(vc.view)
self.addChildViewController(vc)
vc.didMove(toParentViewController: self)
}
else {
mehereButton.tag = 0
let vc = storyboard.instantiateViewController(withIdentifier: "viewController2")
vc.willMove(toParentViewController: self)
containerView.addSubview(vc.view)
self.addChildViewController(vc)
vc.didMove(toParentViewController: self)
}
}
I believe code is pretty self explanatory :D now what is mehereButton.tag = 1 ?? Simple you want to toggle view on button press don't you :D hence I have created a IBOutlet for mehereButton and changing its tag :)
now finally in IBAction of mehereButton
#IBAction func buttonTapped(_ sender: UIButton) {
self.changeEmbededVC(for: self.mehereButton.tag)
}
but we need to load one of the view by default isn't it :D
so change your viewDidAppear to
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.changeEmbededVC(for: 0)
}
Hope I answered your question In detail :P I know you can't neither up vote or accept answer as you don't have enough reputation :) but hope it will help somebody in future as well :)
Related
I have created my own TabView the first tab is always the Home tab which contains a TableView . The other 3 Tabs Search, Menu and Inbox are subviews . I can go from
Home to Search then Back to Home and it works
Home to Menu then Back to Home and it works too
Home to Menu then to Search and back to Home brings me back to the Menu subview . I essentially want to eliminate all subviews when clicking the Home Tab . Also each TabView is in it's own controller .
This is my code
From Home Controller to Menu Controller
#IBAction func MenuTabAction(_ sender: UIButton) {
let Popup = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MenuC") as! MenuC
self.addChildViewController(Popup)
Popup.view.frame = self.view.frame
Popup.view.tag = 100
self.view.addSubview(Popup.view)
Popup.didMove(toParentViewController: self)
}
From Menu Controller to Home Controller & Search Controller
#IBAction func HomeTabAction(_ sender: UIButton) {
if let viewWithTag = self.view.viewWithTag(100) {
print("Tag 100")
viewWithTag.removeFromSuperview()
}
}
#IBAction func SearchTabAction(_ sender: UIButton) {
let Popup = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LocalSearchC") as! LocalSearchC
Popup.view.frame = self.view.frame
Popup.view.tag = 100
self.view.addSubview(Popup.view)
Popup.didMove(toParentViewController: self)
}
I am guessing that the remove superview only removes 1 superview at a time so if I go from Subview1 to subview2 then click on the HomeTab it brings me to subview1 instead of the original HomeTab . Is there a way to remove all superview/subviews when clicking the Home Tab ?
Each Tab got it's own view controller. Ideally, you should removeFromSuperview all controllers you're not showing.
At your code, you only removeFromSuperView at HomeTabAction.
Try to change it:
if let viewWithTag = self.view.viewWithTag(100) {
print("Tag 100")
viewWithTag.removeFromSuperview()
}
to
for v in self.view.subviews {
if v.tag == 100 {
v.removeFromSuperview()
}
}
But please, keep in mind that each time a user press any tabs without returning to home (i.e: tapping many times between Menu and Search), it's look like you are just instantiating many controllers, without removing them.
You should remove other Views every time a new one is instantiated. Would be wise to give a unique tag to each view controller and remove the hidden others after every change, not only when returning to Home. Or at least, check if the view controller with a given type already is instantiated before create a new one.
Actually, you don't need to manually instantiate the viewcontrollers (LocalSearch, Menu). TabViewcontrollers can link a vc with each tab item via a segue. In fact, when you add your tabvc to the project, it will come with 2 viewcontrollers, each connected to an item in the tabview, and that's it, you just need to replace them or adapt them, no need to "load" them.
The only scenario where you'd need to do this, is if your buttons were "dynamic", as in, the content to be loaded changes depending on some other circumstances. As long as clicking "Search" goes to LocalSearchViewController, just link it with a segue on the storyboard.
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 pretty new to iOS development, but I could not find an answer to this question (maybe I could not find the right keywords to search..?).
Anyway, I have this "problem":
I have 2 storyboards, A and B, both with some views inside.
I go from view A-2 to view B-1 via #IBAction - a simple button click - with the following code:
#IBAction func onExercisesButtonTapped(_ sender: AnyObject) {
let storyboard = UIStoryboard(name: "Exercises", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "ExercisesVC") as UIViewController
controller.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
present(controller, animated: true, completion: nil)
}
On the other hand, in the B-1 view I have a UICollectionView that is populated with data coming from a CoreData model.
The cells within the UICollectionView appear only after the animation is completed. If I set animated: false in the perform function and thus no animation is performed from A-2 view to B-1 view the cells are immediately visible.
The cells are the only element that appear after the completion of the animation. What is the cause of this behaviour? Can I avoid it with some sort of preloading/prefetching or other methods?
Is there a way to display the cells before the animation starts, just like the UICollectionView background and all the other elements within the view?
Thank you in advance :)
Update your view in viewWillAppear:.
I have just made a small transition so my project loads a different storyboard as its main (did it in the info.plist).
I have my new storyboard to keep my viewController that are responsible for login screen etc. Just to make it more clear.
After the login button is tapped I want to initiate a navigationController from another storyboard:
func instantiateViewController(fromStoryboard storyboard: String, withIdentifier identifier: String) -> UIViewController! {
let storyboard = UIStoryboard(name: storyboard, bundle: nil)
let viewController = storyboard.instantiateViewControllerWithIdentifier(identifier)
return viewController
}
#IBAction func loginButtonTapped(sender: UIButton) {
let viewController = instantiateViewController(fromStoryboard: "Main", withIdentifier: "MainNavigationController")
presentViewController(viewController, animated: true, completion: nil)
}
Everything works correctly but one thing is driving me nuts.
After presenting the MainNavigationController from Main.storyboard its view hierarchy is not maintained.
What I mean is, the labels and buttons which supposed to be on top of another, full screen UIView (but are not its child and so they should remain) are now behind it.
What might be causing this and what is the simplest way to make them appear on top (as they do whey I open main.storyboard)
EDIT
I added a line of code in the rootView of the MainNavigationController in its viewDidLoad method:
self.view.sendSubviewToBack(wholeScreenView)
and it solved the problem.
However, does anybody know why do I have to code it myself and the views are not like in the Main.storyboard?
The storyboard is not configured the way you think it is. Your wholeScreenView is in fact in front of the other views, in the storyboard. The other views (the labels and buttons) are not subviews of wholeScreenView; they are subviews of the main view, and so is wholeScreenView. It is a later subview, so it is in front of them.
I was wondering if i could make some sort of a segue between these 2 views in a Xib file.
the main View is loaded into a scrollview in the storyboard.
.
(so if ik click Bewerken(edit) i would get pushed to the view controller on the right)
Thanks!
I would suggest changing this to a Container View inside the storyboard instead of a separated Xib. You can add and position/size UIContainerView as a subview and add a"Storyboard Embed Segue to attach another ViewController. This is what it will look like in IB/Storyboard:
During runtime, the blue UIView (or the embedded UIViewController if you like) will be embedded in the hosting UIView as a subview:
If you change your implementation to this, you are in the beautiful segue world where you can just drag-and-drop segues :)
You could do 2 things:
1 - Wrap the main view in a Navigation Controller so you can do the following:
#IBAction func loadEditController(sender:UIButton){
let editController = RegisterController(nibName:"RegisterXIB", bundle:nil)
navigationController?.pushViewController(editController, animated: true)
}
2 - Present the Edit Controller over the current context and animate it yourself
#IBAction func loadEditController(sender:UIButton){
let editController = RegisterController(nibName:"RegisterXIB", bundle:nil)
editController.modalPresentationStyle = UIModalPresentationStyle.OverCurrentContext
presentViewController(editController, animated: false, completion: nil)
//Move it offscreen and the animate it here
}