Unable to inherit properties from base UIViewController class - ios

I'm new to iOS development and this may be a simple question. My application will have many view controllers which will share similar properties. I created a base view controller class which subclasses UIViewController. All other view controllers subclass the base view controllers. However, the properties I set in base view controller class, are not inherited. For example, the navigation bar buttons should all be the color green yet are the default blue color when I build the app.
Here's the base view controller class:
class BaseViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.leftBarButtonItem?.tintColor = UIColor.greenColor()
self.navigationItem.rightBarButtonItem?.tintColor = UIColor.greenColor()
}
This is the code for other view controllers that subclass the base view controller above:
class AboutViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = NSLocalizedString("About", comment: "About page title")
}
I've searched the web and SO for similar questions but wasn't able to find an answer. I don't see anything fundamentally wrong with my code. I am using Xcode 7.2.1 and testing on iOS 8+. Also tested in Xcode 7.2.3 beta but had similar results.

From what I see, this isn't an inheritance issue. If you set a break point in viewDidLoad of BaseViewController it does indeed execute that code. I think your issue is with how to change the tint color. Customizing the navigation bar in iOS is such a pain that I usually stray away from using navigation controller and end up just creating my own top bar view I can fully customize.
If you are trying to change the tint color of both left and right bar items for your entire app put this code in your AppDelegate file inside the application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) function:
UINavigationBar.appearance().tintColor = UIColor.greenColor()
Here's a blog post I created that shows how to change the other colors of UINavigationBar: Navigation Bar Colors.

Related

Trying to combine a Sidemenu and a Tapbarmenu with Xcode's Storyboards

I'm trying to combine a Sidemenu and a Tapbarmenu using Xcode's Storyboards.
I took the template from here: https://johncodeos.com/how-to-create-a-side-menu-in-ios-using-swift/
Then, I modified the Home View to have a tap bar :
The first issue that I got is that I lost the SideMenu button :
.
Furthermore, you can notice that the Topbar got really thick and the View is not centered.
To fix the first issue, I tried to create a new cocoapod file for the new navigation controller in order to get the SideMenu button working again :
class MenuViewController: UITabBarController {
#IBOutlet var sideMenuBtn: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
// Menu Button Tint Color
navigationController?.navigationBar.tintColor = .white
sideMenuBtn.target = revealViewController()
sideMenuBtn.action = #selector(revealViewController()?.revealSideMenu)
}
}
I assigned the new class to the Navigation controller but the sideMenuBtn won't appear on the Sidemenu button.
I don't know if my explanations were very clear but don't hesitate to ask me for more information.
Thx,
Julien

Swift UISplitViewController - unable to present master view before detail view (iOS 14, Xcode 12.4)

