UIViewControllers sharing 'generic' IBAction - swift

I have an app with 6 UIViewControllers.
ANY viewcontroller features a function like this one:
#IBAction func onHelp(_ sender: UIBarButtonItem) {
DispatchQueue.main.async(execute: { () -> Void in
let helpVC = self.storyboard?.instantiateViewController(withIdentifier: "Help") as! HelpViewController
helpVC.starter = "MapHelp"
helpVC.helpSubtitle = "Map"
self.present(helpVC, animated: true, completion: nil)
})
}
Any IBAction in any viewcontroller presents the same HelpViewController but passing different parameters (starter and helpSubtitle).
Since I don't like to repeat code, first of all I thought this function should be converted to something more generic.
But: is there any way to create a generic IBAction, working for every viewcontroller?

Create a BaseViewController and add the generic method there.
class BaseViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func genericMethod(starter: String, helpSubtitle: String){
let helpVC = self.storyboard?.instantiateViewController(withIdentifier: "Help") as! HelpViewController
helpVC.starter = starter
helpVC.helpSubtitle = helpSubtitle
self.present(helpVC, animated: true, completion: nil)
}
#IBAction func onHelp(_ sender: UIButton?) {
//You can use this method as generic IBaction if you want. It can be connected to buttons of all child View Controllers. But doing so will limit your param sending ability. On the plus side though, you won't have to define an IBAction everywhere and you can simply connect your child VC's button to Parent Class' IBAction.
}
}
Now inherit your ViewControllers from this class like:
import UIKit
class ViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func btnTapped(_ sender: Any) {
genericMethod(starter: "View Controller", helpSubtitle: "I was triggered from VC1")
}
}
and
import UIKit
class SecondViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func btnTapped(_ sender: Any) {
genericMethod(starter: "View Controller 2", helpSubtitle: "I was triggered from VC2")
}
}
That's it. Both your ViewControllers can call the parent method. If you still want to use the generic IBAction, you can do that too but I'd not recommend that course given that you want to pass params that can vary. If you wanted to do it though, it would look like this:
Bear in mind, the ViewController here has been inherited from the base ViewController which is why it can access the IBActions defined in the parent class. All you have to do is drag and connect.

Related

Change variable while dismissing modal controller

EDIT: I have decided to change the way my app works, so this problem is solved. Thanks to everyone who helped!
I have a modal controller where when I press a button it dismisses the view. What I want to do is change a variable in another view controller when I dismiss it, is that possible? Or, if this doesn't work, is there a way for me to access the changed variable of another swift file? I will add my code below:
class PopupViewController: UIViewController {
var event = ""
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func dismiss(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
#IBAction func event910(_ sender: Any) {
event = "storyTime"
dismiss(animated: true, completion: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as! ViewController
vc.event = event
}
}
I want to pass the changed variable "event" to another view controller, how can I do this?
Delegate View Controller is as follows. : -
it is the place where you will send the data to the next swift file
protocol myprotocol {
func anyfunction(_ param1:String)
}
struct mystruct1 {
var delegate:myprotocol?
// where you want tot start the delegate / send the data to the next file
func anymethod(){
delegate.anyfunction(sendTheDataYouWant)
}
}
// it is here you will receive the data
class anyclass:UIViewController ,myprotocol {
let class1 = mystruct1()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
class1.delegate = self
}
func anyfunction(param1:String){
// here Save the data you want
// because this function will be triggered as delegate will be called
}
}
ps:- I reccomend you to read https://docs.swift.org/swift-book/LanguageGuide/Protocols.html
& apple docs

How to run code in your main view controller in swift when a pop up closes

I'm currently writing my first swift app. Currently there is one view/view controller that loads when the app is run as well as a popup window tied to a separate view-controller (like so: https://www.youtube.com/watch?v=S5i8n_bqblE). When I close the pop-up I want to update several things on my original view and run some code. However, neither func viewDidLoad() nor func viewDidAppear() seems to run. And I can't do anything from the pop-up view since I don't have access to the components in the main view-controller from it. What should I do?
The pop-up is "presented modally" if that makes a difference?
I'm assuming you have a MainViewController from which you're presenting the PopupVC. You can use delegate pattern here.
Define a PopupVCDelegate as follow
protocol PopupVCDelegate {
func popupDidDisappear()
}
In your PopupVC define a delegate property of type PopupVCDelegate. And in the closePopup method, call the delegate method popupDidDisappear
class PopupVC: UIViewController {
public var delegate: PopupVCDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func closePopup(_ sender: Any) {
dismiss(animated: true, completion: nil)
delegate?.popupDidDisappear()
}
}
Now any class that adopts this delegate will be able to receive the callback when the closePopup is called. So make your MainViewController to adopt this delegate.
class MainViewController: UIViewController, PopupVCDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
func showPopup() {
let popupViewController = //Instantiate your popup view controller
popupViewController.delegate = self
//present your popup
}
func popupDidDisappear() {
//This method will be called when the popup is closed
}
}
Another way is to fire a notification through NSNotificationCenter on closePopup and add an observer in MainViewController to listen to that notification. But it is not recommended in this scenario.
Edit
As you have asked for the NSNotificationCenter method. Please change your classes as follow
class PopupVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func closePopup(_ sender: Any) {
dismiss(animated: true, completion: nil)
NotificationCenter.default.post(name: NSNotification.Name("notificationClosedPopup"), object: nil)
}
}
class MainViewController: UIViewController, PopupVCDelegate {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(onPopupClosed), name: NSNotification.Name(rawValue: "notificationClosedPopup"), object: nil)
}
#objc func onPopupClosed() {
//This method will be called when the popup is closed
}
deinit {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: "notificationClosedPopup"), object: nil)
}
}

