how can I manage controllers in Container View with using tab bar - ios

On my storyboard I have main ViewController, not TabBarViewController, which consist of TabBar on the bottom, view on the top and ContainerView on the middle. ContainerView have a NavigationController. I also have 4 ViewControllers, one of them - RootViewController of NavigationController. I wish to show one of ViewControllers when I selecting TabBarItem, and in future I will add slide menu, which also will show selected ViewController.
I have next code, which only shows initial ViewController inside ContainerView, and when I selecting TabBarItems, new ViewControllers don't showing and I see only first View Controller. What goes wrong?
class ViewController: UIViewController {
#IBOutlet weak var container: UIView!
#IBOutlet weak var first: UITabBarItem!
#IBOutlet weak var second: UITabBarItem!
#IBOutlet weak var third: UITabBarItem!
#IBOutlet weak var fours: UITabBarItem!
#IBOutlet weak var tabBar: UITabBar!
var firstVC: FirstViewController?
var secondVC: SecondViewController?
var thirdVC: ThirdViewController?
var foursVC: FoursViewController?
var navi: UINavigationController?
override func viewDidLoad() {
super.viewDidLoad()
tabBar.delegate = self
initialSetup()
}
func initialSetup() {
tabBar.selectedItem = tabBar.items?.first
navi = self.storyboard?.instantiateViewController(withIdentifier: "containerNavi") as? UINavigationController
firstVC = self.storyboard?.instantiateViewController(withIdentifier: "FirstViewController") as? FirstViewController
secondVC = self.storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController
thirdVC = self.storyboard?.instantiateViewController(withIdentifier: "ThirdViewController") as? ThirdViewController
foursVC = self.storyboard?.instantiateViewController(withIdentifier: "FoursViewController") as? FoursViewController
}
func showVC(number: Int) {
switch number {
case 0:
navi?.popToRootViewController(animated: true)
print("0")
case 1:
if let second = secondVC {
navi?.pushViewController(second, animated: true)
}
print("1")
case 2:
if let third = thirdVC {
navi?.pushViewController(third, animated: true)
}
print("2")
case 3:
if let fours = foursVC {
navi?.pushViewController(fours, animated: true)
}
print("3")
default:
return
}
}
}
extension ViewController: UITabBarDelegate {
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
showVC(number: item.tag)
}
}
Storyboard screenshot:

You can try to use this extesion to add/remove any of the 4 to containerView
extension UIViewController {
func add(_ child: UIViewController, frame: CGRect? = nil) {
addChildViewController(child)
if let frame = frame {
child.view.frame = frame
}
view.addSubview(child.view)
child.didMove(toParentViewController: self)
}
func remove() {
willMove(toParentViewController: nil)
view.removeFromSuperview()
removeFromParentViewController()
}
}
// use it like this
let vc = self.storyboard?.instantiateViewController(withIdentifier: "first")
self.add(vc, frame: self.containerView.frame)
to remove
vc.remove()

Related

Xcode: instantiating a view controller with custom variable

So I want to instantiate a view controller from storyboard and change its static variables.
This is "vc1" - the view controller to be instantiated:
import UIKit
class vc1: UIViewController {
#IBOutlet weak var lbl_title: UILabel!
static var title = "initial value"
override func viewDidLoad() {
super.viewDidLoad()
lbl_title.text = vc1.title
}
}
And this is my root vc.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var btn_go: UIButton!
#IBAction func btn_gogogo(_ sender: Any) {
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "vc1") as! vc1
vc.title = "bla"
self.present(vc, animated: true, completion: nil)
}
}
Here I'm trying to change the static variable of the view controller that I just instantiated,
with no effect. The variable ( in my case 'title' ) is always stuck to its initial value.
What is the problem here?
Best
Mark
Don't try to override the view controller's title property. Instead, create your own:
class vc1: UIViewController {
#IBOutlet weak var lbl_title: UILabel!
var myTitle = "initial value"
override func viewDidLoad() {
super.viewDidLoad()
lbl_title.text = myTitle
}
}
class ViewController: UIViewController {
#IBOutlet weak var btn_go: UIButton!
#IBAction func btn_gogogo(_ sender: Any) {
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "vc1") as! vc1
vc.myTitle = "bla"
self.present(vc, animated: true, completion: nil)
}
}

Cannot switch to another child view controller in container view

