简体   繁体   中英

present a ViewController from a Swift class derived from an NSObject?

This project was written in Objective C and a bridging header and Swift files were added so they can be used at run time. When the app starts, Initializer called in Mock API client is printed in the debugger. Is it possible to present a ViewController from the initializer?

Xcode Error:

Value of type 'MockApiClient' has no member 'present'

//MockApiclient.Swift

import Foundation

class MockApiClient: NSObject
{

override init ()
{
    print("Initializer called in Mock API client")
    
    if isLevelOneCompleted == false 
        
        {
      
        print("It's false")
       
        let yourVC = ViewController()
            self.present(yourVC, animated: true, completion: nil)
        
        } else 
             {
           
            print("It's true")

        }
}

var isLevelOneCompleted = false

@objc func executeRequest()
{
    print("The execute request has been called")
    
    isLevelOneCompleted = true 
    
        if isLevelOneCompleted {
            
            print("It's true")

            } else {
                //do this
            }
    }
}

Update - ViewController.m

// prints "The execute request has been called" from the debugger window

- (void)viewDidLoad {
[super viewDidLoad];

MockApiClient *client = [MockApiClient new];
[client executeRequest];
}

You can't call present(_:animated:completion) because it is a method of UIViewController , not NSObject.

Why not pass a viewController reference to the MockApiClient to present on instead like so. Be sure to check Leaks or Allocations on instruments to avoid the client retaining the controller.

class MockApiClient: NSObject {
    var referencedViewController: UIViewController?

    override init() {
        let presentableViewController = ViewController()
        referencedViewController.present(presentableViewController, animated: true, completion: nil)
    }

    deinit {
        referencedViewController = nil
    }

}

let apiClient = MockApiClient()
apiClient.referencedViewController = // The view controller you want to present on

Assuming you're using UIKit, you'll have to present the view controller from the nearest available attached view controller. If you know for certain that no other view controllers would currently be presented then you can safely present from the root view controller:

UIApplication.shared.keyWindow?.rootViewController?.present(someViewController, animated: true, completion: nil)

This concept of attached and unattached/detached view controllers is never officially explained but the infamous UIKit warning of presenting view controllers on detached view controllers is real. And the workaround is finding the nearest available attached view controller, which at first (when nothing is currently being presented) is the root view controller (of the window). To then present an additional view controller (while one is currently being presented), you'd have to present from that presented view controller or its nearest parent view controller if it has children (ie if you presented a navigation view controller).

If you subclass UIViewController , you can add this functionality into it to make life easier:

class CustomViewController: UIViewController {
    var nearestAvailablePresenter: UIViewController {
        if appDelegate.rootViewController.presentedViewController == nil {
            return appDelegate.rootViewController
        } else if let parent = parent {
            return parent
        } else {
            return self
        }
    }
}

Then when you wish to present , you can simply do it through this computed property:

nearestAvailablePresenter.present(someViewController, animated: true, completion: nil)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM