UISplitViewController in Swift navigation from DetailView - objective-c

I'm trying to create a MasterDetail Application in Swift and it's running well on iOS8 Simulator. However, when I tried it on my iOS 7.1 iPad I got this error:
**Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '- [UISplitViewController displayModeButtonItem]: unrecognized selector sent to instance**
This is in my AppDelegate.swift file(generated by Xcode, I didn't add anything):
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let splitViewController = self.window!.rootViewController as UISplitViewController
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as UINavigationController
navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem()
splitViewController.delegate = self
let masterNavigationController = splitViewController.viewControllers[0] as UINavigationController
let controller = masterNavigationController.topViewController as MasterViewController
controller.managedObjectContext = self.managedObjectContext
return true
}
The problem comes from this line where the left button is created:
navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem()
When I remove this line, it runs on iOS 7 but displays only the DetailView. When I swipe from the left edge the MasterView doesn't appear(on iOS 8 simulator it does) and basically there is no navigation to the MasterView
Had anyone have the same problem?
Thank you!

You can still use deprecated callback function in UISplitViewControllerDelegate to add and remove UIBarButtonItem to your detail view for IOS7 platform. Implement as below in your UISplitViewControllerDelegate
func splitViewController(svc: UISplitViewController, willHideViewController aViewController: UIViewController, withBarButtonItem barButtonItem: UIBarButtonItem, forPopoverController pc: UIPopoverController) {
if !self.respondsToSelector(Selector("displayModeButtonItem")) {
let navigationController = self.viewControllers.last as! UINavigationController
let detailViewController: UIViewController? = navigationController.viewControllers?.last as? UIViewController
barButtonItem.image = UIImage(named: "IC_BackArrow")
detailViewController?.navigationItem.leftBarButtonItem = barButtonItem
} else {
// This callback function is depreciated in IOS8. We use displayModeButtonItem.
}
}
func splitViewController(svc: UISplitViewController, willShowViewController aViewController: UIViewController, invalidatingBarButtonItem barButtonItem: UIBarButtonItem) {
if !self.respondsToSelector(Selector("displayModeButtonItem")) {
let navigationController = self.viewControllers.last as! UINavigationController
let detailViewController: UIViewController? = navigationController.viewControllers?.last as? UIViewController
detailViewController?.navigationItem.leftBarButtonItem = nil
} else {
// This callback function is depreciated in IOS8. We use displayModeButtonItem.
}
}

I had the same issue. displayModeButtonItem is not supported by IOS versions prior to IOS 8. I suspect that you are compiling against IOS 8 but when deploying you are deploying onto an Ipad app with IOS 7.1 installed. I have remedied this situation in my situation (because I can) by installing IOS 8 on the target Ipad. It then worked without issue.

Related

Change navigation root view programmatically objective c

I want to change the navigation's root view controller programmatically using objective c. I am using a storyboard. I tried to change it in-app delegate but didn't work, there's something new introduced called scene delegate where I believe I can change the root from there, but I found no resources for that matter. Can anyone help?
Use below code:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = MyRootViewController()
self.window = window
window.makeKeyAndVisible()
}
}
Reference: https://www.andrewcbancroft.com/blog/ios-development/ui-work/accessing-root-view-controller-ios13-scenedelegate/
And: How set rootViewController in Scene Delegate iOS 13

Segue between UIViewControllers without Storyboard OR XIB

