0

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!!!

yuckbarry
  • 1
  • 2
  • If your *existing* project has a `UITabBarController` as the Initial View Controller in your Storyboard, ***that*** is the controller you need to instantiate and transition to. It looks like you are trying to load `ViewController()` - which I assume is the first tab? – DonMag May 11 '22 at 19:29
  • Oh - re-reading... *"Gave my UITabBarControlly a storyboard ID of "mainSB""* ... change the last part of this: `let viewController = storyBoard.instantiateViewController(withIdentifier: "mainSB") as! ViewController` to: `!as UITabBarController` – DonMag May 11 '22 at 19:32
  • Whoops - typo... that should have been `as! UITabBarController` ... And... you don't want to use `.present(...)` ... take a look at this answer for a way to swap the current root view controller (your `LaunchViewController`) with the tab bar controller you're loading from the Storyboard: https://stackoverflow.com/a/41144822/6257435 – DonMag May 11 '22 at 19:40
  • @DonMag ,That worked!!! I can't thank you enough! I had a feeling It was gonna be something simple. I kept overthinking everything. And I think I did actually do that, but in between I was changing different things and I probably didn't undo those changes. So simple. Pardon me, as this is like learning 3 hours of the Russian language after speaking only English all my life, and then writing a high school level short story in Russian about river banks in Antarctic deserts. Thank you again! Works as expected! – yuckbarry May 12 '22 at 01:55

0 Answers0