I have a main view controller, a container view and 2 child view controllers and i would like to be able to switch between the children (for example: when the application loads for the first time, i would like that the controller containing the MapView to be loaded and when i press the Search Bar found in the main view, the controller with the table to be loaded).
Here is my storyboard: https://i.stack.imgur.com/rDPMe.png
MainScreen.swift
class MainScreen: UIViewController {
#IBOutlet private weak var searchBar: UISearchBar!
#IBOutlet private weak var ContainerView: UIView!
//private var openSearchBar: Bool?
private var openMapView: Bool = true
private var openPlacesList: Bool = false
private var containerView: ContainerViewController!
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//let containerView = segue.destination as? ContainerViewController
if containerView == nil{
containerView = segue.destination as? ContainerViewController
}
if openMapView == true{
containerView!.moveToMapView()
}
else if openPlacesList == true{
containerView!.MoveToOpenPlaces()
}
}
}
//search bar delegate functions
extension MainScreen: UISearchBarDelegate{
//detects when text is entered
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
openPlacesList = true
openMapView = false
containerView!.MoveToOpenPlaces()
}
}
ContainerViewController.swift:
class ContainerViewController: UIViewController {
private var childViewController: UIViewController!
private var first: UIViewController?
private var sec: UIViewController?
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MainToMap"{
first = segue.destination as! MapViewController
self.addChild(first!)
self.view.addSubview(first!.view)
self.didMove(toParent: self)
}else{
sec = segue.destination as! PlacesListController
}
if(first != nil && sec != nil){
interchange(first!,sec!)
}
}
func interchange(_ oldVc: UIViewController,_ newVc: UIViewController ){
oldVc.willMove(toParent: nil)
self.addChild(newVc)
self.view.addSubview(newVc.view)
self.transition(from: oldVc, to: newVc, duration: 2, options: UIView.AnimationOptions.transitionCrossDissolve, animations: {
newVc.view.alpha = 1
oldVc.view.alpha = 0
}, completion: { (complete) in
oldVc.view.removeFromSuperview()
oldVc.removeFromParent()
newVc.willMove(toParent: self)
})
}
func moveToMapView(){
performSegue(withIdentifier: "MainToMap", sender: nil)
}
func MoveToOpenPlaces(){
performSegue(withIdentifier: "MainToSearches", sender: nil)
}
}
The problem is that when I press the search bar, it calls the method interchange and then it just gives a SIGABRT 1 error. I tried this tutorial: https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/ImplementingaContainerViewController.html#//apple_ref/doc/uid/TP40007457-CH11-SW1 and many more but so far no luck. I am stucked here and don't know how i can solve this problem.
Stack: https://i.stack.imgur.com/Zqpm1.png
SIGABR 1 Error: https://i.stack.imgur.com/NBgEN.png
You appear to be trying to manually transition between child view controllers, but at the same time using segues (which do their own transitioning for you). Eliminate the segues (other than the initial embed segue, if you're using a storyboard with a "container view"), and just manually instantiate the child view controllers using their storyboard IDs. But don't use segues and then try to replace the child view controllers in prepare(for:sender:).
Also, when you use transition(from:to:duration:options:animations:completion:), you should not add the views the the view hierarchy yourself. That method does that for you (unless you use the showHideTransitionViews option, which tells the method that you're taking this over, something we don't need to do here). Likewise, when you use the transitionCrossDissolve option, you don't need to mess with alphas, either.
Thus, using the code snippet from that article you reference, you can do:
class FirstViewController: UIViewController {
#IBOutlet weak var containerView: UIView! // the view for the storyboard's "container view"
#IBOutlet weak var redButton: UIButton! // a button to transition to the "red" child view controller
#IBOutlet weak var blueButton: UIButton! // a button to transition to the "blue" child view controller
// tapped on "transition to red child view controller" button
#IBAction func didTapRedButton(_ sender: UIButton) {
redButton.isEnabled = false
blueButton.isEnabled = true
let oldVC = children.first!
let newVC = storyboard!.instantiateViewController(withIdentifier: "RedStoryboardID")
cycle(from: oldVC, to: newVC)
}
// tapped on "transition to blue child view controller" button
#IBAction func didTapBlueButton(_ sender: UIButton) {
blueButton.isEnabled = false
redButton.isEnabled = true
let oldVC = children.first!
let newVC = storyboard!.instantiateViewController(withIdentifier: "BlueStoryboardID")
cycle(from: oldVC, to: newVC)
}
func cycle(from oldVC: UIViewController, to newVC: UIViewController) {
// Prepare the two view controllers for the change.
oldVC.willMove(toParent: nil)
addChild(newVC)
// Get the final frame of the new view controller.
newVC.view.frame = containerView.bounds
// Queue up the transition animation.
transition(from: oldVC, to: newVC, duration: 0.25, options: .transitionCrossDissolve, animations: {
// this is intentionally blank; transitionCrossDissolve will do the work for us
}, completion: { finished in
oldVC.removeFromParent()
newVC.didMove(toParent: self)
})
}
func display(_ child: UIViewController) {
addChild(child)
child.view.frame = containerView.bounds
containerView.addSubview(child.view)
child.didMove(toParent: self)
}
func hide(_ child: UIViewController) {
child.willMove(toParent: nil)
child.view.removeFromSuperview()
child.removeFromParent()
}
}
That yields:

