Swift: Call a function in another ViewController - ios

I have a TableViewController to handle my data and a sortFunction. What I want to do is to call the sortFunction in a SortViewController and when the Button "done" is clicked go back to my TableViewController (which should show the sorted data now)
My Question is: how can I call the sortFunction() which is included in my TableViewController in the SortViewController?
This what I have:
TableViewController:
func sortFunction(){
self.data?.sortInPlace({ $0.clicks > $1.clicks })
self.tableView.reloadData()
}
SortViewController:
When the specific sort Button is clicked:
#IBAction func sortBestClicks(sender: AnyObject) {
// how to call the function from TableViewController here??
}
When Done is Clicked:
#IBAction func doneButton(sender: AnyObject) {
self.performSegueWithIdentifier("unwindToStart", sender: self)
}

In TableViewController viewDidLoad add
NSNotificationCenter.defaultCenter().addObserver(self, selector: "methodOfReceivedNotification", name:"NotificationIdentifier", object: nil)
and add a method in TableViewController like
func methodOfReceivedNotification() {
sortFunction()
}
in SortViewController call the method with
NSNotificationCenter.defaultCenter().postNotificationName("NotificationIdentifier", object: nil)
Hope this helps you.
You could also use a delegate protocol.

Related

Swift - access UIButton inside ContainerView in another ViewController

I am having a problem accessing my UIButton that's inside a ContainerView from another ViewController.
If the user taps the button a SubView should appear inside of my ViewController. I tried dragging the button in my ViewController file to create an #IBAaction func tapped() but that is not working.
It only works inside of the ContainerView.swift file. But I can not create my Subview there...
I hope you get my problem, I am grateful for every help!
import UIKit
class ContainerViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func addButtonTapped(_ sender: Any) {
print("add Button")
}
You can reach your ViewController inside your ContainerView using its parent property. So, inside your ContainerView class:
if let parentVC = self.parent as? ViewController {
parentVC.showSubView() // your method to show the sub view.
}
You can achieve this using NotificationCenter and Delegates. Here is how you can achieve it with NotificationCenter
In your ContainerViewController you can post notification on button click
#IBAction func yourBtnTap(sender : UIButton) {
NotificationCenter.default.post(name: Notification.Name("myBtnTapped"), object: nil)
}
In your CurrentViewController you have to first register to receive this notification. So in your viewDidLoad, do this:
NotificationCenter.default.addObserver(self, selector: #selector(self.myBtnTappedAction(notification:)), name: Notification.Name("myBtnTapped"), object: nil)
Now in your CurrentViewContoller create myBtnTappedAction function that will be called whenever the button is tapped.
#objc func myBtnTappedAction(notification : Notification) {
// now you can perform all your actions here. this function will be called everytime the button in the container view is tapped.
}

Swift 3: UITabBarController change tab on unwind

I have UITabBarController. From the Home tab, I segued to a ThankYouVC.
When I unwind from ThankYouVC, I want to change the selected tab.
What I've tried:
HomeVC
#IBAction func unwindToMain(segue:UIStoryboardSegue) {
print("unwind")
self.tabBarController?.selectedIndex = 0
}
The console log prints unwind, but doesn't change the index.
Another attempt:
enum Notifications: String, NotificationName {
case QRDoneNotification
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(unwindCallBack), name: Notification.Name("QRDoneNotification"), object: nil)
}
#IBAction func unwindToMain(segue:UIStoryboardSegue) {
print("unwind")
NotificationCenter.default.post(name: Notifications.QRDoneNotification.name, object: nil)
}
func unwindCallBack() {
self.tabBarController?.selectedIndex = 0
}
Still no luck!!
Help me out.
The problem is that an unwind segue unwinds to the view controller that holds the function. So that's where you end up.
One solution: subclass UITabBarController and put your unwind segue there.
class MyTabBarController: UITabBarController {
#IBAction func unwindToMain(segue:UIStoryboardSegue) {
print("Unwinding to the custom tab bar controller...")
selectedIndex = 1
}
}
So, add that class to your project... set the Custom Class of your current UITabBarController to MyTabBarController... Assign your Exit / Unwind segue to this new one, and don't forget to delete your existing unwindToMain() function and unwind connection.

iOS Swift Programmatically Trigger button from previous ViewController

