Hi I'm new to iOS Development, I'm developing an iOS Application in that I want to use same footer throughout the app. Now I'm creating different uiviews in every controller but this is not optimal way. How can I create single uiview and reuse it in app. I'm using storyboards.In my footer I have four buttons
Create your reusable view as a XIB and load it programatically where ever you need it. This answer shows how to handle XIBs.
This should work fine for your footer.
For more complex reusable views, which make sense to have their own UIViewConroller:
create the viewController in storyboard
instantiate it like this:
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "someViewController")
add it as child viewController like this:
func add(childViewController controller: UIViewController, embedViewIn containerView: UIView) {
controller.willMove(toParentViewController: self)
addChildViewController(controller)
containerView.addSubview(controller.view)
// addCustomConstraints
}
You can later remove the child viewController like this:
func remove(childViewController controller: UIViewController) {
controller.willMove(toParentViewController: nil)
controller.view.removeFromSuperview()
controller.removeFromParentViewController()
}
You have to use custom tabBar, Have a look at https://github.com/hartlco/MHCustomTabBarController this link, I have implemented it its easy to understand.
Related
i'm working on multiple frameworks and my question is just a "philosophic" one.
I created an utility function to show view controllers
static func presentViewController(identifier: String, storyboardName: String = "Main", presentationStyle: UIModalPresentationStyle = .fullScreen){
let storyboard = UIStoryboard(name: storyboardName, bundle: InternalConstants.bundle)
var viewResult: UIViewController
if #available(iOS 13.0, *) {
viewResult = storyboard.instantiateViewController(identifier: identifier)
} else {
viewResult = storyboard.instantiateViewController(withIdentifier: identifier)
}
viewResult.modalPresentationStyle = presentationStyle
var top = UIApplication.shared.keyWindow?.rootViewController
while top?.presentedViewController != nil {
top = top!.presentedViewController
}
top!.present(viewResult, animated: true, completion: nil)
}
First of all, is this a correct way to present a view controller or is there a better way?
Then, is it better to present a view controller in a navigation controller or not?
First of all, is a correct way to present a view controller or there is a better way?
instead of a utility make it inside
extension UIViewController {
func ......
}
Then, is better to present a view controller in a navigation controller or not?
a nav is oriented for push/pop but it's also not wrong to use it to present another vc
First of all, is this a correct way to present a view controller or is there a better way?
as long as it's working then it's correct it's just your way of doing this specific thing, but is it the right thing to do as for an iOS and UIKit standpoint the answer is no it's usually is a bad thing to present a viewController by looking at the rootViewController's presentedViewController because it's not guaranteed that the last presentedViewController you find is a good thing to present on it and you won't know until it breaks, that presentedViewController could be a UISearchController and if you use UIContentContainer or ContainerView from storyboards, you might have a small viewController that is just a UISlider at the end, this could be bad for viewController appearance and disappearance
another problem that you will face is when you need to pass data to and from the viewController that you presenting by using this approach you don't even have a reference to the viewController you are presenting, because you are only passing an identifier
from an MVC standpoint you should never try to present viewController from a UIView by calling your function from your view directly Thats Bad Practice
if you take a look at the UIKit SDK if you ever try to present any system UIViewController you will find that you have the responsibility of instantiating and presenting the vc for example UIImagePickerController, UIActivityViewController, UIDocumentPickerViewController, UIDocumentMenuViewController, UIPrinterPickerController, UIVideoEditorController
Apple themselves didn't go for providing a function to present theirs system vcs
instead if you are developing a framework and don't want to give users access to your viewControllers you should make you own window and give it a rootViewController
Apple also has many examples for this too, in the AuthenticationServices framework for security reasons you should not have a reference to the safari web browser they have something called ASWebAuthenticationSession that controls the flow of presenting and dismissing the Safari Web ViewController by calling start() and cancel() functions
also the users of your framework will not always want to present your viewController with the default presentation animation they might want to use custom viewContollers animations which they will need access to the transitioningDelegate property on the viewController
imagine every public useful property on UIViewController will not be accessible if you go with this approach
Then, is it better to present a view controller in a navigation controller or not?
as for this part it's totally fine to present anything on a navigationController
Storyboards headaches
as for the storyboards initialization headaches there are plenty of articles out there talking about optimizing the storyboards initialization call site for that I would recommend doing something like this
extension UIStoryboard {
enum AppStoryBoards: String {
case
login,
main,
chat,
cart
}
convenience init(_ storyboard: AppStoryBoards, bundle: Bundle? = nil) {
self.init(name: storyboard.rawValue.prefix(1).capitalized + storyboard.rawValue.dropFirst(), bundle: bundle)
}
}
This way you can initialize a storyboards using enum which improves the call site to be like this
let login = UIStoryboard.init(.login)
then you can have another extension for view controller initialization like this
extension UIStoryboard {
func instantiateInitialVC<T: UIViewController>() -> T {
return self.instantiateInitialViewController() as! T
}
func instantiateVC<T: UIViewController>(_: T.Type) -> T {
return self.instantiateViewController(withIdentifier: String(describing: T.self)) as! T
}
}
and you can then call it like this
let loginVC = UIStoryboard.init(.login).instantiateInitialVC()
or this
let loginVC = UIStoryboard.init(.login).instantiateVC(LoginViewController.self)
by doing that you improve your overall code for presenting any viewController
let dvc = UIStoryboard.init(.login).instantiateVC(LoginViewController.self)
dvc.plaplapla = "whatever"
present(dvc, animated: true)
In my app I use side bar as in facebook. when the user slides out the side bar a uiimageview is displayed. when user taps on the image it takes hm to a different viewcontroller. the problem i am facing is that I have created sidebar programatically and the other view to which I want to navigate the user is created using storyboard. So my source view is created programatically and destination view is created using storyboard. So can someone explain me if there is any way of using "Segue" in this scenario. Since i can not create segue using storyboard I need to do it programatically but even after a lot of googling i could not find the answer.
Well, to get another instance of another storyboard programmatically you can use something like:
let newController = UIStoryboard(name: "MyStoryboard", bundle: nil).instantiateViewControllerWithIdentifier("MyIdentifier") as! MyViewController
and then you push to your navigation controller, or add as a child view controller or something...
If you don't wanna bother with identifiers you can just use the instantiateInitialViewController instead of instantiateViewControllerWithIdentifier
Might help
"userSB" is the viewcontroller storyboard identifier
#IBAction func tapSearchCriteria(_ sender: Any?) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let aVC = storyboard.instantiateViewController(withIdentifier: "userSB") as? AViewController
aVC?.modalPresentationStyle = UIModalPresentationStyle.custom
aVC?.transitioningDelegate = self
aVC?.udelegate = self
self.present(aVC!, animated: true, completion: nil)
}
I have been following the XLpagerTabStrip cocoapods extension to set up a tab bar at the top of my view controller (https://github.com/xmartlabs/XLPagerTabStrip). I am implementing the ButtonBarPagerTabStripViewController and have followed the steps exactly, but the UIScrollView is not displaying the child view controllers.
I think this is because in the example the child view controllers are set up programmatically rather than in the storyboard, whereas mine are currently set up in the storyboard, and hence are displaying no content, yet there is no way that I can see to connect the child view controllers to the ButtonBarController via the storyboard.
Is there a way to display the child view controllers using the storyboard, or must it be done programmatically?
The GitHub page says to implement the following function (But this doesnt seem to work if your child view controllers are set up via the storyboard):
override public func viewControllersForPagerTabStrip(pagerTabStripController: PagerTabStripViewController) -> [UIViewController]
{
return [MyChildViewController(), MySecondChildViewController()]
}
You need to load controller from storybard:
override public func viewControllersForPagerTabStrip(pagerTabStripController: PagerTabStripViewController) -> [UIViewController]
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let childOneVC = storyboard.instantiateViewControllerWithIdentifier("childOneVC")
let childTwoVC = storyboard.instantiateViewControllerWithIdentifier("childTwoVC")
return [childOneVC, childTwoVC]
}
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
}