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.
Related
My question is duplicate but I need not a suitable answer. Also, I have raised the same before how to callback the array of data to another viewController in iOS Swift
I have a parent viewController called CreateCardViewController and a child controller called webViewController.
In parent viewController, I have used carbonKit for showing the tab bar menu. When the tab bar menu first index is webViewController (that's a child controller).
My question is: How to send data to the parent controller from the child controller?
For example: From a child, viewController will get a list of tab bar menu items. After getting tab bar menu items, I need to send menu items to parent viewController to show tab bar.
Here is the clear picture, which I am trying to do:
you can use delegate like what #Aqua said. or use observation for this.
class ParentViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(self.updateParentViewController(_:)), name: Notification.Name(rawValue: "updateParentViewController"), object: nil)
}
#IBaction func updateParentViewController(_ notification: NSNotification){
if let receivedData = notification.userInfo?["data"] as? Any {
//use received data
// update your parentViewController.
}
}
}
//.............
class ChildViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
func sendDataToParentViewController() {
let dataDict:[String: Any] = ["data"://what you want to send.]
NotificationCenter.default.post(name: . updateParentViewController, object: nil, userInfo: dataDict)
}
}
this works for me.
On your custom ChildViewController add propertyparentViewController and set it when you create that child view controller. Then, implement specific method on parent viewcontroller, that receives data from child view controller.
protocol ParentViewControllerProtocol {
func receiveChildData(_ child: UIViewController, data: Any)
}
class ChildViewController: UIViewController {
var parentViewController: ParentViewControllerProtocol!
func timeToSendDataToParentViewController() {
parentViewController.receiveChildData(self, data: self.data)
}
}
class ParentViewController: UIViewController, ParentViewControllerProtocol {
func receiveChildData(_ child: UIViewController, data: Any) {/*handle data*/}
func addChildViewController() {
let child = ChildViewController();
child.parentViewController = self
// do the rest of adding child to parent
}
}
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.
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
}
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)
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)
}