简体   繁体   中英

How to present programmatically a view controller from a UIVIew

I am using a Class which is a subclass of MessageView ( Swift Message Library ) which is inherit from UIView . Inside, I have a UIButton and I want to present programmatically another ViewController through it.

Here is my code below :

import Foundation
import SwiftMessages
import UIKit

class MyClass: MessageView {

    var hideBanner: (() -> Void)?


    @IBAction func helpButtonPressed(_ sender: UIButton) {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let newViewController = storyBoard.instantiateViewController(withIdentifier: "newViewController") as! NewViewController
        self.present(newViewController, animated: true, completion: nil)

    @IBAction func tryAgainButtonPressed(_ sender: UIButton) {
        hideBanner?()
    }

    open override func awakeFromNib() {

    }

}

I have tried this, but it is not working since the UIView do not have the present method.

.present is a method in UIViewController class, that's the reason you cannot present view controller from UIView class.

To achieve this, get the root view controller and present the controller as follows:

let appDelegate  = UIApplication.shared.delegate as! AppDelegate
let viewController = appDelegate.window!.rootViewController as! YourViewController
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let newViewController = storyBoard.instantiateViewController(withIdentifier: "newViewController") as! NewViewController
        viewController .present(newViewController, animated: true, completion: nil)

First get top ViewController using this. Then you can present your viewController .

if var topController = UIApplication.sharedApplication().keyWindow?.rootViewController {
    while let presentedViewController = topController.presentedViewController {
        topController = presentedViewController
    }

    // topController now can use for present.
    let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
    let newViewController = storyBoard.instantiateViewController(withIdentifier: "newViewController") as! NewViewController
    topController.present(newViewController, animated: true, completion: nil)
}

The iOS convention is that only a ViewController s presents another ViewController .

So the answers above - where the View is finds the current ViewController via UIApplication.sharedApplication().keyWindow?.... will work but is very much an anti-pattern.

The preferred way would be:

  • Your MyClass view has presentation code only
  • You must have a ViewController which has a reference to this MyClass view
  • This ViewController has the @IBAction func tryAgainButtonPressed
  • From there, you can present the next ViewController

Try this #simple code.

import Foundation
import SwiftMessages
import UIKit

class MyClass: MessageView {

var hideBanner: (() -> Void)?


@IBAction func helpButtonPressed(_ sender: UIButton) {
    let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
    let newViewController = storyBoard.instantiateViewController(withIdentifier: "newViewController") as! NewViewController
    UIApplication.shared.keyWindow?.rootViewController?.present(newViewController, animated: true, completion: nil)
}

@IBAction func tryAgainButtonPressed(_ sender: UIButton) {
    hideBanner?()
}

open override func awakeFromNib() {

}

}

Here is the example code using delegation pattern.

class YourViewController: UIViewController {
  var yourView: MyClass // may be outlet

    override func viewDidLoad() {
        super.viewDidLoad()

        yourView.delegate = self
    }

}

protocol MyClassDelegate:class {
    func tryAgainButtonDidPressed(sender: UIButton)
}

class MyClass: MessageView {

  weak var delegate: MyClassDelegate?


   @IBAction func tryAgainButtonPressed(_ sender: UIButton) {
        delegate?.tryAgainButtonDidPressed(sender: sender)
    }

}

You can achieve this by two ways

  • Protocol
  • By giving reference of that view controller to the view when you are initializing view

Sorry for the late reply. MessageView already provides a buttonTapHandler callback for you:

/// An optional button tap handler. The `button` is automatically
/// configured to call this tap handler on `.TouchUpInside`.
open var buttonTapHandler: ((_ button: UIButton) -> Void)?

@objc func buttonTapped(_ button: UIButton) {
    buttonTapHandler?(button)
}

/// An optional button. This buttons' `.TouchUpInside` event will automatically
/// invoke the optional `buttonTapHandler`, but its fine to add other target
/// action handlers can be added.
@IBOutlet open var button: UIButton? {
    didSet {
        if let old = oldValue {
            old.removeTarget(self, action: #selector(MessageView.buttonTapped(_:)), for: .touchUpInside)
        }
        if let button = button {
            button.addTarget(self, action: #selector(MessageView.buttonTapped(_:)), for: .touchUpInside)
        }
    }
}

which is automatically invoked for any button you connect to the button outlet. So the recommended method for presenting another view controller is to have the presenting view controller configure the presentation logic in this callback:

messageView.tapHandler = { [weak self] in
    guard let strongSelf = self else { return }
    let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
    let newViewController = storyBoard.instantiateViewController(withIdentifier: "newViewController") as! NewViewController
    strongSelf.present(newViewController, animated: true, completion: nil)
}

If your view has more than one button, you can handle them all through buttonTapHandler since it takes a button argument. You just need to configure the target-action mechanism for each button:

otherButton.addTarget(self, action: #selector(MessageView.buttonTapped(_:)), for: .touchUpInside)

Or you can add one callback for each button by duplicating the above pattern.

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