I have a basic navigation setup in my Storyboard: a vanilla UIViewController embedded in a UINavigationController. In my main VC I have two buttons that each segue to a UIViewController subclass: LabelledVC. In the subclass's viewDidAppear(_:) method I set the navigation item's titleView to a custom image:
class LabelledVC: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let logoImage = UIImage(named: "apple")
let logo = UIImageView(image: logoImage)
logo.contentMode = .scaleAspectFit
logo.frame = CGRect(x: 0, y: 0, width: 32, height: 32)
navigationItem.titleView = logo
}
}
For some reason LabelledVC's viewDidAppear(_:) method is being called when the app loads (before it is pushed onto the navigation stack) which doesn't make any sense to me. You can find the project here.
Your MainVC is inherit from LabelledVC. So when application did show this controller the system calling viewDidAppear in ViewController but you don't have implementation for this method, so system call this method from parent class.
One other thing. For your example best place to configure NavigationItem is viewDidLoad method.
Related
I have a simple VC:
class ViewController: UIViewController {
lazy var button = UIButton(frame: CGRect(x: view.frame.width/2-100, y: view.frame.height/2-25, width: 200, height: 50))
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
title = "ViewController"
view.addSubview(button)
button.configuration = .tinted()
button.configuration?.title = "Click me"
button.configuration?.baseBackgroundColor = .systemPink
button.configuration?.baseForegroundColor = .systemPink
button.addTarget(self, action: #selector(click), for: .touchUpInside)
}
And the function for the button:
#objc
func click() {
let newVC = ViewController()
newVC.title = "ViewController 2"
newVC.view.backgroundColor = .systemCyan
var copyVCS = self.navigationController!.viewControllers
print("\n-----Copied Stack------\n\(copyVCS)")
copyVCS = copyVCS.dropLast()
copyVCS.append(newVC)
print("\n-----Mutated Stack------\n\(copyVCS)")
self.navigationController!.setViewControllers(copyVCS, animated: true)
print("\n-----New Navigation Stack-----\n\(navigationController!.viewControllers)")
}
Basically, I am testing a bug I have in a larger app I'm working on.
The issue is where I am setting the new navigation stack by calling
self.navigationController?.setViewControllers(copyVCS, animated: true)
Seems like the new navigation stack isn't the same as the copyVCS that I pass to the above method's argument.
The console after clicking the button:
-----Copied Stack------
[<VCBugReproduce.ViewController: 0x149d090f0>] // ✓
-----Mutated Stack------
[<VCBugReproduce.ViewController: 0x149f04440>] // ✓
-----New Navigation Stack-----
[<VCBugReproduce.ViewController: 0x149d090f0>, // ˟
<VCBugReproduce.ViewController: 0x149f04440>]
Is there a reason the new navigation stack isn't the same as the mutated stack? for some reason, the popped ViewController still appears in the navigation stack, but it appears now at the first index of the navigation stack array.
The docs say:
If animations are enabled, this method decides which type of
transition to perform based on whether the last item in the items
array is already in the navigation stack. (either a push or a pop...)
Only one transition is performed, but when that transition
finishes, the entire contents of the stack are replaced with the new
view controllers.
Is it that you're reading the value of viewControllers before the animation transition has completed, and that is before the value has in fact changed to the 'after animation' value.
I implemented the share extension and I want animate my View Controller with a crossDissolve, so i set the modalPresentationStyle = .overFullScreen and modalTransitionStyle = crossDissolve but it seems not working. The VC still appear from the bottom to the top and with the new iOS 13 modal style (not completly full screen).
Anyone know how to solve it? It tried both with and without storyboard.
NB: I'm not talking about a normal VC presentation, but the presentation of the share extension, it means that it's another app that present my VC.
One way to do it would be to have the system presented viewcontroller as a container.
And then present your content viewcontroller inside modally.
// this is the entry point
// either the initial viewcontroller inside the extensions storyboard
// or
// the one you specify in the .plist file
class ContainerVC: UIViewController {
// afaik presenting in viewDidLoad/viewWillAppear is not a good idea, but this produces the exact result you are looking for.
// meaning the content slides up when extension is triggered.
override func viewWillAppear() {
super.viewWillAppear()
view.backgroundColor = .clear
let vc = YourRootVC()
vc.view.backgroundColor = .clear
vc.modalPresentationStyle = .overFullScreen
vc.loadViewIfNeeded()
present(vc, animated: false, completion: nil)
}
}
and then use the content viewcontroller to show your root viewcontroller and its view hierarchy.
class YourRootVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let vc = UIViewController() // your actual content
vc.view.backgroundColor = .blue
vc.view.frame = CGRect(origin: vc.view.center, size: CGSize(width: 200, height: 200))
view.addSubview(vc.view)
addChild(vc)
}
}
Basically a container and a wrapper in order to get the control over the views being displayed.
Source: I had the same problem. This solution works for me.
I am using right Slider menu in my application in which i have set NavigationController height but if we are navigating through slider menu options then the view show the default height of slider navigation controller.
Navigation Bar Height:
self.navigationController?.navigationBar.frame = CGRect(x:0, y:0, width:self.view.frame.size.width, height:80)
In slider menu, we are navigating like this:
let mainViewController = storyboard.instantiateViewController(withIdentifier: "MainViewController") as! MainViewController
self.loginViewController = UINavigationController(rootViewController: mainViewController)
self.slideMenuController()?.changeMainViewController(self.allPhotosViewController, close: true)
As per my understanding you can't did customization in Default NavigationBar frame but yes it is possible with CustomNavigationBar.
Create CustomNavigationBar extend from UIView & make design as per need .
Here is the code for what #Mukesh has shown:
import UIKit
class CustomNavigationBar: UINavigationBar
{
override func draw(_ rect: CGRect)
{
// Drawing code
self.frame.size.height = 90
self.backgroundColor = UIColor.red
}
}
You don't need a Xib file. You can just create a Swift file and then in you Storyboard file edit the NavigationBar Custom Class : Class which is what #Mukesh has done in his screen shot. I have tested it and it works.
I've created a UINavigationController with a UIToolbar. Inside the UIToolbar there are multiple UIBarButtonItems. The UIToolbar has a subclass which i use to set the toolbar settings and create the UIBarButtonItems.
By pressing a UIBarButtonItem I want to navigate to another ViewController. As you can see in the code below, I've created a function for .addTarget, called "settingsPressed".
//SetToolbar
class ToolbarClass: UIToolbar {
//Set height of toolbar
override func sizeThatFits(_ size: CGSize) -> CGSize {
var size = super.sizeThatFits(size)
size.height = 60
return size
}
//Toolbar settings
override func layoutSubviews() {
super.layoutSubviews()
//Default
self.isTranslucent = false
self.barTintColor = UIColor(red: 48/255, green: 148/255, blue: 172/255, alpha: 1)
//Buttons
//Settings
let settingsBtn = UIButton()
settingsBtn.frame = CGRect(x: 0, y: 0, width: 46, height: 46)
settingsBtn.setImage(UIImage(named: "Settings-Button")?.withRenderingMode(.alwaysOriginal), for: .normal)
settingsBtn.addTarget(self, action: #selector(self.settingsPressed), for: .touchUpInside)
let settingsButton = UIBarButtonItem()
settingsButton.customView = settingsBtn
self.setItems([settingsButton], animated: false)
}
func settingsPressed() {
//How to navigate to a viewcontroller?
}
}
I've found some swift codes to navigate to another viewcontroller, but these codes don't work in my situation because i'm using a subclass. In this case the ".self.storyboard?" doesn't make sense:
let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: "ClassesOverviewViewController") as! ClassesOverviewViewController
self.navigationController?.pushViewController(secondViewController, animated: true)
Your implementation breaks MVC principles and causes the problems which you shouldn't face at all.
You should not add controller logic (creating new VC and navigating to it) into view (UIToolbar and it's subclass are view elements).
First way to fix it: add UIToolbar to storyboard view with added settings bar button item, connect action of bar button item with function from your VC, implement this function to navigate.
Second way to fix it: leave subclass of UIToolbar (it is not preferable if you only adding bar button item in subclass), declare public property for settings bar button item, use target and action to set your VC as target and function from VC as action for settings bar button item, implement this function to navigate.
I am having troubles setting the self.navigationItem.titleView, could someone please help me catch my mistake.
import Foundation
class CustomNavigationController: UINavigationController
{
override func viewDidLoad() {
let logo = UIImage(named: "browse_back")
var hexColor = 0x21BBD4 as UInt
self.navigationBar.barTintColor = GeneralHelper.UIColorFromRGB(hexColor)
self.navigationItem.titleView = UIImageView(image: logo)
}
}
Here is my code for setting the titleView to an image.
When I run the application, the color of the navigation bar is being changed to the correct color, but the titleView image is not displaying.
I've tested to ensure the image does exist.
Thanks.
The managing UINavigationController object uses the navigation items
of the topmost two view controllers to populate the navigation bar
with content.
Source: UINavigationItem Class Reference
You have to set the titleView of the navigationItem of the controller that is the top most controller in the navigation stack managed by your custom navigation controller.
For those using a UILabel as your titleView
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
navigationItem.titleView?.sizeToFit()
}
Hope this works!