When BackgroundClicked is triggered go back to the previous viewcontroller and programatically click on a button on the previous viewcontroller on the load. But only if BackgroundClicked was clicked from the child view controller
for example
//this button is on child controller
#IBAction func BackgroundClicked(sender: AnyObject)
{
self.navigationController?.popToRootViewControllerAnimated(true)
// when im on the root controller click button abc
// button abc resides on the root controller
}
I only want button abc clicked on the load of the root controller only if BackgroundClicked was clicked. I'm not sure how to go about this or if it's even possible
You can send a notification when button is pressed into your ChildViewController like show in below:
Add this code in your ChildViewController when you press a button:
#IBAction func BackgroundClicked(sender: AnyObject)
{
NSNotificationCenter.defaultCenter().postNotificationName("refresh", object: nil)
self.navigationController?.popToRootViewControllerAnimated(true)
}
In your ParentViewController add this code into your viewDidLoad method:
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "refreshList:", name:"refresh", object: nil)
}
and here is your helper function:
func refreshList(notification: NSNotification){
println("parent method is called")
}
Now when ever you press back button from your ChildViewController refreshList method will call from your ParentViewController.
Check THIS sample project for more Info.
Hope this will help.
So that will go back to the root view controller, but what you want to do is you want to create a function in the previous view controller. In the code in the viewController that you are going back to run the name of the function in the viewDidLoad() method. Also whenever you click the back button you want to change the value of a variable because you don't want to run the function every time int he viewDidLoad(). This is what the code to the previous view controller should look like:
var decideInt: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
if decideInt == 0 {
// Do Nothing
} else if decideInt == 1 {
functionYouWantToRun()
decideInt = 0
}
}
func functionYouWantToRun() {
//Write the code to the function you want to run
}
Here is the code that you need to write to go back:
//this button is on child controller
#IBAction func BackgroundClicked(sender: AnyObject)
{
self.navigationController?.popToRootViewControllerAnimated(true)
var goToView: DestinationViewControllerName = DestinationViewControllerName()
goToView.decideInt = 1
}

Refreshing Data when dismissing Modal

I have an reference to a managed object called selectedItem, I load the data in on view controller and have a modal segue (over current context) to another view to edit the title property of the selectedItem.
I expect a textLabel to refresh the data when dismissing the modal view but it does not. I have used the same method to add to the table data and it worked, because I use tableView.reloadData but how can I refresh the label data using the modal segue ? or basically have the label change to the new value.
detailsViewController
var selectedItem: Item!
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
self.tableView.reloadData()
titleLabel.text = selectedItem.title
}
override func viewDidLoad() {
super.viewDidLoad()
titleLabel.text = selectedItem.title
}
EditViewController
#IBAction func save(sender: AnyObject) {
selectedItem.title = editTitle.text
var error: NSError?
if context!.save(nil){}
context?.save(&error)
self.presentingViewController?.viewWillAppear(true)
self.dismissViewControllerAnimated(true, completion: {});
}
PS: I tried to do a work around by using another segue to go back to the first view but that crashed, does anybody know why ?
You can use a NSNotification, they are pretty handy for this sort of thing, here's an example of some generic usage:
Parent View Controller
In viewDidLoad:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "udpateObject:", name: "udpateObject", object: nil)
In deinit:
NSNotificationCenter.defaultCenter().removeObserver(self, name: "udpateObject", object: nil)
Then you'll make a func that matches the selector of the observer:
func udpateObject(notification: NSNotification) {
// here you'll get the object you update in a different view
if let receivedObject = notification.object as? YOUR_OBJECT_DATA_TYPE {
self.ThisInstanceVariable = receivedObject
// Update any UI elements
}
}
Update View Controller
Wherever you update your data:
NSNotificationCenter.defaultCenter().postNotificationName("udpateObject", object: YOUR_UPDATED_OBJECT)

IOS Swift handle global events

How can I handle global events triggered by the notification centre for example in my API class I fire an event if an error response is received e.g. (500). When that event is fired an UIAlert should be displayed on what ever view controller is active, or on logout the login view controller should be presented.
As far as I can see there is no easy way to get the current view controller in order to interact with it. (Note that my root view controller is NOT a navigation controller).
An alternative solution, that will work regardless of whether your view controllers are embedded in a UINavigationController or not, would be to subclass UIViewController. This class will handle receiving the NSNotification that an error occurred and will also handle displaying the alert:
class MyViewController: UIViewController {
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
NSNotificationCenter.defaultCenter().addObserver(self,
selector: "errorOccured",
name: "ErrorNotification",
object: nil)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self, name: "ErrorNotification", object: nil)
}
func errorOccured() {
// Present an UIAlertViewController or the login screen.
}
}
Now, any UIViewControllers that should display an alert when the error notification is posted just have to be a subclass of MyViewController. Just make sure, if you override viewWillAppear or viewWillDisappear, that you call super.viewWillAppear or super.viewWillDisappear.
Is this way too hard to get current view controller ( when not using navigation controller ) ?
// on your app delegate
getCurrentViewController(self.window!.rootViewController!)
func getCurrentViewController(viewController:UIViewController)-> UIViewController{
if let navigationController = viewController as? UINavigationController{
return getCurrentViewController(navigationController.visibleViewController)
}
if let viewController = viewController?.presentedViewController {
return getCurrentViewController(viewController)
}else{
return viewController
}
}
For BroadCast Notification
NSNotificationCenter.defaultCenter().postNotificationName("erro400", object: nil)
For Receive
override func viewWillAppear(animated: Bool) {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "ErroOccure", name: "erro400", object: nil)
}
func ErroOccure()
{
//present alert from here
// do whatever you want
}
You have to Remove Notification when you finish with it.
override func viewWillDisappear(animated: Bool) {
NSNotificationCenter.defaultCenter().removeObserver(self)
}

Resources