How to create UI programmatically in Xcode 11? [duplicate] - swift

This question already has an answer here:
Xcode 11 & iOS13, using UIKIT can't change background colour of UIViewController
(1 answer)
Closed 3 years ago.
Update: Finally got it working. See below code for SceneDelegate.swift
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window?.windowScene = windowScene
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
}
In contrast to the tutorials and the articles online, I was not able to create a working UI with the latest Xcode. This was also the case with Xcode 10, but I did not care at the time.
My steps are as follows:
Delete Main.storyboard
Delete Main from project settings
Write basic UIWindow code:
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
let mainVC = ViewController()
window?.rootViewController = mainVC
return true
}
At this point I am getting an error saying cannot find the storyboard named Main in the bundle. If I go ahead and clear the entry from Info.plist, this time it complains that there are not enough characters in the storyboard name.
Any ideas?

These are the steps I followed to get UI working programmatically in Xcode 11.
1) Created a new project as usual
2) I deleted the Main.Storyboard file from the project navigator on the left-hand side.
3) In General tab I removed the Main option from the Main Interface drop-down list
4) Next, go to your Info.plist file and completely remove the Storyboard Name from the list
5) Now if you run the app, it should not crash and it should show you a black screen.
6) Next, go to the SceneDelegate.swift file and initialise your UIWindow object as usual. There is a catch here and is new in Xcode 11. You also set the windowScene property of the window object, or else it won't work.
7) Once that's all done, Go to your ViewController class and set the background colour of the view to some colour and you should be all good.
You can also use templates:
Xcode 11 Programmatic UIKit templates
Hope this helped you!!!

This works for me on Xcode 10.3 Swift 5, and Xcode 11 Swift 5.1.
On your new Xcode project, on the Info.plist file, delete the launch screen and main interface file name entries, (don't leave the entry there with an empty string)
Remove the #UIApplicationMain attribute from your AppDelegate class.
Also, this link may help you, you can find info on UIApplicationMain
https://docs.swift.org/swift-book/ReferenceManual/Attributes.html
See code below:
// Created by Juan Miguel Pallares Numa on 9/16/19.
// Copyright © 2019 Juan Miguel Pallares Numa. All rights reserved.
import UIKit
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var myViewController = ViewController()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = myViewController
window?.makeKeyAndVisible()
return true
}
}
import UIKit
class ViewController: UIViewController {
convenience init() {
self.init(nibName: nil, bundle: .main)
view = UIView(frame: UIScreen.main.bounds)
view.backgroundColor = UIColor(
displayP3Red: 0.0, green: 0.7, blue: 0.0, alpha: 1.0)
}
}
// call this file "main.swift"
import Foundation
import UIKit
UIApplicationMain(
CommandLine.argc, CommandLine.unsafeArgv, nil, NSStringFromClass(AppDelegate.self))

Related

How do I rename the initial programmatic ViewController class for my iOS app without involving storyboards

I have been looking for a while and cannot find an answer to this question that does not involve storyboards.
It's quite simple. When you build a new iOS app in Xcode you are given the ViewController.swift file that contains the initial ViewController() class for the app. This view controller is the main view for the app.
If I rename the class to say ViewControllerTest(), when I build and run the app now only loads a blank screen since it cannot find the ViewController class as I renamed it.
How do I set the project in Xcode to use the new ViewControllerTest() class as the initial view controller?
Thanks
If you want to disregard using the storyboard entirely, which I prefer because then all code is in one place, you need to add the following in your AppDelegate or SceneDelegate depending on what version Xcode you are creating your project with.
Xcode 10 and earlier
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let window = UIWindow(frame: UIScreen.main.bounds)
// Or whatever you want to name your view controller class
window.rootViewController = ViewController()
window.makeKeyAndVisible()
self.window = window // retain instance
return true
}
}
for Xcode 10 and earlier you should also change the value of the UIMainStoryboardFile property to an empty string in your Info.plist.
Xcode 11 and later
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = scene as? UIWindowScene else { return }
let window = UIWindow(windowScene: windowScene)
// Or whatever you want to name your view controller class
window.rootViewController = ViewController()
window.makeKeyAndVisible()
self.window = window // retain instance
}
}
for Xcode 11 projects and newer, you should populate the Application Scene Manifest property as shown here:

iOS 13/Facebook SDK error "Unable to find a valid UIWindow"

(To forestall well-intended suggestions, yes I posted this question last week on Facebook's developer forum. No responses yet.)
TL;DR
Facebook SDK 5.8 complains at startup FBSDKLog: Unable to find a valid UIWindow.
The Main Story
In a from-scratch, one-view Xcode 11/iOS 13 project, there is no longer a default UIWindow member associated with the application. (The window per se is still around; you can see it, contained in a UIWindowScene, using the View Hierarchy Debugger in Xcode, or the Reveal app.)
FBSDK 5.8 does seem to be iOS-13-aware, and looks around for it. The relevant code is at line 498 of
https://github.com/facebook/facebook-ios-sdk/blob/master/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKInternalUtility.m.
Facebook's code iterates over the application's connectedScenes member, which for me is an empty set. How do I modify my code so that FBSDK finds the window?
Some Hacking
I tried adding the following to scene(_:willConnectTo:options:) but it seems to be too late — the FBSDKLog message has already appeared by then. (So I'm flailing...)
guard let s = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: s)
The following also failed, but it was just a shot in the dark:
guard let s = (scene as? UIWindowScene) else { return }
self.window = UIWindow(frame: s.coordinateSpace.bounds)
self.window?.windowScene = s
self.window?.rootViewController = ViewController(nibName: nil, bundle: nil)
self.window?.makeKeyAndVisible()
If you don't use the new behaviour and don't mind reverting to the old way, you can try the following
Delete Application Scene Manifest key from Info.plist
Delete SceneDelegate.swift
Add var window: UIWindow?to AppDelegate.swift
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow? // <-- Here
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// window!.makeKeyAndVisible()
return true
}
}
Add window variable to AppDelegate class
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
Assign UIWindow inside didFinishLaunchingWithOptions
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
if let window = window {
window.makeKeyAndVisible()
self.window = window
}
ApplicationDelegate.shared.application(
application,
didFinishLaunchingWithOptions: launchOptions
)
return true
}

