What is the best way to show a message on Apple Watch independently of the active interface controller? - ios

I would like to display a local message (generated by WatchKit code) on the Apple Watch. My problem is that the message may be triggered by a code segment outside of the currently active interface controller.
What is the best way to modally present a new interface controller independent of the currently active (top-most) interface controller?
One way would be to get a pointer to the currently active interface controller. But how to do this in an easy way?
One possible solution is to use a global property that holds a pointer to the currently active interface controller. It gets updated in the willActivate methods of all interface controllers.
Another solution would be to always pass the pointer to the currently active interface controller to all methods that may want to show a message.
Is there an easier way to get a pointer to the top-most interface controller?
Well, I'm not even convinced that the above approach is a good one. What if the top-most controller is dismissed shortly after presenting the interface controller with the message? Then the message is probably not shown very long or not at all.
Is it possible to modally present the message interface controller on top of everything independent of the currently active (top-most) interface controller?
Should I use local notifications? Is there something like UIAlertView?
What is the best and easiest way to solve this problem?

There is not any UIAlertView or WKAlertView in WatchKit. You must initialize a new view controller with a text view, and pass it the string you want to show.
You can use this method to do so:
Swift:
presentControllerWithName([Name] , context: [String])
Objective-C:
- presentControllerWithName:context:

To show a message independently of the active interface controller, it is possible to present an interface controller using presentControllerWithName:context: from the main interface controller (the one with the arrow in the storyboard). This works even if the main interface controller is not currently active.
For this solution to work, it is necessary that the method that calls presentControllerWithName:context: has a pointer to the main interface controller. This can be achieved by passing self from the main interface controller to other interface controllers and methods.
However, I sometimes want to show a message from an object deep down in the hierarchy. And I do not want to pass the pointer to the main interface controller to every method that possibly wants to display a message to the user.
So, my current solution is to store a pointer to the main interface controller in a global variable (using extern) and then presenting from this main interface controller.
Acknowledgements: I would like to thank "Schemetrical" and "Mike Swanson" for the comments that solved this problem.

Related

Can you make a Swift Protocol that forces the view controller to fire specific custom NSNotifications?

This is a design pattern question.
I have a 'framework' I'm building, and depending on the current displayed ViewController the framework needs to inject a UIButton into that ViewControllers view.
I don't want the ViewControllers to 'know' explicitly about the 'framework', so I was initially using the delegate pattern but that required a 'over-seer' to ensure that when only specific view controllers were loaded, that they knew about the 'framework'.
However, it occurred to me if I could, via a protocol, ensure that all delete ViewControllers fired off the same two custom NSNotifications i.e. subscribeToRequestButton and unsubscribeForButton, then the 'framework' would listen out for those, and upon receiving them, have the view controller object passed to it (via the Notification) so that it can inject the button.
Thereby preventing the view controller from knowing about or having a reference to the 'framework'; it just knows, if it want's that button, to fire those two notifications, and ensure that it implements a method for when that button is touched.
Is this possible or is there a better approach?
The key problems are that
a) I cannot have the View Controllers that need the button know about the framework; as they never have the opportunity to have a reference to is passed to them by some manager class, and
b) The framework doesn't know about the view controllers existence until it receives a notification that is needs to inject a button into something.
EDIT:
I'll just use a singleton pattern with a few public accessor methods for passing data in or querying.

write something to already opened view, when coming back from the modal view

Edit something in the parent view, when moved from the child modal view. Should I use "viewWillAppear" or some other pre-defined functions for the view to be appeared in foreground..
There are actually plenty of ways to achieve what you want.
One would be checking on viewWillAppear,
another way is to create protocol, and call some protocol method when child is going to be dismissed.
Also, you could have a property in the child class to hold object
reference to the parent, and when being dismissed call some method
on parent to notify the parent that the modal is being dismissed.
You could use NotificationCenter, and post a notification, and
handle the notification on the parent to update it, as well.
I don't know which one is the most suitable for you, if you give more context to the problem, I could clarify the answer. Good luck!
Edit:
Here is the official Apple documentation;
Here you can find information about how to use the notifications and notification center.
You can search the Google for more on Notifications and NotificationCenter.

Present View Controller without being on a View Controller - Swift / Objective-C