#IBDesignable with protocol

I have a UIview xib within a view controller, UIview class have two buttons with protocol function, but the protocol function never called when I press button, storyboard image like below
protocol method like below
import UIKit
#objc protocol TopViewDelegate: NSObjectProtocol {
#objc optional func pressRefreshButton()
#objc optional func pressMenuButton()
}
UIView class
#IBDesignable class OnJob_Top: UIView,TopViewDelegate {
weak var delegate : TopViewDelegate? = nil
#IBAction func refreshButtonTouchUpInside(_ sender: UIButton) {
delegate?.pressRefreshButton!()
}
#IBAction func menuButtonTouchUpInside(_ sender: UIButton) {
delegate?.pressMenuButton!()
print("come come")
}
view controller class
class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let topView = OnJob_Top()
topView.delegate = self
}
}
extension HomeViewController:TopViewDelegate {
func pressMenuButton() {
print("come") // never come here
}
func pressRefreshButton() {
print("come") // never come here
}
}
Consider this code:
let topView = OnJob_Top()
topView.delegate = self
In the first line, you create a completely new OnJob_Top view.
In the second line, you make it the delegate.
In the third line... but there is no third line. The view vanishes in a silent puff of smoke. It is useless.
Meanwhile, the view in the storyboard never gets a delegate. So its delegate methods are never called.

Stop animation of first view controller when second is started swift programming

How to stop animation of first view controller when second is started in swift programming. I have created a function which stops animation in first view controller. I want it to be called in second view controller.
In first View Controller
func stopAni(){
self.resultView.stopAnimating()
ButtonAudioPlayer.stop()
ButtonAudioPlayer1.stop()
ButtonAudioPlayer2.stop()
ButtonAudioPlayer3.stop()
ButtonAudioPlayer4.stop()
ButtonAudioPlayer5.stop()
ButtonAudioPlayer6.stop()
Not sure how to call this function in second view controller.
You can create a delegate something like:
protocol StopAnimationDelegate{
func stopAnimations()
}
Then, on your first view controller you're going to adopt this protocol:
class FirstViewController : UIViewController, StopAnimationDelegate{
//..... here code
func stopAnimations(){
//Stop your animations or call your method stopAni here.
}
//.... here more code
#IBAction func openSecondViewController(sender:UIButton){
self.performSegueWithIdentifier("segue_first_second",sender:nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "segue_first_second"{
let secondViewController = segue.destinationViewController as! SecondViewController
secondViewController.delegate = self
}
}
}
On your second view controller, you can make something like:
class SecondViewController: UIViewController{
var delegate:StopAnimationDelegate?
#override func viewDidLoad(){
delegate?.stopAnimations()
}
}
Note: That's a way of how you can accomplish that, but all depends on what you need to do, for example you can simply stop the animations when you perform the segue (but again, that depends on what you want to do).
Another option, is using NSNotificationCenter to post a notification to Stop the animation, something like:
In First View Controller:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "stopAnim", name: "kStopAnimations", object: nil)
}
//...Your stopAnim method
//... More Code
}
class SecondViewController : UIViewController{
override func viewDidLoad() {
NSNotificationCenter.defaultCenter().postNotificationName("kStopAnimations", object: nil)
}
}

Delegate using Container View in Swift

I'm developing an app for iPad Pro. In this app, containerView use to add additional views and interact with them.
First, I created a protocol:
protocol DataViewDelegate {
func setTouch(touch: Bool)
}
Then, I created my first view controller
import UIKit
class ViewController: UIViewController, DataViewDelegate {
#IBOutlet var container: UIView!
#IBOutlet var labelText: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
func setTouch(touch: Bool) {
if touch == true {
labelText.text = "Touch!"
}
}
}
And finally, I created a view that will be embedded in containerView.
import UIKit
class ContainerViewController: UIViewController {
var dataViewDelegate: DataViewDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func touchMe(sender: AnyObject) {
dataViewDelegate?. setTouch(true)
}
}
But for some reason, nothing happened, the first view controller receives nothing in setTouch function.
My question is: In this case, using container, how can I make the communication between two ViewsControllers?
Like #nwales said you haven't yet set the delegate. You should do set the delegate in prepareForSegue function on your first viewController (who contain the viewContainer)
First select the embed segue and set an identifier in the attributes inspector.
Then in the parentViewController implement the func prepareForSegue like this:
Swift 4+:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "the identifier") {
let embedVC = segue.destination as! ViewController
embedVC.delegate = self
}
}
Below:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
if (segue.identifier == "the identifier") {
let embedVC = segue.destinationViewController as! ContainerViewController
embedVC.dataViewDelegate = self
}
}
Looks like you defined the delegate, but have not set the delegate. This happens to me all the time.