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:
MyClass
view has presentation code only MyClass
view @IBAction func tryAgainButtonPressed
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
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.