I am having this issue where I am using a UISplitViewController MainSplitVC, and I am unable to present the master view controller over the detail view controller. Basically, when testing this on an iPad, I want the master and detail VC to be visible side by side, but on an iPhone (portrait mode), I want only the master VC. Currently, this works on iPad, but on an iPhone in portrait mode, Swift is showing the detail view controller first, and I have to click the back button in the navigation bar to return to the master view controller.
I have tried every possible approach that I could think of. For instance, I created a class for the Split View Controller, MainSplitVC, where I subclass UISplitViewController and UISplitViewControllerDelegate. Then, in viewDidLoad(), I set the preferred display mode to oneBesideSecondary (since allVisible was replaced by that according to Xcode). I also include the function collapseSecondary() to always collapse back to the master view controller.
class MainSplitVC: UISplitViewController, UISplitViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
print("viewDidLoad called")
self.preferredDisplayMode = UISplitViewController.DisplayMode.oneBesideSecondary
}
func splitViewController(
_ splitViewController: UISplitViewController,
collapseSecondary secondaryViewController: UIViewController,
onto primaryViewController: UIViewController) -> Bool {
print("collapseSecondary called")
return true
}
}
I have consulted many other posts regarding this issue, and all the posts indicate that using the collapseSecondary() function and setting preferredDisplayMode to oneBesideSecondary or allVisible should do the job. However, none of this is working. What's more is that the collapseSecondary() function is not even being called, even though I included UISplitViewControllerDelegate in the class header.
Could anyone clarify if I made any mistakes in the code below? When the app opens, I just want the master view controller to be shown; the detail view controller is simply a blank view controller that changes to another when something in the tableview of the master view controller is clicked.
The links I used for reference were as follows:
UISplitViewController showing detail view controller first on iPhone, also delegate not calling proper functions
UISplitViewController in portrait on iPhone shows detail VC instead of master
Open UISplitViewController to Master View rather than Detail
Thanks!
EDIT 2/3/21: I've resolved the issue. I instead extended SceneDelegate under UISplitViewControllerDelegate and included the following function:
func splitViewController(_ svc: UISplitViewController, topColumnForCollapsingToProposedTopColumn proposedTopColumn: UISplitViewController.Column) -> UISplitViewController.Column {
return .primary
}
The collapseSecondary function didn't work apparently because there is a bug where "Interface Builder doesn’t allow creating a classic style UISplitViewController. (65966010) (FB8107534)", so I get the error message "Skipping delegate callback, splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:. Unsupported for UISplitViewController style DoubleColumn".
Thanks!
Returning .primary should solve your issue.
#available(iOS 14.0, *)
public func splitViewController(_ svc: UISplitViewController, topColumnForCollapsingToProposedTopColumn proposedTopColumn: UISplitViewController.Column) -> UISplitViewController.Column {
return .primary
}
[From Documentation]
Asks the delegate to provide the column to display after the split view interface collapses.
When the split view controller transitions from a horizontally regular to a horizontally compact size class, it calls this method and asks you for the column to display when that transition is complete. Use this method to customize the view controller you’re collapsing to.

iOS Top Navigation Bar

Which is the best approach in order to create a Top Navigation Bar like this one on Instagram?
I have a Tab Bar controller which has 5 views but I do not understand how to create another navigation bar inside one of this views. Should I create two labels and connect each of them with another view or is there something better to achieve this?
Your approach is good, would be nice to see some screenshots or code to fully understand it.
If you want to put navigation bar inside those tab bar views you need to put a container view that connects to a navigation controller.
I created a simple project to show you how I achieved this.
Using a scrollview to contain another two view containers embedded with some navigation controllers:
The view containers allows you to embed any type of view / controller:
Apple docs has a nice reference of how to do this, in your case you just need to change ViewController to NavigationController.
And if you don't mind to use third party code, well there are plenty of options for you to choose:
PolioPager
import PolioPager
class ViewController: MainContainerViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func tabItems()-> [TabItem] {
return [TabItem(title: "One"),TabItem(title: "Two")]
}
override func viewControllers()-> [UIViewController]
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController1 = storyboard.instantiateViewController(withIdentifier: "cont1")
let viewController2 = storyboard.instantiateViewController(withIdentifier: "cont2")
return [viewController1, viewController2]
}
}
In above example you can use PolioPager to instantiate 2 different navigation view controllers, identified by storyboard identifier cont1 cont2 for example.
Other trip party libraries:
https://github.com/XuYanci/GLViewPagerController
https://github.com/rechsteiner/Parchment

iOS: Default status bar style with UIViewControllerBasedStatusBarAppearance YES