Add a view controller as a subview in another view controller

I have already read this LINK , but not working for me. I want to show a viewController as a subview in another viewController.
Here is my code -
import UIKit
import CarbonKit
class ViewController: UIViewController, CarbonTabSwipeNavigationDelegate {
#IBOutlet weak var containerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let items = ["All", "WOMEN", "MEN", "KIDS", "HOME", "CITY"]
let carbonTabSwipeNavigation = CarbonTabSwipeNavigation(items: items, delegate: self)
carbonTabSwipeNavigation.insert(intoRootViewController: self)
}
func carbonTabSwipeNavigation(_ carbonTabSwipeNavigation: CarbonTabSwipeNavigation, viewControllerAt index: UInt) -> UIViewController {
// let screen = self.storyboard?.instantiateViewController(withIdentifier: "demo") as! demo
// showSubViewContrller(subViewController: vc)
// return screen
let storyBoard = getStoryBoardByIndentifier(identifier: "All")
let vc = storyBoard.instantiateViewController(withIdentifier: "AllViewController") as! AllViewController
showSubViewContrller(subViewController: vc)
return vc
}
//Subview Controller
func showSubViewContrller(subViewController:UIViewController) {
self.addChildViewController(subViewController)
subViewController.view.frame = containerView.frame
self.containerView.addSubview(subViewController.view)
subViewController.didMove(toParentViewController: self)
}
func getStoryBoardByIndentifier(identifier:String)->UIStoryboard {
return UIStoryboard.init(name: identifier, bundle: nil)
}
}
I have a NavigationBar and a tapBar. Would like to show the viewController inside the view in a container.
But when the view loads it's coverUp/hide the tabBar.
How to solve this and show the viewController in my specified container.
Project Link - GitHub
Somehow i am able to fix your issue with below changes:
Replace this method carbonTabSwipeNavigation.insert(intoRootViewController: self) with carbonTabSwipeNavigation.insert(intoRootViewController: self, andTargetView: containerView) in viewDidLoad
Note : Give UITaBar bottom constraint to SuperView not SafeArea:
Add below code in ViewController:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tabbar.invalidateIntrinsicContentSize()
}
After doing this when you run you will UITabBar:

I am trying to using a custom delegate in swift but when I want to call its methods, it did not get called

I have been searching for how the delegate works and I tried to do it in my project. Unfortunately, the delegate method I implement does not get called ever. I am trying to do a slide-out navigation panel. so what I did is that I put two uicontainerviews, one is for slide-out navigation panel and the other for main view controller
enter image description here
The code is that
For main view controller
protocol MainViewControllerDelegate {
func toggleSideMenu()
}
class MainViewController: UIViewController {
var delegate: MainViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
// MARK: - Slide Action
#IBAction func slideMenuTapped(_ sender: UIBarButtonItem){
delegate?.toggleSideMenu()
print("Slide Menu has been tapped")
}
}
For container view controller
class ContainerVC: UIViewController {
#IBOutlet weak var SideMenuConstraint: NSLayoutConstraint!
#IBOutlet weak var slideMenuContainer: UIView!
#IBOutlet weak var mainViewContainer: UIView!
var mainViewController: MainViewController?
var isSideMenuOpened = false
override func viewDidLoad() {
super.viewDidLoad()
mainViewController = UIStoryboard.mainViewController()
mainViewController?.delegate = self
}
}
extension ContainerVC: MainViewControllerDelegate{
func toggleSideMenu() {
print("It works")
if isSideMenuOpened{
isSideMenuOpened = false
SideMenuConstraint.constant = -260
mainViewContainer.layer.shadowOpacity = 0
} else {
isSideMenuOpened = true
SideMenuConstraint.constant = 0
mainViewContainer.layer.shadowOpacity = 0.59
}
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}
}
extension UIStoryboard{
static func mainStoryboard() -> UIStoryboard { return UIStoryboard(name: "Main", bundle: Bundle.main) }
static func mainViewController() -> MainViewController? {
return mainStoryboard().instantiateViewController(withIdentifier: "MainViewController") as? MainViewController
}
}
Please let know what's wrong
I think the reason is that you embed your main view controller in navigation controller :
let navigationController = self.childViewControllers.last as! UINavigationController
let mainViewController = navigationController.topViewController as! MainViewController
mainViewController?.delegate = self
Here is where you got wrong:
mainViewController = UIStoryboard.mainViewController()
mainViewController?.delegate = self
this mainViewController is not the same as the child of the container view controller, so setting its delegate doesn't really do anything.
You need to first get the VC that is the child of the container view controller:
mainViewController = self.childViewControllers.last as! MainViewController
mainViewController.delegate = self