How get rootViewController with iPadOS multi window (SceneDelegate)?

I am using Xcode 11 (beta3) and building an app for iOS 13. In my project I created the delegate methods for UIWindowSceneDelegate declaring it in Info.plist.
Now I'm able to create multiple windows (and UIScene).
How can I access the rootViewController now I've not anymore a single window? I need it to get some reference to objects and bounds it holds.
In my AppDelegate window is nil, and in my ViewController (child view controller) instance I tried using self.view.window.rootViewController but I found out that viewDidLoad() is too soon (I think) and the window is still nil, works in viewDidAppear(), but I don't need to make this process every time the view controller appears.
What's the best practice with this new way to handle application scenes?
Here is my AppDelegate:
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
return true
}
func application(_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
My SceneDelegate:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// yes it's empty, I'm using storyboard
}
Now you have more than one rootViewController, one for each scene.
First, you have to answer which one you need at the moment of usage.
Probably you want to get one of the rootViewController of the currently active scene then you can use this:
var rootVC:UIViewController? = nil
if #available(iOS 13.0, *) {
for scene in UIApplication.shared.connectedScenes {
if scene.activationState == .foregroundActive {
rootVC = ((scene as? UIWindowScene)!.delegate as! UIWindowSceneDelegate).window!!.rootViewController
break
}
}
} else {
// Fallback on earlier versions
}
You can access connected scenes using:
UIApplication.shared.connectedScenes
As per Apple documentation:
Connected scenes are those that are in memory and potentially doing active work. A connected scene may be in the foreground or background, and it may be onscreen or offscreen.
Then you can iterate through them and try to get UIWindowScene from there.
guard let windowScene = (scene as? UIWindowScene) else { return }
print(windowScene.windows) // This will print windows associated with the scene.
On the other hand, from the view controller you can access the window through the view:
self.view.window

How to Convert React Native Objective C Project into Swift

I'm developing an iOS Project which includes MongoDB Stitch and React-Native. MongoDB API is written in Swift and React Native iOS Structure is based on Objective-C. So I decided to convert Objective-C Project into Swift so that I can work with the Swift API of MongoDB and communicate with React Native via RCTBridge. So I converted the project into Swift but when I compile it in Xcode it shows a blank screen. I have created AppDelegate.swift and ProjectName-Bridging-Header.h files.
I'm using React Native Version 0.59.5
I have used this code in AppDelegate.swift file
import Foundation
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var bridge: RCTBridge!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let jsCodeLocation: URL
jsCodeLocation = RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index.ios", fallbackResource:nil)
let rootView = RCTRootView(bundleURL: jsCodeLocation, moduleName: "REPLACE_WITH_YOUR_PROJECT_NAME", initialProperties: nil, launchOptions: launchOptions)
let rootViewController = UIViewController()
rootViewController.view = rootView
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = rootViewController
self.window?.makeKeyAndVisible()
return true
}
}

type of 'window' has different optionality than required by protocol 'uiapplicationdelegate' after XCode update to 6.3

I have this code var window = UIWindow() in my AppDelegate. My app is working fine before. After I updated my XCode to 6.3, I can no longer run my iOS app in simulator as I am getting the error
type of 'window' has different optionality than required by protocol
'uiapplicationdelegate'
Thanks for all your contributions. I am not really sure about the reason why suddenly my code window declaration is no longer working. To fix it, I used the answer from here: https://stackoverflow.com/a/25482567/2445717
I revert the declarion of window to the default: var window: UIWindow?
and then used the code below for didFinishLaunchingWithOptions
window = UIWindow(frame: UIScreen.mainScreen().bounds)
if let window = window {
window.backgroundColor = UIColor.whiteColor()
window.rootViewController = ViewController()
window.makeKeyAndVisible()
}
In Swift 2, AppDelegate have:
var window: UIWindow?
instead of
var window: UIWindow
because it should be nil
You can use a lazy var to make code simply
lazy var window: UIWindow? = {
let win = UIWindow(frame: UIScreen.mainScreen().bounds)
win.backgroundColor = UIColor.whiteColor()
win.rootViewController = UINavigationController(rootViewController: self.authViewController)
return win
}()
If you cmd-click on the word UIApplicationDelegate in the class definition of your code, you will open the protocol definition. I suspect you are using this call:
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) {...}
and that may have changed in Swift 1.2, but does not seem to be widely documented. If you wrote instead
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow) {...}
then you would get the error message you report.
This particular problem is not fixed up by the automated program Daniel Nagy mentions - I ran into a similar issue.
If you have provided that optional function, then just add a ? after the UIWindow in the function definition.