Has anyone gotten Swift to play nice enough with iOS 7 to handle multiple storyboards?
At the very least, has anyone gotten device suffixes to work properly? Whenever I rename Main.storyboard in the Master-Detail example project to Main~iPad and/or Main~iPhone, I seem to get load errors.
I think it has something to do with device suffixes, because the following will do what I want :
var window: UIWindow?
var storyboard: UIStoryboard?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
//device suffixes ~iPad and ~iPhone don't work right now, in fact, a lot of .storyboard stuff is borked
self.window = UIWindow() //window doesn't have an init with frame class, so we need to set that to the screen bounds in order to have touch
self.window!.frame = UIScreen.mainScreen().bounds
//Since I'm targeting iOS 7 and later, we can't use UISplitViewController everywhere (it becomes universal in iOS 8)
if UIDevice.currentDevice().userInterfaceIdiom == .Pad {
self.storyboard = UIStoryboard(name: "Main~iPad", bundle: nil)
self.window!.rootViewController = self.storyboard!.instantiateInitialViewController() as UIViewController
//iPad-specific initialization
let splitViewController = self.window!.rootViewController as UISplitViewController
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.endIndex-1] as UINavigationController
splitViewController.delegate = navigationController.topViewController as DetailViewController
}else{ //iPhone / iPod
self.storyboard = UIStoryboard(name: "Main~iPhone", bundle: nil)
self.window!.rootViewController = self.storyboard!.instantiateInitialViewController() as UINavigationController
//iPhones should just have a navigation controller as their root view
}
self.window!.makeKeyAndVisible() //got to manually key since we're initializing our window by hand
//at this point we have the iPad storyboard or the iPhone storyboard loaded
return true
}
I know that it looks like we're going to be moving away from device-specific tags, but answering this will be important for people that need to keep supporting iOS 7.
The device specific stuff and orientation is changing soon. Swift source compatibility for iOS 7 and Mavericks isn't guaranteed but binary compatibility is. Maybe you're running up against the limits of the compatibility.
Related
I recently started a new project with the newest Xcode and now the function
self.navigationController?.pushViewController to push a new controller is not working.
I've looked at similar answers and checked my own old projects where it is working, they mentioned the Navigation controller being nil but I thought I addressed that.
I think it has to do with the new SceneDelegate file that is part of how views are managed.
Here is the code of the regarding the first VC that pops up in the app in the SceneDelegate file.
let nav1 = UINavigationController()
let mainView = MainViewController(nibName: nil, bundle: nil) //ViewController = Name of your controller
nav1.viewControllers = [mainView]
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
// window.rootViewController = UIHostingController(rootView: contentView)
window.rootViewController = nav1
self.window = window
window.makeKeyAndVisible()
}
Remove SceneDelegate.swift file and it's delegate methods.
I had same issue with Xcode 11.1 with new project.
Remove SceneDelegate.swift file and also remove its delegate methods from AppDelegate.swift. This solution worked for me.
My open source code created with Xcode 11 and Swift 5.
https://github.com/gauravparvadiya/swift-news-reddit
Like they said above,
remove SceneDelegate.swift,
the sceneDelegate methods from the AppDelegate.swift
PLUS
remove these from the plist file:
UISceneConfigurations
UIWindowSceneSessionRoleApplication
key: UISceneConfigurationName
value: Default Configuration
key: UISceneDelegateClassName</key>
value: $(PRODUCT_MODULE_NAME).SceneDelegate
key: UISceneStoryboardFile
value: Main
If you fail to remove these, you will get a black view when you run your app. But, the error messages in the console should help guide you to delete the necessary items from the plist.
I have a query which is quite intriguing to me, it happens that I am learning Swift, it seems to me fantatisc the tools that xcode provides make the job a lot easier, but based on my experience in Android projects as a minimum it is recommended to use MVC to maintain the order of the project, same try to swift for which they recommended me to use xib files, it can also be used to work in groups. Until there is everything fantastic but when I tried to implement it I find it too complicated, for which I preferred to use storyboards but the use of libraries and other files make that when loading my storyboard file I delayed too much which is because it is loaded with enough ViewController. Added to this, using the NavigationController in storyboards is easy, it makes navigating a lot easier. My problem is how could I do this with xib files? In a moment I try to do it, but my ViewController loaded vertically and without the navigation bar and I have no idea how to develop it.
By code to load a ViewController is this way.
let Storyboard = UIStoryboard(name: "Main", bundle: nil)
let menuVC = Storyboard.instantiateViewController(withIdentifier: "MenuSelectedViewController") as! MenuSelectedViewController
self.navigationController?.pushViewController(menuVC, animated: true)
And by interface builder with segue
So how can I develop this navigation bar with xib files and my TabBarController ?
You just need UINavigationController(rootViewController: yourViewController)
Here is an example using this
let yourViewController = DiscoverViewController(nibName: "yourViewController", bundle: nil)
yourViewController.tabBarItem.image = UIImage(named: "imageName")
let navigationController = UINavigationController(rootViewController: yourViewController)
// TabBarController
let tabbarController = UITabBarController()
tabbarController.tabBar.tintColor = ThemeColor
tabbarController.tabBar.barTintColor = .white
tabbarController.viewControllers = [navigationController] //add your other controllers here as needed
// Make it root or what ever you want here
self.window?.rootViewController = tabbarController
self.window?.makeKeyAndVisible()
I am proudly new to iOS developing and I am trying to build my first app. I am doing a course on an online platform which does the following in the
AppDelegate -> application didFinishLaunchingWithOptions:
let navigationController = window?.rootViewController as! UINavigationController
let notebooksListViewController = navigationController.topViewController as! NotebooksListViewController
notebooksListViewController.dataController = dataController
This app has a Navigation controller which begins with an UIViewController.
I have 2 questions here, first is why this works, I mean, I am in AppDelegate, so the NotebooksListViewController (first view of the app) is not instantiated yet (I think), so why I am able to inject a variable in it?
On the other hand, the second question, is how can I do this in a different scene? I have a TabBarViewController as first scene, and the first tab is a UITableViewController and I want to inject the same way my dataController var, how can I accomplish this? I could not get to do it, neither understand it.
Thanks in advance.
It works, because of some Xcode magic:
In your Target Setting, General tab, the Main Interface entry specifies the name of the Storyboard to be loaded automatically when your app starts up:
In the storyboard, the Initial View Controller then will be instantiated. It seems like this is an UINavigationController.
Since this is done automatically, it just works - until you want to do something special :-)
If you want to start up with a different scene - maybe from a different view controller - you could just change either the Main Interface to another storyboard, the Initial View Controller (inside the storyboard) or both.
Or, you could just start up by yourself, by leaving the Main Interface empty and create your own view controller inside the app delegate (didFinishLaunchingWithOptions), something like
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if let tabVC = mainStoryboard.instantiateViewControllerWithIdentifier("TabCtrl") as? UITabBarController {
self.window?.rootViewController = tabVC
// Access the subcontrollers, or create them
// Initialize their values
// tabVC.viewControllers[0].data = ...
} else {
// Ooops
}
self.window?.makeKeyAndVisible()
Answer to your first question
as the method name is self explanatory didFinishLaunchingWithOptions means your application is didfinish with launching with options and its about to enter in foreground so here application need to set rootViewController so in this method controller you want to set as view controller is initiated thats why you can inject variable in it
answer to second question
let navigationController = window?.rootViewController as! UITabbarController
let VC = navigationController.childViewController
//Now Using VC you can access all you controller of tabbar controller
let notebooksListViewController = navigationController.topViewController as!
NotebooksListViewController
notebooksListViewController.dataController = dataController
now as shown above you can use VC to access you view controllers
but be careful here because VC return viewcontroller array so you need make checks for perticular VC you want to access
I'm implementing a drawer layout design in an app.
But my app starts with a small screen with an animated logo (simple HTML5 animation), then a login screen (g+ and Facebook), then the main screen where I'm implementing the MMDrawerController.
The question is in my AppDelegate:
window?.rootViewController = centerContainer
window?.makeKeyAndVisible()
So the app start in this screen. Is it possible to not make rootviewcontroller the center container and still using MMDrawerController?
I need to add MMDrawerController to my third viewcontroller in my app
But, in order to MMDrawerController to work, it requires to be the rootViewController
I allready tried to add to my first ViewController an Empty MMDrawerLayout but, then, the third controller no longer works
//global var
var centerContainer : MMDrawerController?
// then the appdelegate
let rootViewController = self.window!.rootViewController
let mainStoryBoard:UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let centerViewController = mainStoryBoard.instantiateViewControllerWithIdentifier("GaleriaPeliculas")
let leftViewController = mainStoryBoard.instantiateViewControllerWithIdentifier("LeftSideViewController")
let leftSideNav = UINavigationController(rootViewController: leftViewController)
let centerSideNav = UINavigationController(rootViewController: centerViewController)
//And here is the problem,
window?.rootViewController = centerContainer //how can it work without this line??
window?.makeKeyAndVisible()
Is it possible to not make rootviewcontroller the center container and still using MMDrawerController?
No. You need to keep MMDrawerController as the rootViewController if you still want to make use of the left/right drawers. It's no different than a UITabBarController in the sense that it contains multiple view controllers to be conditionally displayed.
I'm new to Digits for iOS. I got the authentication part working, and now I'm trying to integrate the sign-in with the rest of my app.
One thing that really puzzles me is that in Cannonball, the Initial View Controller is the main screen. However, when running Cannonball, the first screen I see is the Sign In screen.
I don't see any segue from the Navigation Controller to the Sign In. Also, I've looked at the code in the Theme Chooser View Controller, and don't see any reference to the Sign In's View Controller.
So, how does the Sign In screen actually get executed? Where is that logic happening? Thanks.
The answer is that the AppDelegate.swift file does the logic. The code below were copy/pasted from Cannonball's. I went ahead and added comments (some came with Cannonball).
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
// Developers: Welcome! Get started with Fabric.app.
let welcome = "Welcome to Cannonball! Please onboard with the Fabric Mac app. Check the instructions in the README file."
assert(NSBundle.mainBundle().objectForInfoDictionaryKey("Fabric") != nil, welcome)
// Register Crashlytics, Twitter, Digits and MoPub with Fabric.
//Fabric.with([Crashlytics(), Twitter(), Digits(), MoPub()])
Fabric.with([Digits()])
// Check for an existing Twitter or Digits session before presenting the sign in screen.
// Detect if the Digits session is nil
// if true, set the root view controller to the Sign In's view controller
if Twitter.sharedInstance().session() == nil && Digits.sharedInstance().session() == nil {
//Main corresponds to Main.storyboard
let storyboard = UIStoryboard(name: "Main", bundle: nil)
//Sign In's View Controller
let signInViewController: AnyObject! = storyboard.instantiateViewControllerWithIdentifier("SignInViewController")
//Set the UIWindow's ViewController to the SignIn's ViewController
window?.rootViewController = signInViewController as? UIViewController
}
return true
}