Animate parent VC object from a child VC

Is it possible to animate views of a parent VC in Swift?
I've got a root/master VC with a UIView which I'm using as a sort of a UITabBarController, so the rest of my 4 main VCs are children of the root.
On some of the child VCs, I have subviews that should take up the whole screen, without seeing the custom tab bar (UIView) from the root VC, but it still floats above.
I would like to have it slide off the screen via Y axis whenever I open the fullscreen subviews, but I can't seem to access or manipulate the root VCs properties as it returns nil on runtime.
Here's the custom tab bar root VC so you can understand the structure of the code:
class RootVC: UIViewController {
//This is where we pull all of our content from other VCs
//when a tab bar button is selected
#IBOutlet weak var contentView: UIView!
//The custom tab bar itself with an array of button outlets
#IBOutlet public weak var customTabBarContainer: UIView!
#IBOutlet var tabBarButtons: [UIButton]!
//4 main view VCs that are reflected in the tab bar
public var mapVC: UIViewController!
public var favoritesVC: UIViewController!
public var chatVC: UIViewController!
public var profileVC: UIViewController!
//Array for the VCs above
public var viewControllers: [UIViewController]!
//Index of the selected button determend by their tags
public var selectedIndex: Int = 0
#IBOutlet weak var loadingLogo: UIImageView!
override public func viewDidLoad() {
//Populating viewControllers array with
//initiated VCs in Main storyboard
let storyboard = UIStoryboard(name: "Main", bundle: nil)
mapVC = storyboard.instantiateViewController(withIdentifier: "MapVC")
favoritesVC = storyboard.instantiateViewController(withIdentifier: "FavoritesVC")
chatVC = storyboard.instantiateViewController(withIdentifier: "ChatVC")
profileVC = storyboard.instantiateViewController(withIdentifier: "ProfileVC")
viewControllers = [mapVC, favoritesVC, chatVC, profileVC]
//Custom tab bar + buttons visual properties
customTabBarContainer.layer.cornerRadius = customTabBarContainer.frame.height / 2
customTabBarContainer.layer.shadowColor = UIColor.darkGray.cgColor
customTabBarContainer.layer.shadowOffset = CGSize.zero
customTabBarContainer.layer.shadowRadius = 10
customTabBarContainer.layer.shadowOpacity = 0.9
tabBarButtons[0].imageView?.contentMode = .scaleAspectFit
tabBarButtons[1].imageView?.contentMode = .scaleAspectFit
tabBarButtons[2].imageView?.contentMode = .scaleAspectFit
tabBarButtons[3].imageView?.contentMode = .scaleAspectFit
}
override public func viewDidAppear(_ animated: Bool) {
loadingLogo.popOut()
//Loads the initial VC
contentView.addSubview(mapVC.view)
mapVC.view.frame = self.view.frame
mapVC.didMove(toParentViewController: self)
customTabBarContainer.isHidden = false
//Selects the inital home button
tabBarButtons[0].isSelected = true
}
#IBAction func didTabButton(_ sender: UIButton) {
//Keeps a track of which bar button is selected
let previousIndex = selectedIndex
selectedIndex = sender.tag
//Deselects the previous bar button
tabBarButtons[previousIndex].isSelected = false
//Removes the previous VC
let previousVC = viewControllers[previousIndex]
previousVC.view.removeFromSuperview()
previousVC.removeFromParentViewController()
print("switced to \(viewControllers[selectedIndex])")
//Selects the tapped bar button
tabBarButtons[selectedIndex].isSelected = true
tabBarButtons[selectedIndex].popIn()
//Brings up the selected VC
let nextVC = viewControllers[selectedIndex]
contentView.addSubview(nextVC.view)
nextVC.view.frame = self.view.frame
nextVC.didMove(toParentViewController: self)
}
}
And here's the code I'm trying to use to manipulate the customTabBarContainer from a child of the MapVC:
UIView.animate(withDuration: 0.4, animations: {
let root = self.parent?.parent as! RootVC
root.customTabBarContainer.frame.origin.y -= root.customTabBarContainer.frame.height
}, completion: nil)
why are you trying to access the parent of the parent then?
self.parent?.parent as RootVC
assuming you are using an extension like this one to find your parentVC:
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.next
if parentResponder is UIViewController {
return parentResponder as! UIViewController!
}
}
return nil
}
}
you should be able to access parent via
let root = self.parentViewController as! RootVC
I've figured out an answer, just in case anyone else encounters a similar problem. It will not take you to the VCs immediate parent, but instead, to its most distant ancestor, which solves my particular problem in this case.
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let rootVC = appDelegate.window?.rootViewController as! RootVC
rootVC.customTabBarContainer.isHidden = true

Resources