I'm creating an app that implements a Facebook and a Twitter service. In my view I have a button that toggles sharing on Facebook/Twitter or not. If Facebook/Twitter aren't connected, then the button will show "connect to".
When I click the button, a method in my controller (not my viewcontroller) will try to toggle the value because this controller tracks the state of my app. When I'm not connected to a social network my controller will notice and will call the correct service. This service will start a webview to provide the user credentials.
And now my question:
When I'm in my service and I need to provide credentials via a webview. When I want to show this webview I need to pass a View Controller that will handle the presenting. How do I provide a viewcontroller here?
Approaches I found:
Call the appdelegate singleton and get the current presenting
viewcontroller (I find this dirty but correct me if I'm wrong).
Since I'm injecting my service into my controller in
appdelegate.didFinishLaunchingWithOptions I could inject the UIWindow
of the appdelegate and ask for the current presenting viewcontroller
(Is almost the same as the first approach)
Create a protocol implemented by a viewcontroller. The service has a property that is equal to that protocol and in my app delegate inject the
viewcontroller into the service.
Have a property in your controller
that will be the presentingviewcontroller and let your controller
implement the approach #3 protocol. When a function of that protocol
is fired, the controller will handle it and use the
presentingviewcontroller property for the webview. I only need to
inject a viewcontroller into my controller in the appdelegate.
Don't implement anything view related in that service.
My current implementation is approach #3.
If some things are not clear, please let me know.
Any help is appreciated!
Thanks in advance!
I was hoping this question would of got more attention, I was interested to know how other people would handle this situation.
As you have already stated, there are a few ways to achieve what you need, but the answer depends on having knowledge of the application.
I would definitely try to keep business logic and UI as separate as possible, there are two methods that I can think of to do this, but they are pretty much the same thing.
Make the (non UI) controller responsible for the login check with a callback function, you can then leave the controller responsible for business logic and the calling ViewController responsible for rendering any UI as a result of that check.
As you suggested, using protocols, your controller could have a delegate that will be a ViewController that conforms to your protocol. Then usage would just be getting your shared instance, setting the delegate and calling the required fuctionality.
I would likely favor option 2 to be more Swift-like as it is a protocol orientated language.
If the controller is acting globally and accessed from anywhere within the application you could potentially end up duplicating a lot of code to make these checks, you could get around this by creating an extension of UIViewController which provides the functionality to interact with the controller.
I hope this helps, Would definitely be interesting to see the way other people would approach this.

How can I set the initial controller on storyboard contains WKInterfaceController programmatically?

I want to have few view controllers on the Watch app storyboard. The issue is that I want to load different initial view controller depending the target I'm building for.
I can see there is a single value called "is initial controller" on the WKInterfaceController but I can't find a way to set the initial view programmatically. I tried using other methods supplied but all are pushing the second view on top of the original initial which is not what I want (you have a back button on top bar).
If you have any way you can think of supporting this that's great. Creating a different storyboard file is last option.
Actually, the solution is really simple. It is true that the pages are being built before everything I had to rebuild all of it at run time. More that the flag (Is initial Controller) couldn't be changed at run time but you sure can build all from scratch.
I set the identifier of the WKViewController to "exampleViewControllerName" (on the StoryBoard) and add this code to awakeWithContext:context
NSArray *array1=[[NSArray alloc] initWithObjects:#"exampleViewControllerName", nil];
[WKInterfaceController reloadRootControllersWithNames:array1 contexts:nil];
You sure can use the context and make that more complicated but that's the basic of it, and good enough for the simplest answer possible.
What you are actually asking is how can you control the initial view controller of a watch kit app.
From the Apple Watch Programming Guide
All interface controllers in a page-based interface are created and
initialized before the interface is displayed, but only one interface
controller at a time is displayed. Normally, WatchKit displays the
first interface controller in the sequence initially. To change the
initially displayed interface controller, call the becomeCurrentPage
method from its init or awakeWithContext: method.
If you are using a hierarchical interface rather than a page-based interface then your app will always start with the same initial view.

Accessing linked Segues created in a Storyboard

I am trying to create a class that is similar in functionality to the UITabBarController, but with some fundamentally different functionality. It is called a dropdownViewController and has a primary content view with a UITabBar-like interface at the top of the screen that allows for other UIViewControllers to be modally presented and dismissed over this primary viewController.
I would like this class to be able to be set up using the storyboard to some extent, and I have created a custom Segue that connects my dropDownViewController class with each of its child viewControllers.
My current solution is to assign identifiers to each of the Segues that are then stored in array within the dropdownViewController. I can call the segues programmatically using the performSegueWithIdentifer: method, but this solution isn't as flexible or intuitive as I would like to to be.
Right now, all the custom Segues that I have setup are connected to the "manual" triggered segue in the storyboard connections panel for the dropdownViewController. (I would put screenshots but this is my first post)
However, I want to mimic the functionality of the UITabBarController class, which has an alternate triggered segue in the storyboard connections panel called viewControllers that each of its child views are assigned to. Unless there are some compile-time macros handling these story board interactions, I assume that the UITabBarController uses these connections to determine what it's view controllers are. However, I can't figure out how to setup this functionality with my own class
After searching around for a solution, it seems likely that this is functionality Apple kept for its own use and is limited to their own classes as a feature in Xcode, but if anyone has solutions or ideas it would be greatly appreciated.
I haven't tried this, but I think you should be able to do it with your own custom segues. In the perform method, you would just add the destination view controller to the source view controller's (DropDownViewController) array of view controllers. In the DropDownViewController's viewDidLoad method (or maybe in an initializer or awakeFromNib, not sure which is most appropriate), you would execute all these segues so that they run right after the controller is loaded like is done for a tab bar controller.

Resources