I have custom uinavigation class. In that custom delegate method declared. How to access that method to view controller - swift

#objc protocol MyDelegate {
func buttonAction()
}
class CustomNavigationBar: UINavigationController {
var delegte : MyDelegate?
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton.init(frame: CGRectMake(200, 10, 50, 30))
button.setBackgroundImage(UIImage(named: "a.png"), forState: .Normal)
button.addTarget(self, action: "testing", forControlEvents: .TouchUpInside)
self.navigationBar.addSubview(button)
}
func testing(){
self.delegte?.buttonAction()
print("Pavan")
}
If i press this button, testing is calling.
But in viewcontroller calling delegate method but giving error
class ViewController: UIViewController,MyDelegate{
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "hi"
let vc = CustomNavigationBar()
vc.delegte = self
}
func buttonAction() {
print("Tupale")
}

would u mind to hint the error message?
updated:
In CustomNavigationBar class, you have to change var delegte : MyDelegate? to var delegte : UIViewController?.
then in ViewController class, you could set self which is an instance of UIViewController to the delegate of vc.
Have a try.

Related

How do I targeting selector with addTarget in another class

I try to understand how to control component form one class to another class but that's not working.
class VC:UIViewController {
override func viewDidLoad() {
let instance = Test()
self.view.addSubView(instance.button)
}
}
class Test:NSObject {
var button:UIButton!
override init() {
button = UIButton(frame:CGRect(x:0, y:0, width:100, height:40))
button.setTitle(("TEST", for:.normal)
button.addTarget(self, action: #selector(tapButton(_:)), for: .touchUpInside)
}
#objc func tapButton(_ sender:UIButton) {
print("TAP Button")
}
}
When I tapped the button, nothing happens !
I try to change
button.addTarget(self, action: #selector(tapButton(_:)), for: .touchUpInside)
with
button.addTarget(nil, action: #selector(tapButton(_:)), for: .touchUpInside)
That's not working !
How to resolve this problem ?
Thanks for your help.
You have 3 issues:
instance is a local variable, so it gets deallocated as soon as viewDidLoad() finishes. Make it a property of VC. The button only keeps a weak reference to the object, so when it is deallocated it becomes nil. In cases where the target is nil, UIKit will search up the responder chain for the action method. Since VC doesn't supply a tapButton method, nothing happens when the button is pressed.
You need to call super.init() so that self is available to be used with the button. self can't be created until all properties have been initialized. Because init is an override, you must call super.init() to initialize the properties that NSObject provides before you can use self in the button.
Your frame puts your button in an unaccessible region of the screen. I changed your frame to put the button inside the safe area of the iPhone 11. I also made the button .green so that I could see it.
class VC: UIViewController {
var instance = Test() // make instance a property
override func viewDidLoad() {
// let instance = Test() // this was a local variable that doesn't hang around
self.view.addSubview(instance.button)
}
}
class Test: NSObject {
var button: UIButton!
override init() {
super.init() // call this so that you can use self below
button = UIButton(frame:CGRect(x: 100, y: 100, width: 100, height: 40))
button.setTitle("TEST", for:.normal)
button.backgroundColor = .green
button.addTarget(self, action: #selector(tapButton(_:)), for: .touchUpInside)
}
#objc func tapButton(_ sender: UIButton) {
print("TAP Button")
}
// Add deinit to see when this object is deinitialized. When
// instance is local to viewDidLoad() this object gets freed
// when viewDidLoad() finishes.
deinit {
print("Oops, the Test object has been deinitialized")
}
}

How can I make sure that after declaring a delegate of a protocol in Swift 5.2, that delegate is not nil when it is called?

I am trying to clear a textfield in MainViewController from the DetailViewController. I have the following code in a Swift Playground.
import UIKit
import PlaygroundSupport
protocol DetailViewControllerDelegate: class {
func bar()
}
class DetailViewController: UIViewController {
var detailViewControllerDelegate: DetailViewControllerDelegate!
override func loadView() {
let view = UIView()
view.backgroundColor = .white
let button = UIButton()
button.frame = CGRect(x: 100, y: 200, width: 180, height: 20)
button.setTitle("Hello World!", for: .normal)
button.backgroundColor = .blue
button.addTarget(self, action: #selector(handlePress), for: .touchUpInside)
view.addSubview(button)
self.view = view
}
#objc func handlePress() {
print("\(#function)")
if let vrvc = detailViewControllerDelegate {
vrvc.bar()
} else {
print("detailViewControllerDelegate is NIL")
}
}
}
class MainViewController : UIViewController, DetailViewControllerDelegate {
func bar() {
print("Inside Bar")
}
override func loadView() {
let detailViewController = DetailViewController()
detailViewController.detailViewControllerDelegate = self
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = DetailViewController()
When the button is pressed, detailViewController is nil. How can I make sure that detailViewController is NOT nil when the button is pressed?
You have two distinct DetailViewControllers:
override func loadView() {
let detailViewController = DetailViewController()
detailViewController now references a new instance of DetailViewController
detailViewController.detailViewControllerDelegate = self
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = DetailViewController()
liveView now references a new instance of DetailViewController which is different from the one above and in which you have not set detailViewControllerDelegate.
If you need to create a DetailViewController in one place and reference it in another you need to store a reference to it in a property that is accessible in both places.
That said, the second instance of DetailViewController is being created in a statement which looks like an attempt to test code in the Playground so maybe you just need to think about how you are testing.

How do I call an interstitial ad to load on another UIViewController?

I have a UIViewController with a UIView displayed on it; pressing a button on the UIView loads my interstitial ad. When the UIView is subsequently displayed, I want the interstitial to be displayed with the rootVC as the UIViewController.
However, this code does not seem to work as intended:
1) My View Controller:
class MyViewController: UIViewController {
let button: UIButton = {
let btn = UIButton()
btn.translatesAutoresizingMaskIntoConstraints = false
btn.setTitle("BUTTON", for: .normal)
btn.addTarget(self, action: #selector(showUIView), for: .touchUpInside)
return btn
}()
#objc func showUIView(_ sender: UIButton) {
let popUp = MyUIView()
self.view.addSubview(popUp)
}
2) My UIView:
class MyUIView: UIView {
var interstitial: GADInterstitial!
let button: UIButton = {
let btn = UIButton()
btn.translatesAutoresizingMaskIntoConstraints = false
btn.setTitle("UIVIEW BUTTON", for: .normal)
btn.addTarget(self, action: #selector(prepareInterstitial), for: .touchUpInside)
return btn
}()
#objc func prepareInterstitial(_ sender: UIButton) {
interstitial = GADInterstitial(adUnitID: "ca-app-pub-3940256099942544/4411468910")
let request = GADRequest()
interstitial.load(request)
dismissPopUp()
if interstitial.isReady {
interstitial.present(fromRootViewController: MyViewController())
}
}
I get this in the console:
Warning: Attempt to present <GADFullScreenAdViewController: 0x7f8611e22fc0> on <Project.MyViewController: 0x7f8612884800> whose view is not in the window hierarchy!`
which I do not understand because MyViewController is still very much a part of the view hierarchy.
I'd be really grateful if someone could show me how to fix this error, I'm relatively new to coding and not sure what I am doing wrong. Thank you!
The reason why this doesn't work is because you are creating a brand new VC here:
interstitial.present(fromRootViewController: MyViewController())
This MyViewController() is not the VC that is shown on the screen! You just created by calling its initialiser.
You need to somehow get the VC that's shown on the screen. One simple way to do this is to add a rootVC property to your MyUIView:
weak var rootVC: UIViewController?
And then present this instead:
if let rootVC = self.rootVC { // nil check!
interstitial.present(fromRootViewController: rootVC)
}
In showUIView, set self as rootVC:
#objc func showUIView(_ sender: UIButton) {
let popUp = MyUIView()
popUp.rootVC = self
self.view.addSubview(popUp)
}

How can I call a function that creates a button from another viewcontroller?

I have a function in my TuesdayViewController.swift that creates a button.
Whenever it's Tuesday, I want that button to appear in my ViewController.swift
I have this function in my TuesdayViewController:
func tuesdayView(){
let button = UIButton(frame: CGRect(x: 16, y: 200, width: 343, height: 45))
button.backgroundColor = .red
button.setTitle("Test button", for: .normal)
self.view.addSubview(button)
}
If I call this function in Tuesday's viewDidLoad(), the button shows in the TuesdayViewController.
How can I make it so I call this function through my main ViewController and for the button to show in the main ViewController screen?
I have tried this in my main ViewController but the button doesn't appear:
override func viewDidLoad(){
super.viewDidLoad()
TuesdayViewController().tuesdayVew()
}
Any help would be appreciated!
If I understand your question right, you just want to add the same kind of button from your TuesdayViewController into your MainViewController, and not adding the button to the TuesdayViewController by calling it in the MainViewController.
To do that, you just can declare your tuesdayView as a public property of your TuesdayViewController, like so:
TuesdayViewController.swift
class TuesdayViewController: UIViewController {
public lazy var tuesdayView: UIButton = {
let button = UIButton(frame: CGRect(x: 16, y: 200, width: 343, height: 45))
button.backgroundColor = .red
button.setTitle("Test button", for: .normal)
return button
}()
override func viewDidLoad(){
super.viewDidLoad()
self.view.addSubview(self.tuesdayView)
}
}
And call that property like what you've been doing before.
MainViewController.swift
class MainViewController: UIViewController {
override func viewDidLoad(){
super.viewDidLoad()
let button = TuesdayViewController().tuesdayView
self.view.addSubview(button)
}
}
You need to use an observer with NotificationCenter:
First you need to create a Notification Name
extension Notification.Name {
static let notName = Notification.Name("notName")
}
TuesdayViewController:
override func viewDidLoad() {
NotificationCenter.default.addObserver(self, selector: #selector(tuesdayViewObserver), name: .notName, object: nil)
}
#objc func tuesdayViewObserver(_ notification: Notification) {
//Here you call the tuesdayView function
tuesdayView()
}
And in the controller in which you want to fire the method, you post the notification and the method will be executed:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.post(name: .notName, object: nil)
}
For further info, I recommend you to take a look to NotificationCenter and how it works. LINK

Swift 3 Method from Class not working with #selector syntax

I feel as though I'm not understanding the Swift #selectors properly. I'm trying to connect a button to a method from another class.
I have a class to print the button when pushed:
class printThings {
#IBAction func printMe(_ sender: UIButton){
print("Button Pushed.")
}
}
And then the ViewController:
class ViewController : UIViewController {
override func ViewDidLoad(){
super.viewDidLoad()
//button setup here
let printMe = printThings()
button.addTarget(printMe, action: #selector(printMe.printMe(_:)), for: .touchUpInside)
//add button to subview
}
}
This never triggers the print statement in the class. I'm sure I'm missing something simple.
Thanks.
The problem is that printMe is a temporary, local variable:
let printMe = printThings() // local variable
button.addTarget(printMe, action: #selector(printMe.printMe(_:)), for: .touchUpInside)
// ... and then viewDidLoad ends, and `printMe` vanishes
So by the time you push the button, printMe has vanished; there is no one to send the button message to.
If you want this to work, you need to make printMe persist:
class ViewController : UIViewController {
let printMe = printThings() // now it's a _property_ and will persist
override func viewDidLoad(){
super.viewDidLoad()
//button setup here
button.addTarget(self.printMe, action: #selector(self.printMe.printMe(_:)), for: .touchUpInside)
//add button to subview
}
}