Is there a way how to set the default status bar style while keeping the UIViewControllerBasedStatusBarAppearance enabled?
Here is the problem, I'm dealing with:
Nearly the whole app needs to be using UIStatusBarStyle.LightContent as the navigation bar has a dark background. Originally, UIViewControllerBasedStatusBarAppearance was disabled and the following was set in in Info.plist for while text status status bar:
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>
This worked just fine until I found out that this .LightContent status bar is shown even for some of the share extensions, like Facebook Messenger, causing it to be unreadable:
This could be solved by using UIViewControllerBasedStatusBarAppearance, but then I would need to add the following method to all of view controllers which I want to avoid as the app is quite large.
Moreover, for the one screen in the app that has light nav bar background, I was switching to dark nav bar using UIApplication.sharedApplication().setStatusBarStyle() but this method in deprecated in iOS 9.
Any ideas how to solve this? Swizzling?
Solution
The easiest and cleanest way how to achieve that is to add the following line in AppDelegate's application:willFinishLaunchingWithOptions method:
UINavigationBar.appearance().barStyle = .Black
This will make .LightContent as the default status bar style thorough the app as long as your app uses UINavigationController.
Don't forget to keep the following setting in app's Info.plist if want to use .LightContent status bar style during launch for splash screen:
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>
TL;DR
My current setup, which is very similar to many other apps, uses UITabBarController as the top most controller with UINavigationController stack for each tab.
UINavigationController takes care of the status bar style (as it should) and do not call preferredStatusBarStyle() on its child view controllers. Therefore, implementing the subclassing solution proposed by par does not work in my case.
Further subclassing the custom subclass of UINavigationController I'm using would not be a clean solution either.
Now, since the app has UIViewControllerBasedStatusBarAppearance enabled and correct status bar style everywhere in the app itself, SFSafariViewController and share extension like Messages, Mail, etc use the correct (.Default) status bar style too.
The only exception where the correct status bar style is not used is the Facebook Messenger's share extension mentioned in the question. However, it seems to be a bug in the extension itself as all apps I have tried that use .LightContent status bar style (like Twitter, for example) have the same issue - presented FB Messenger share extension from the app has a status bar with white color text.
A solution I use quite frequently is to create a base view controller class that all view controllers in my app derive from. This has the advantage of allowing use of the view-controller-based status bar style-setting functionality with a default (light or dark) style, which can then be overridden on a per-view-controller basis as necessary.
A base view controller is also really handy once you start getting into trait-collection based changes, custom transition animations that you want for most view controllers, a central point for analytics tracking, and other useful things.
Yes, you have to go through your potentially large source base and change all your UIViewControllers into BaseViewControllers, but this is often as easy as a global search-and-replace.
Here's what the BaseViewController looks like with status-bar related methods:
class BaseViewController: UIViewController {
var statusBarHidden: Bool = false { didSet { setNeedsStatusBarAppearanceUpdate() } }
var statusBarStyle: UIStatusBarStyle = .lightContent { didSet { setNeedsStatusBarAppearanceUpdate() } }
var statusBarUpdateAnimation: UIStatusBarAnimation = .fade { didSet { setNeedsStatusBarAppearanceUpdate() } }
override var preferredStatusBarStyle: UIStatusBarStyle { return statusBarStyle }
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { return statusBarUpdateAnimation }
override var prefersStatusBarHidden: Bool { return statusBarHidden }
}
For all view controllers that use the default light style, you don't need to do anything special:
class ViewController: BaseViewController { }
In the cases where you need a dark status bar, do:
class DarkStatusBarViewController: BaseViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
statusBarStyle = .default
}
}
Note also that you could rename the DarkStatusBarViewController above to DarkStatusBarBaseViewController and derive from it instead of BaseViewController when you need a dark status bar. Then you don't need to duplicate the status bar code in every view controller that needs it and you maintain a nice linear relationship for all your BaseViewController functionality.

Set all back button titles in UINavigationController

I'm trying to be clever about setting all title properties of the the "Back" buttons in a UINavigationController so that I don't have to do self.navigationController.navigationBar.backButtonItem.title = "Back" everywhere or subclass a UINavigationController and set it everywhere, so I've created this extension:
extension UINavigationItem {
open var backBarButtonItem: UIBarButtonItem? {
get {
return self.backBarButtonItem
}
set {
newValue?.title = "Back"
backBarButtonItem = newValue?
}
}
}
But it says 'backBarButtonItem' used within its own type.
Has anybody done this before or can think of a way to make it work?
You are getting this error because you cannot create a variable with the name which is similar to those variables which are defined in the SDK.
You can't override the existing functionality
Like in your case you are naming it as backBarButtonTitle which is defined as open var backBarButtonItem: UIBarButtonItem? in UINavigationBar class of UIKit
As it is mentioned in doc of Apple
Extensions can add new functionality to a type, but they cannot
override existing functionality.
Please follow this Screen shot Image then run your project . I think you can solved your problem easily :)

Resources