I have a nifty project I downloaded from GitHub (here) and I am playing around with it. The project has no storyboard or xibs whatsoever, and only one viewController, which is defined with just a viewController.h file and a viewController.m file.
Perhaps a noob question, but can I have viewController1.h/m programmatically segue to viewController2.h/m without using ANY xibs or storyboards? I found a lot of code on SO and elsewhere allowing one to segue programmatically from one view to another within a Storyboard, from one xib to another or from a scoreboard to a xib (though not the opposite) but nothing on how to segue from one totally code-based vc to another. All the code I found requires that you define the view in terms of the bundle location of the storyboard or xib file, but I want to use neither.
Note: I accepted the answer I did because of its ingenuity/interesting-ness, but for the sake of simplicity I personally ended up opting with this answer to the same question (mine was a duplicate it appears): iOS: present view controller programmaticallly
You can use [viewController presentViewController:anotherController animated:YES completion:nil]; to present the view controller modally.
Another alternative is to use a UINavigationController and do [viewController.navigationController pushViewController:anotherController animated:YES];
The second method will only work if viewController is in the stack of a navigationController
Here is my Context class which changes view controllers. It works with either your own view classes or storyboard view classes.
Specific to your question look at the open function. If there is no root controller when I call open, I assign it as the root view controller. Otherwise I present it from the root view controller.
import Foundation
import UIKit
private let _StoryBoard = UIStoryboard(name: "Main", bundle: nil)
private let _RootWindow = UIWindow(frame: UIScreen.mainScreen().bounds)
public var ROOT_VIEW_CONTROLLER:UIViewController = C_RootViewController()
//abstract base of context classes
class Context:NSObject
{
class var STORYBOARD:UIStoryboard
{
return _StoryBoard
}
class var ROOTWINDOW:UIWindow
{
return _RootWindow
}
var _currentController:Controller!
class func reassignRootViewController(controller:UIViewController)
{
Context.ROOTWINDOW.rootViewController = controller
ROOT_VIEW_CONTROLLER = controller
}
func initController(controllerName:String)->Controller
{
return Context.STORYBOARD.instantiateViewControllerWithIdentifier(controllerName) as Controller
}
func initControllerFromStoryboard(storyboardName:String,controllerName:String)->Controller
{
var storyboard:UIStoryboard = UIStoryboard(name: storyboardName, bundle: nil)
return storyboard.instantiateViewControllerWithIdentifier(controllerName) as Controller
}
func open(controller:UIViewController)
{
if(Context.ROOTWINDOW.rootViewController == nil)
{
Context.ROOTWINDOW.rootViewController = ROOT_VIEW_CONTROLLER
Context.ROOTWINDOW.makeKeyAndVisible()
}
ROOT_VIEW_CONTROLLER.presentViewController(controller, animated: true, completion: {})
}
func close(controller:UIViewController)
{
ROOT_VIEW_CONTROLLER.dismissViewControllerAnimated(true, completion: nil)
}
}

Initializing another window using storyboard for OS X

I have created a Cocoa application in Xcode6 which uses storyboards. As a template, Xcode provides a window for the application. I want to add a second window to show when the program is first loaded. So basically, there will be two windows showing up.
I have put a window controller on Main.storyboard where the first window also resides. However, I couldn't find the way to show this second window when the program starts. Could you please help?
Thank you.
In your Storyboard, select your second Window Controller. In the identity inspector, specify a name for this window controller, e.g secondWindowController
Then, in your app delegate, set up a property for the window controller:
#property NSWindowController *myController;
In your applicationDidFinishLaunching: method implementation, create a reference to the Storyboard. This way you get access your window controller from the storyboard.
After that, the only thing left to do is to display the window by sending your window controller the showWindow: method.
#import "AppDelegate.h"
#interface AppDelegate ()
#end
#implementation AppDelegate
#synthesize myController;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSStoryboard *storyBoard = [NSStoryboard storyboardWithName:#"Main" bundle:nil]; // get a reference to the storyboard
myController = [storyBoard instantiateControllerWithIdentifier:#"secondWindowController"]; // instantiate your window controller
[myController showWindow:self]; // show the window
}
#end
Swift 3 version:
var myWindowController: NSWindowController!
override init() {
super.init()
let mainStoryboard = NSStoryboard.init(name: "Main", bundle: nil)
myWindowController = mainStoryboard.instantiateController(withIdentifier: "myWindowControllerStoryboardIdentifier") as! NSWindowController
myWindowController.showWindow(self)
}
Make sure you define myWindowController outside the function or else the window won't show up.
It's actually better to do this in init() (of AppDelegate) as you may need it earlier on.
Swift 5:
The project setup in XCode 13 has entirely changed. There is no longer an example of how to connect to the storyboard from the AppDelegate. Instead, they are hardcoding a NSWindow. I still find Storyboards useful, hence the below should come in handy. Remember to name your WindowController in Storyboard as mainWindowController.
let mainStoryboard = NSStoryboard.init(name: NSStoryboard.Name("Main"), bundle: nil)
var monitorcontroler = mainStoryboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("mainWindowController")) as! NSWindowController
monitorcontroler.showWindow(self)
swift 4 version :
var monitorcontroler: NSWindowController!
override init() {
super.init()
let mainStoryboard = NSStoryboard.init(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil)
monitorcontroler = mainStoryboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "moniteur")) as! NSWindowController
monitorcontroler.showWindow(self)
}

