I have an app with multiple UISplitViewControllers that each have their own MasterView and DetailView. I noticed, however, that when I launch right into one of the SplitViews, I get presented with the DetailView, and have to navigate back to the MasterView first. I would like to change that, and found out that this works with the preferredDisplayMode, but setting it somehow causes problems.
I've create a subclass of UISplitViewController for all three SplitViews, and tried overriding the preferredDisplayMode like this:
import UIKit
internal class SplitViewController : UISplitViewController {
#IBAction internal func unwindToSplitView(segue: UIStoryboardSegue)
override let preferredDisplayMode = UISplitViewControllerDisplayMode.PrimaryOverlay
}
However, I get the following error:
Cannot override with a stored property 'preferredDisplayMode'
What am I doing wrong? Thanks.
You should instead override the func viewDidLoad() and set preferredDisplayMode to the value that you want in there. Like so:
override func viewDidLoad() {
super.viewDidLoad()
preferredDisplayMode = .PrimaryOverlay // Or UISplitViewControllerDisplayMode.PrimaryOverlay if you prefer (both are equivalent)
}
Related
I am trying to have a switch appear in the off state once a view loads. Not always, but only if a boolean value that I created("switchBool") is false.
I've tried using the two ways on the apple documentation website. The two ways are shown in my code example. One is commented out.
import UIKit
class ViewController: UIViewController {
var switchBool = false
#IBAction func switchControl(_ sender: UISwitch) {
//sender.isOn = switchBool
sender.setOn(switchBool, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
The app is building and running without errors. However, I want the switch to be in the off state if "false" is assigned to the bool "switchBool", which in my example it is, but no matter what I try the switch always appears in the on state when the view loads up.
You need to create an IBOutlet for your switch(using the assistant editor button and storyboard). Then, you can just set the switch to be off in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
yourSwitch.setOn(switchBool, animated: true)
}
In the storyboard you can set it to be off in the Attribute inspector
I'm having issues with iOS 11's large titles when using a Table View Controller.
I have set prefersLargeTitles to true in the viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
}
When running my app the title appears as if prefersLargeTitles is set to small, but if I then scroll down, the large title appears.
I have 2 UIViewControllers, and they both display the large title correctly, bar the UITableViewController.
I have tried different combinations of setting prefersLargeTitles to true in the code and within the storyboard, where you can set Large Title to Automatic, Always or Never.
I can only find one other question on Stack Overflow which refers to this exact issue, but none of the answers seem to solve it.
Has anyone else who has run into this issue been able to solve it?
After setting prefersLargeTitles, you can trigger the large titles to show by calling setContentOffset on your UITableView.
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
self.tableView.setContentOffset(CGPoint(x: 0, y: -1), animated: true)
}
Perhaps you are reloading the tableView prior to viewDidLoad? I observed the same behavior today. In my case, I had a didSet on a variable that was set before viewDidLoad that called tableView.reloadData.
I was able to fix this by adding a guard so the reloadData only happened when the variable wasn't previously nil, meaning the first time.
Here's my calling class for reference...
/// Master Controller in UISplitViewController
class MyMasterViewController: UIViewController {
private let controller = MyTableViewController()
override func viewDidLoad() {
super.viewDidLoad()
controller.instanceVariable = data
showDetailViewController(navigationController, sender: self)
}
...
private func reloadDetail() {
controller.instanceVariable = newData
}
}
And here's the fix - added the guard statement below...
/// Detail Controller in UISplitViewController
class MyTableViewController: UITableViewController {
var instanceVariable: MyData! {
didSet {
guard oldValue != nil else { return }
tableView.reloadData()
}
}
// UITableViewController Methods...
}
Hope this helps! If my example doesn't make sense, I'd suggest commenting out all references to tableView.reloadData and then only re-adding when sure they aren't getting called until after the tableView loads initially.
You need to enable large titles on the navigation controller. It is tricky to find, so please see the screenshot below.
If your table view controller seques on to other detail view controllers then you should set large title to Never in the storyboard for those other view controllers.
DO NOT MESS with prefersLargeTitles in code like this:
self.navigationController?.navigationBar.prefersLargeTitles = false
I have created a subclass of UIViewController called LoginController. I have a LoginController.xib file that contains a view controller with a few elements in it. I have set the class of the view controller to LoginController and I have set my Main Interface to LoginController. Upon launching my app, I see my splash screen, followed by a pure black screen. My LoginController class just has the default code like so
class LoginController: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
}
}
I just created a new xcode project. My results are totally normal. So I guess it's because you are not setting the LoginController.xib as the initial xib to load?
This question already has answers here:
Removing the title text of an iOS UIBarButtonItem
(39 answers)
Closed 7 years ago.
I do not want to see the "Back" text that is displayed automatically when going to a view controller with no Title. I would like this to happen everywhere on my application, is there a few lines of code I can put in the app delegate to make this possible?
I have tried a few approaches from SO before posting this question and have found no success. I want to this in Swift, not Obj-c.
I tried this with no success; it ran fine, but the back text was still displayed in the next view controller.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let backItem = UIBarButtonItem()
backItem.title = "Something Else"
navigationItem.backBarButtonItem = backItem // This will show in the next view controller being pushed
}
I would like to put this in app delegate so it would happen throughout my application rather than having to put that into every single swift file in my project. Anybody have any ideas?
There are several different ways to do this. I’ll present a solution that gives you a bit more flexability as you’ll want to display a button some times, hide other times, and customize at other times. This example assumes you are using Navigation Controller based UI, but can be adapted to other types.
Whenever I create an app I like to create my own UIViewController class and have all of my UIViewControllers inherit from this single view controller. If I want to customize something for all my UIViewControllers I can do this at my new super class level since all the other views inherit from that.
In my sample code below I create my custom UIViewController called MasterViewController. I have all of my UIViewControllers inherit from it: ViewController, ViewController2 & ViewController3. Read the notes in the code below to understand what’s going on.
NOTE: Make sure you check for the case where the UIViewController should not have the back button as this would be the case where there is nothing to return to. You can add this code to MasterViewController but I did not for this sample.
//=========================================================
//MasterViewController
//=========================================================
// Master View Controller that all UIViewControllers inherit from.
import UIKit
class MasterViewController: UIViewController {
// This will be title if the user doesn't change it in a subclass.
var backButtonTitle = "Default Back Title"
override func viewDidLoad() {
// This resets the default back button to always be shown unless the user calls createCustomBackButtonWithTitle from child class.
self.navigationItem.hidesBackButton = false
super.viewDidLoad()
}
func createCustomBackButtonWithTitle(customTitle: String) {
// Hide the default back button.
self.navigationItem.hidesBackButton = true
// Programmatically create custom back button.
let backButton = UIBarButtonItem(title: customTitle, style: UIBarButtonItemStyle.Plain, target: self, action: "goBack:")
self.navigationItem.leftBarButtonItem = backButton
}
#IBAction func goBack(sender: UIButton!) {
navigationController?.popViewControllerAnimated(true)
}
}
//=========================================================
//ViewController
//=========================================================
// This view controller customizes the back name and inherits the default "go back" behavior from MasterViewController.
import UIKit
class ViewController: MasterViewController {
override func viewDidLoad() {
super.viewDidLoad()
createCustomBackButtonWithTitle("ButtonName")
}
}
//=========================================================
//ViewController2
//=========================================================
// This view controller customizes the back name but overrides the default "goBack" behavior from MasterViewController to do something different
import UIKit
class ViewController2: MasterViewController {
override func viewDidLoad() {
super.viewDidLoad()
createCustomBackButtonWithTitle("ButtonName 2")
}
// Override the back button behavior from super class because we don't want default "back" behavior.
#IBAction override func goBack(sender: UIButton!) {
// Costume Code here
}
}
//=========================================================
//ViewController3
//=========================================================
// This view controller inherits from MasterViewController, but gets the default "back" behavior from a normal
// UINavigationController since it never calls createCustomBackButtonWithTitle to change the behavior
import UIKit
class ViewController3: MasterViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
Most people wind up doing something like #xdeleon suggested, but I highly recommend not doing that, if for no other reason that that you'll break the built-in swipe behavior to navigate backwards in the view controller stack. There are a couple answers on SO that tell you how to restore this back-swipe functionality, but as someone who's had to fix this in an existing app with 200+ view controllers, I'd like to save you a lot of pain.
If you don't mind working in storyboards, then make sure that each of your view controllers' navigation item's back button text is an empty string. However, since that's a lot to keep track of, it's marginally easier to do in code, and the only way I've found works perfectly is to create a cachedTitle instance variable on your view controller, and implement the view controller's viewWillAppear() and viewWillDisappear() methods like so:
override func viewWillAppear(animated: Bool) {
// Reset the view controller's title only if it doesn't already have one.
if (navigationItem.title == nil || navigationItem.title == "") && navigationItem.titleView == nil {
navigationItem.title = cachedTitle
}
}
override func viewWillDisappear(animated: Bool) {
if navigationItem.title != nil { // not 'if let'!
cachedTitle = navigationItem.title
navigationItem.title == ""
}
}
You can either put these methods in a UIViewController subclass that all of your view controllers extend, or, better yet, put these in custom methods in a category on UIViewController, and just call them in your view controllers' viewWillAppear() and viewWillDisappear() calls.
If you want to use something other than the back button's '<' symbol, don't add a custom back or left button; just set a custom back indicator image and mask on the navigation bar.
I tried to implement UISplitViewController by following steps in 《iOS 8 by tutorial》。
The ducoment said if I return yes in splitViewController:collapseSecondaryViewController:ontoPrimaryViewController: method, the split view controller will shows only the content from its primary view controller.
But in my project, the split view controller shows both primary and secondary view controller in collapsed interface no matter I return true of false in this method. And the most wired thing is that this method is only called once when the app begins running.
Here is my custom SplitViewController which subclasses to UISplitViewController:
import UIKit
class SplitViewController: UISplitViewController, UISplitViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
// MARK:- UISplitViewControllerDelegate
func splitViewController(splitController: UISplitViewController, collapseSecondaryViewController secondaryViewController: UIViewController, ontoPrimaryViewController primaryViewController: UIViewController) -> Bool {
// We don't want anything to happen. Say we've dealt with it
return true
}
}
I found I needed to add "self.preferredDisplayMode=.primaryOverlay" to my ViewDidLoad.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.delegate = self
self.preferredDisplayMode = .primaryOverlay
}
The preferredDisplayMode has some other options to customize the initial behavior you can toy with to get your preferred look and feel.
Note this is for iPhone, Compact Width. Test also on an iPad as it behaves differently (landscape vs. portrait).