Xcode 13.3.1 - Swift 5
@DonMag Solved this, see @DonMag's comment
So I'm very new to this. I have no background in coding. I'm an automotive repair technician (fancy term for mechanic). This is my first attempt. I built a simple calculator with a UITabBarController. Every single thing I wanted the app to do, I googled and learned how to do it. The calculator loads up fine and everything seems to work. I even added a GADBannerView with Admob and the test ad works on both tabs.
So then I found a video by iOS Academy and thought wow an animated launch screen sounds cool.
import UIKit
class LaunchViewController: UIViewController {
private let imageView: UIImageView = {
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 270, height: 270))
imageView.image = UIImage(named: "launch")
return imageView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(imageView)
view.backgroundColor = .black
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
imageView.center = view.center
DispatchQueue.main.asyncAfter(deadline: .now()+0.5, execute: {
self.animate()
})
}
private func animate() {
UIView.animate(withDuration: 1, animations: {
let size = self.view.frame.width * 9
let diffX = size - self.view.frame.size.width
let diffY = self.view.frame.size.height - size
self.imageView.frame = CGRect(
x: -(diffX/2),
y: diffY/2,
width: size,
height: size
)
})
UIView.animate(withDuration: 1.5, animations: {
self.imageView.alpha = 0
}, completion: {done in
if done {
DispatchQueue.main.asyncAfter(deadline: .now()+0.5, execute: {
let viewController = ViewController()
viewController.modalTransitionStyle = .crossDissolve
viewController.modalPresentationStyle = .fullScreen
self.present(viewController, animated: true)
})
}
})
}
}
I followed the code from the video to a T, except my NEW .swift file doesn't contain the main app calculator code. The launch screen VC isn't my rootVC or maybe not my topmostVC? It was definitely created after building the calculator. So I thought, hey I'll just use the new VC as the initial view.
The above didn't work. App crashed after animation and flagged the following in my rootVC:
@IBOutlet weak var bannerView: GADBannerView! "Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value."
Weird, implicitly unwrapping the optional (don't even know what that really means haha) worked just fine before trying to add a launch screen animation before presenting rootVC. Ok I'll just use ? instead of !
@IBOutlet weak var bannerView: GADBannerView?
//etc etc
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self.view, action: #selector(UIView.endEditing))
view.addGestureRecognizer(tap)
bannerView?.adUnitID = "ca-app-pub-3940256099942544/2934735716" //TestAdUnitID
bannerView?.rootViewController = self
bannerView?.load(GADRequest())
bannerView?.delegate = self
}
No longer crashes, but now just a black screen is presented. Seems to be complaining that
[Presentation] Attempt to present <xxx.ViewController: 0x7fc9c5881a00> on <xxx.LaunchViewController: 0x7fc9c6012aa0> (from <xxx.LaunchViewController: 0x7fc9c6012aa0>) whose view is not in the window hierarchy.
because I guess with:
let viewController = ViewController()
I was "creating a new one rather than grabbing the instance (i) created in the storyboard."
OK sweet. Gave my UITabBarControlly a storyboard ID of "mainSB". Insert:
....etc
DispatchQueue.main.asyncAfter(deadline: .now()+0.5, execute: {
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyBoard.instantiateViewController(withIdentifier: "mainSB") as! ViewController
viewController.modalTransitionStyle = .crossDissolve
viewController.modalPresentationStyle = .fullScreen
self.present(viewController, animated: true)
...etc
No crash, but calculator still not presented. Red flag on:
let viewController = storyBoard.instantiateViewController(withIdentifier: "mainSB") as! ViewController "Thread 1: signal SIGABRT"
This seems to be the main complaint:
Could not cast value of type 'UITabBarController' (0x7fff8679f9e8) to 'xxx.ViewController' (0x10f689ef0).
Oh shoot, maybe there's something wrong because its not a simple view controller; its a tab bar controller with 2 tabs...?
So my logic told me to set my tab bar controller with a custom class of "ViewController" and inherit module from target. screw it.
NOPE! back to this:
[Presentation] Attempt to present <xxx.ViewController: 0x7fa4d78ba000> on <xxx.LaunchViewController: 0x7fa4d800e920> (from <xxx.LaunchViewController: 0x7fa4d800e920>) whose view is not in the window hierarchy.
So I just set my initial UITabBarController as initial view. But then it was just crashing! Couldn't figure out why for an hour. So I undid the last thing I changed, which was giving the tab bar controller a custom class of "ViewController". And now it works again with my calculator as initial view.
But I didn't wanna give up. I was led to this page.
It wasn't very clear as to where to insert these lines, so I kinda just guessed.
This is what I ended up with
func getTopMostViewController() -> UIViewController? {
let scenes = UIApplication.shared.connectedScenes
let windowScene = scenes.first as? UIWindowScene
guard let window = windowScene?.windows.first else { return nil }
var topMostViewController = window.rootViewController
while let presentedViewController = topMostViewController?.presentedViewController {
topMostViewController = presentedViewController
}
topMostViewController?.modalTransitionStyle = .crossDissolve
topMostViewController?.modalPresentationStyle = .fullScreen
getTopMostViewController()?.present(topMostViewController!, animated: true)
//or do I present presentedViewController?I'm confused
return topMostViewController
}
I don't even know if this is correct/makes sense or where to put it.
All that I've tried; I've been trying to put after the dispatch queue:
UIView.animate(withDuration: 1.5, animations: {
self.imageView.alpha = 0
}, completion: {done in
if done {
DispatchQueue.main.asyncAfter(deadline: .now()+0.5, execute: {
})
I hope you understand what I've tried and what I'm trying to do...
Maybe someone can point out if I was just adding code in the wrong area or in the wrong order?
I'd rather not go through the pain of re-building this thing just to get an animated launch screen.
I'm hoping to solve this with code.
Thanks in advance!!!