Is there a way to reposition the UIKit tab bar vertically? Like the
gmail app navigation
This question has been asked quite a few times. None of the solutions I found seem to work for me. I’m looking for a solution from scratch in Swift, no third party libs.
Just like what #rmaddy has commented, you will need to create a customised tab and place it in a view as shown below.
FYI, you can easily build a customised tab bar by using buttons.
Once clicked, you just need to add the new controller as a subview by using the extension below.
extension UIViewController {
func add(parentViewController: UIView, childViewController: UIViewController) {
// Add Child View Controller
addChild(childViewController)
// Add Child View as Subview
parentViewController.addSubview(childViewController.view)
// Configure Child View
childViewController.view.frame = parentViewController.bounds
// Notify Child View Controller
childViewController.didMove(toParent: self)
}
}
So from your main view controller, it should look something like this.
class MainController: UIViewController {
// Outlets
#IBOutlet weak var mainView: UIView!
// Lazy loaded controller
private lazy var tab1Controller: Tab1Controller = {
// Instantiate View Controller
let tab1Controller = UIStoryboard(name: "Tab1Controller", bundle: nil).instantiateViewController(withIdentifier: "Tab1") as! Tab1Controller
return tab1Controller
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
// MARK: - Tab Bar
#IBAction func Tab1TouchUpInside(_ sender: Any) {
// Navigate to child view
self.add(parentViewController: self.mainView, childViewController: self.tab1Controller)
}
}
Hope it helps!
Related
I am embedding an AVPlayerViewController(which is expensive in terms of resources) in a UIViewController (using containment). I need to use another AVPlayerViewController in the subsequently pushed view controller in navigation stack, but it would be nice if I can remove it from the parent & embed it in the child. When the child pops, I want to embed it back in the parent. What is the elegant way to do this (code or storyboards)?
You need to do it in code. Create your 'expensive' view controller and store it using strong reference somewhere. You can show it programmatically anywhere, then you can dismiss it, but it will be store by strong reference. Later you can show it again.
P.S. looks like you view controller have AVPlayer, in this case probably you need to add some method to 'wipe' its state before reuse or at leave pause playback
It can be done like this way. You need to setup player .
import UIKit
import AVKit
class AVViewController: UIViewController {
static var player : AVPlayerViewController?
#IBOutlet var containerView: UIView!
override func viewWillAppear(_ animated: Bool) {
DispatchQueue.main.async {
self.addChildViewController(AVViewController.player!)
self.containerView.addSubview((AVViewController.player?.view)!)
// setup player here.
}
super.viewWillAppear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
for vc in self.childViewControllers{
if let vc = vc as? AVPlayerViewController, AVViewController.player == nil {
AVViewController.player = vc
}
}
// Do any additional setup after loading the view.
}
}
I have the following setup:
StartViewController has a ContainerView that contains ContainerViewController
I try to find a way to hidden an element in StartViewController after a task is performed in ContainerViewController.
For this I try to use delegation method like this:
StartViewController
class StartViewController: UIViewController, showBannerAdDelegate {
#IBOutlet weak var bannerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
bannerView.hidden = false
}
func bannerAdHidden(status: Bool) {
bannerView.hidden = status
}
}
ContainerViewController
protocol showBannerAdDelegate: class {
func bannerAdHidden(status: Bool)
}
class ContainerViewController: UIViewController {
weak var delegate: showBannerAdDelegate! = nil
#IBAction func buttonPressed(sender: UIButton) {
delegate.bannerAdHidden(true)
}
}
If I presented the ContainerViewController I could do in prepareForSegue
let destination = segue.destinationViewController as! ContainerViewController
destination.delegate = self
But in this case both View Controller are always present.
What code should I add to the View Controller to make it work?
Thank you,
If one of the view controllers is inside a container view then it is loaded with an embed segue, which fires when the containing view controller is first loaded. The prepareForSegue method still gets called, so you can set up a delegate exactly as you've described. I always thought embed segues were a little odd (it's not really a segue, more like loading a child view controller) but that's how it works.
EDIT
I think my problem is that I add the views as subviews in the same view, thats why I can't remove it ?
Im trying to learn swiping between views using XIB.
My storyboard contains 3 views
-Login
-Create Account
-View with scrollview that scrolls between a tableview and a blank view. This view has an embedded navigation controller (Editor -> Embed In -> Navigation Controller)
I Don't want the navigation controller to be shown in my blank page.
I have created the tableView Controller and the blank UIControllerView by adding them as "addChildViewController", See code below
import UIKit
class MasterViewForScroll: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
let Inbox : FriendlistTableBarView = FriendlistTableBarView(nibName: "FriendlistTableBarView", bundle: nil)
let Camera : CameraViewController = CameraViewController(nibName: "CameraViewController", bundle: nil)
func creatingSubViews() {
self.addChildViewController(Inbox)
self.scrollView.addSubview(Inbox.view)
Inbox.didMoveToParentViewController(self)
Inbox.navigationController?.navigationBar.hidden = false
var CameraView = Camera.view.frame
CameraView.origin.x = self.view.frame.width
Camera.view.frame = CameraView
self.addChildViewController(Camera)
self.scrollView.addSubview(Camera.view)
Camera.didMoveToParentViewController(self)
self.scrollView.contentSize = CGSizeMake(self.view.frame.width * 2, self.view.frame.height)
}
override func viewDidLoad() {
super.viewDidLoad()
creatingSubViews()
}
So my question is: How do I hide the navigation controller in the "Camera" view.
Thank you
In your CameraViewController class, add the following code:
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBarHidden = true
}
Next, in your FriendlistTableBarView class (I guess it is a UIViewController subclass), add the following code:
override func viewWillAppear() {
super.viewWillAppear()
self.navigationController?.navigationBarHidden = false
}
So, when you swipe to the right - navigation bar will hide, and when you swipe to the left - it will appear again.
In my application I have a uiview at the top of the screen. When I tap that UIView I have a second View Controller slides up. What I would like to accomplish is that when the second View Controller slides into the screen it stops right before it covers up the UIView
What I am trying to accomplish is have a view slide up that contains store information such as hours open, address, phone number etc. So I was thinking that I could just have another view controller that holds all of this information. Only part is I want to stop it sliding up so it is flush with the uiview bar on top.
//tap to bring up the second view controller
#IBAction func showInfoVC(sender: AnyObject) {
self.performSegueWithIdentifier("showSecondVC", sender: self)
}
It sounds like your goals are:
Have a base View
Slide a second View part way up on the first
Assuming this is the case, there are multiple ways you could accomplish this, but Apple would probably recommend View Controller Containment. To accomplish this, you will have:
A SlidingContainerViewController.
This is a custom container View Controller that will hold our other two View Controllers
Some background View Controller
Some foreground View Controller
Here is a basic implementation of a custom SlidingContainerViewController
// SlidingContainerViewController.swift
import UIKit
class SlidingContainerViewController: UIViewController {
//MARK: - Init
init(frontViewController: UIViewController, backViewController: UIViewController) {
super.init(nibName: nil, bundle: nil)
frontViewContoller = frontViewController
backViewContoller = backViewController
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//MARK: - Public
var frontViewContoller: UIViewController = UIViewController()
var backViewContoller: UIViewController = UIViewController()
var splitOriginY: CGFloat = 160.0
func toggleFrontView() {
if frontIsVisible {
UIView.animateWithDuration(0.4) {
self.frontViewContoller.view.frame.origin.y = self.view.frame.height
self.frontIsVisible = false
}
} else {
UIView.animateWithDuration(0.4) {
self.frontViewContoller.view.frame.origin.y = self.splitOriginY
self.frontIsVisible = true
}
}
}
//MARK: - ViewLifecycle
override func viewDidLoad() {
super.viewDidLoad()
addChild(backViewContoller)
addChild(frontViewContoller)
self.frontViewContoller.view.frame.origin.y = self.view.frame.height
}
//MARK: - Private
var frontIsVisible = false
private func addChild(viewController: UIViewController) {
addChildViewController(viewController)
view.addSubview(viewController.view)
viewController.view.frame = view.bounds
viewController.didMoveToParentViewController(self)
}
}
You can then put any custom View Controllers that you want into this container View Controller. The bottom View Controller just needs to call toggleFrontView() on the container View Controller whenever it wants the slide to occur.
Below I've added two sample View Controllers for demonstrations purposes.
You can view the whole project on github: SlidingVC
*Note: This solution is implemented programmatically without Interface Builder. I personally build all of my apps completely in code this way. If you desired to use Interface Builder, you could accomplish the same thing using a Storyboard and custom segues. Here is a related tutorial: A Beginner’s Guide to Animated Custom Segues in iOS 8
I have four ViewController, I don't use an UITabbedbar because It's more difficult to customize.
I use modal segue but I think the memory consumption is excessive.
this is a screen shot of my first and second VC.
What I have to use to change View correctly?
That's the code I use :
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "second") {
let secondVC = segue.destinationViewController as SecondViewController;
}
}
From your Storyboard diagram, it is clear that you have created a segue from each button in your "tab bar" to another view controller. Except for the unwind segue, segues always create a new instance of the view controller they are switching to. So if you use your setup to switch from view controller 1 to view controller 2 and then back to view controller 1, you won't be returning to the view controller you came from but instead you will be creating an entirely new view controller 1.
This is why your memory consumption is excessive. You keep creating view controllers until your app crashes.
I would recommend you return to using a tab bar controller. They were designed to allocate the view controllers once up front and then just switch between them. Also, they have a standard look for a reason, it helps the user of your app know immediately how to interact with them.
To pass data between tabs, you won't use segues because there is no segue happening when you switch tabs. There are many ways you can do this, but they all boil down to having model data stored where all of the tabs can access it. This can be done with CoreData in a larger app. For a simple app, you can do the following:
Create a custom subclass of UITabBarController. Let's call it CustomTabBarController. Have that class create and hold the model data that will be accessed by each of your tabs.
CustomTabBarController.swift:
import UIKit
// This class holds the data for my model.
class ModelData {
var name = "Fred"
var age = 50
}
class CustomTabBarController: UITabBarController {
// Instantiate the one copy of the model data that will be accessed
// by all of the tabs.
var model = ModelData()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
In your Storyboard, in the Identity Inspector, change the class of UITabBarController to CustomTabBarController.
In viewWillAppear in each of your tabs, get a reference to the model data and then you can use it.
FirstViewController.swift:
import UIKit
class FirstViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Get a reference to the model data from the custom tab bar controller.
let model = (self.tabBarController as! CustomTabBarController).model
// Show that we can access and update the model data from the first tab.
// Let's just increase the age each time this tab appears and assign
// a random name.
model.age += 1
let names = ["Larry", "Curly", "Moe"]
model.name = names[Int(arc4random_uniform(UInt32(names.count)))]
}
}
SecondViewController.swift:
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var ageLabel: UILabel!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Get a reference to the model data from the custom tab bar controller.
let model = (self.tabBarController as! CustomTabBarController).model
// This tab will simply access the data and display it when the view
// appears.
nameLabel.text = model.name
ageLabel.text = "\(model.age)"
}
}