How to re-open the Mac Desktop App after closing it by using X?

I have created an App for Mac Desktop, it is working, but Apple rejected it because when we run the App and close it by using "X", the we can not re-open it from the dock though the App icon is still there but it doesn't open the App again and the main issue for that I am struggling is that "If we close the App then in the menu bar there is no option to open it" other App which I have seen does that.
What should I do?
Here's an answer for Swift 3:
Conform to NSWindowDelegate in your View Controller class. Then hide the window instead of closing it by overriding the following method.
self.view.window?.delegate = self
func windowShouldClose(_ sender: Any) -> Bool {
NSApplication.shared().hide(self)
return false
}
Then unhide the application when the app icon is clicked in the dock.
func applicationDidBecomeActive(_ notification: Notification) {
NSApplication.shared().unhide(self)
}
To implement simple show/hide functionality for the red (x) button, make your App Delegate class the window delegate for your main window, as well.
Then add the following code to it:
- (BOOL)windowShouldClose:(id)sender {
[[NSApplication sharedApplication] hide:self];
return NO;
}
- (void)applicationDidBecomeActive:(NSNotification *)notification {
[[NSApplication sharedApplication] unhide:self];
}
my 2 cernts for swift 5/Xcode 10
note: You can call these methods also in ViewController (if useful) to prevent splitting code between NASWindow/NSView-Controllers.
in this case:
class ViewController: NSViewController, **NSWindowDelegate**
{
...
override func viewWillAppear() {
// we choose to delegate ourselves. must be done here, in viewDidLoad window is nil
let window = self.view.window
window!.delegate = self
}
...
no neeed to pass self.. : (as per other guys above.)
func windowShouldClose(_ sender: NSWindow) -> Bool {
NSApplication.shared.hide(nil)
return false // prevent closing.
}
func applicationDidBecomeActive(_ notification: Notification) {
NSApplication.shared.unhide(nil)
}

"Application tried to present modally an active controller"?

I just came across a crash showing a NSInvalidArgumentException with this message on an app which wasn't doing this before.
Application tried to present modally an active controller
UITabBarController: 0x83d7f00.
I have a UITabBarController which I create in the AppDelegate and give it the array of UIViewControllers.
One of them I want to present modally when tapped on it. I did that by implementing the delegate method
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
If that view controller is of the class of the one I want to present modally, I return NO and do
[tabBarController presentModalViewController:viewController animated:YES];
And now I'm getting that error, which seems to mean that you can't present modally a view controller that is active somewhere else (in the tabbar...)
I should say I'm on XCode 4.2 Developer Preview 7, so this is iOS 5 (I know about the NDA, but I think I'm not giving any forbidden details). I currently don't have an XCode installation to test if this crashes compiling against the iOS4 SDK, but I'm almost entirely sure it doesn't.
I only wanted to ask if anyone has experienced this issue or has any suggestion
Assume you have three view controllers instantiated like so:
UIViewController* vc1 = [[UIViewController alloc] init];
UIViewController* vc2 = [[UIViewController alloc] init];
UIViewController* vc3 = [[UIViewController alloc] init];
You have added them to a tab bar like this:
UITabBarController* tabBarController = [[UITabBarController alloc] init];
[tabBarController setViewControllers:[NSArray arrayWithObjects:vc1, vc2, vc3, nil]];
Now you are trying to do something like this:
[tabBarController presentModalViewController:vc3];
This will give you an error because that Tab Bar Controller has a death grip on the view controller that you gave it. You can either not add it to the array of view controllers on the tab bar, or you can not present it modally.
Apple expects you to treat their UI elements in a certain way. This is probably buried in the Human Interface Guidelines somewhere as a "don't do this because we aren't expecting you to ever want to do this".
I have the same problem. I try to present view controller just after dismissing.
[self dismissModalViewControllerAnimated:YES];
When I try to do it without animation it works perfectly so the problem is that controller is still alive. I think that the best solution is to use dismissViewControllerAnimated:completion: for iOS5
In my case i was trying to present the viewController (i have the reference of the viewController in the TabBarViewController) from different view controllers and it was crashing with the above message.
In that case to avoid presenting you can use
viewController.isBeingPresented
!viewController.isBeingPresented {
// Present your ViewController only if its not present to the user currently.
}
Might help someone.
The same problem error happened to me when I tried to present a child view controller instead of its UINavigationViewController parent
I had same problem.I solve it. You can try This code:
[tabBarController setSelectedIndex:1];
[self dismissModalViewControllerAnimated:YES];
For React Native Developer - Problem might not be in AppDelegate Or main.m if app has been successfully build and is running and will crash after splash or perhaps the error screen
Issue might be due to use of fonts/resources that is not available with xcode and not properly configured.. You can find out the error by commenting certain portion starting from App.js and drilling inside the navigation/screens and commenting the components till you find the component that is generating the error....
In my case the resource of fontFamily was making an issue which was used right after splash in walkthrough screen
<Text style={{fontFamily: Fonts.roboto}}>ABC</Text>
Here font roboto wasnot configured properly. Wasted entire days just debugging the error hope its helps you
In my case, I was presenting the rootViewController of an UINavigationController when I was supposed to present the UINavigationController itself.
Just remove
[tabBarController presentModalViewController:viewController animated:YES];
and keep
[self dismissModalViewControllerAnimated:YES];
Instead of using:
self.present(viewControllerToPresent: UIViewController, animated: Bool, completion: (() -> Void)?)
you can use:
self.navigationController?.pushViewController(viewController: UIViewController, animated: Bool)
This is my way which supporting multiple Windows(from a single APP) on the iPad and nested modal present.
import UIKit
///✅Use this public method
public func SheetViewController(ViewController:UIViewController) {
for i in returnAvailableViewControllers().shuffled() {
if i.presentedViewController == nil && !ViewController.isViewLoaded {i.present(ViewController, animated: true, completion: {})}
}
}
///Returns all possible ViewControllers
private func returnAvailableViewControllers() -> [UIViewController] {
let 场景 = UIApplication.shared.connectedScenes
var 存储VC : [UIViewController] = []
for i in 场景 {
if i.activationState == .foregroundActive {
//⭐️Set up “foregroundActive” to give the user more control
var 视图控制器 = (i.delegate as? UIWindowSceneDelegate)?.window??.rootViewController
if 视图控制器 != nil {
存储VC.append(视图控制器!)
}
var 结束没 = true
while 结束没 {
//🌟Enumerate all child ViewController
视图控制器 = 视图控制器?.presentedViewController
if 视图控制器 != nil {
存储VC.append(视图控制器!)
} else {
结束没.toggle()
}
}
}
}
return 存储VC
}