9

I'm trying to change the title of a button after I call back from a notification but it doesn't respond at all. I checked it's not nil and checked the text Im' assigning and all is good. I made the property type strong instead of weak but no success.

- (void) setButtonTitleFromSelectedSearchResult:(NSNotification *)notif
{

    [self popController];

    self.sourceMapItem = [[notif userInfo] valueForKey:@"SelectedResult"];

    NSLog(@"The Selected Result is: %@", self.sourceMapItem.name);

    //Testing
    NSLog(@"%@", self.fromButton); // check it's not nil

    [self.fromButton setTitle:self.sourceMapItem.name];
}
Ali
  • 4,205
  • 4
  • 25
  • 40
  • 1
    Just to clarify: the method is indeed being called, and you see output in the console from the two `NSLog` statements? Also, what does `popController` do? It sounds like it's dismissing the current interface controller so changing the buttons won't happen anyway. – gregheo Apr 04 '15 at 04:04
  • Yes, the above code is called and everything runs properly giving the results of NSLogs correctly but the button doesn't change its title. The `[self popController]` pops a controller that was presented previously where the users chooses an option and that's where the notification is posted while the current controller (where the `self.fromButton` is) pops that controller and then should change the button's title according to what the user has chosen. Don't worry about it, even when it's not there the button doesn't change its title. – Ali Apr 04 '15 at 13:55

4 Answers4

4

With WatchKit, if a user interface element isn't currently visible, it cannot be updated. So, if you've presented another interface controller "on top", you can't update any of the presenting controller's interface elements until you've dismissed the presented controller. At that point, you can safely update the presenting controller in its willActivate method.

SushiGrass' method of passing blocks is certainly one valid approach. In my testing, however, I ended up having to manage multiple blocks, and many of the subsequent blocks reversed what earlier queued blocks had accomplished (for example, first changing a label's text to "foo", then "bar", then "foo" again. While this can work, it isn't optimal.

I'd suggest that anyone who is working on a WatchKit app takes a moment to consider how they want to account for off-screen (i.e. not-currently-visible) interface elements. willActivate is your friend, and coming up with a way to manage updates in that method is worthwhile if you're moving from controller to controller.

For what it's worth, I've encapsulated a lot of this logic in a JBInterfaceController subclass that handles a lot of this for you. By using this as a base class for your own interface controller, you can simply update your elements in the added didUpdateInterface method. Unfortunately, I haven't yet had the time to write proper documentation, but the header files and sample project should get you going: https://github.com/mikeswanson/JBInterfaceController

Mike Swanson
  • 3,337
  • 1
  • 17
  • 15
  • Could you please give the document about "With WatchKit, if a user interface element isn't currently visible, it cannot be updated."? – allenlinli Dec 22 '16 at 02:09
3

I'm using latest XCode 6.3 and below code working with me. self.testBtn is bind with Storyboard and its WKInterfaceButton

I also have attached screenshot with affected result.

I'm setting initial text in - (void)willActivate

- (void)willActivate {
    [super willActivate];
    [self.testBtn setTitle:@"Test"];
    [self performSelector:@selector(justDelayed) withObject:nil afterDelay:5.0]
}

-(void)justDelayed
{
    [self.testBtn setTitle:@"Testing completed...!!"];
}

enter image description here

enter image description here

Ashvin
  • 8,227
  • 3
  • 36
  • 53
  • Can u try pushing another interface controller and form that controller Post a notification where the 1st controller will respond to it by dismissing the 2nd controller and then changing the button text like what you see in my code? – Ali Apr 16 '15 at 15:37
2

If you're using an IBOutlet for the property fromButton be sure that is connected to WKInteface on the storyboard, like below:

IBOutlet

Andr3a88
  • 679
  • 5
  • 13
  • It's connected and it responds to changing the title twice before this 3rd attempt where nothing happens. – Ali Apr 02 '15 at 11:09
  • 1
    Unfortunately I cannot understand where the problem is, you have to be more specific with the code. The 3rd times the notification is received? Are you sure that `[self.fromButton setTitle:self.sourceMapItem.name];` is performed in the main thread? – Andr3a88 Apr 02 '15 at 12:03
  • I mean the button changes its title properly elsewhere other than when coming from a response to NSNotification (code above). I check for main thread using `if ([NSThread isMainThread]) { NSLog(@"is main thread"); }` And it's actually main thread. I tried changing the button's title inside `dispatch_async(dispatch_get_main_queue(), ^{ [self.fromButton setTitle:self.sourceMapItem.name]; });` but no success either. – Ali Apr 04 '15 at 13:50
  • 1
    With WatchKit, if a user interface element isn't currently visible, it cannot be updated. You'll have to account for that if you've pushed another interface "on top." Then, when the base interface controller's willActivate is called, you can perform the update. – Mike Swanson Apr 21 '15 at 05:33
  • @MikeSwanson I verified that and what you said is correct. My notification call back (code above) is called before `willActivate` is called. I just don't know how to fix that. I downloaded your github code and honestly it's above my level and I don't know how could I use it to solve my problem. – Ali Apr 22 '15 at 14:48
  • I solved the problem by using a Boolean set to `NO` by default and change it to `YES` in the notification call back then to know when I'm coming from the pushed controller I check for the Boolean in `willActivate` and if it is `YES` I change the button title in `willActivate` instead of in the notification call back. @MikeSwanson Could you make your comment an answer so I can accept it? – Ali Apr 22 '15 at 15:15
  • 1
    Glad to hear that you solved the problem! I've elevated my comment to an answer and elaborated a bit so that it's more useful. – Mike Swanson Apr 22 '15 at 16:03
2

I solved this kind of issue by creating a model object that has a property that is a block of type () -> (Void) (in swift). I create the model object, set the action in the block that I'd like the pushing WKInterfaceController to do on completion, and finally pass that model object in the context to the pushed WKInterfaceController. The pushed WKInterfaceController holds a reference to the model object as a property and calls it's completion block when it's done with whatever it needs to do and after func popController().

This worked for me for patterns like what you are describing along with removing rows on detail controller deletion, network calls, location fetches and other tasks.

You can see what I'm talking about here: https://gist.github.com/jacobvanorder/9bf5ada8a7ce93317170

SushiGrass Jacob
  • 19,425
  • 1
  • 25
  • 39
  • This sounds interesting but could you please post the code so I can try it? Preferably Objcetive-C. Thanks. – Ali Apr 21 '15 at 16:18
  • I tried a technique like SushiGrass' and it worked well for simple updates. The more complex updates, however, required multiple blocks, and that became cumbersome. I ended up writing a WKInterfaceController subclass that defers screen updates until a view is actually visible. You can see an example in my GitHub project: https://github.com/mikeswanson/JBInterfaceController – Mike Swanson Apr 21 '15 at 20:31
  • @mikeswanson, this is pretty solid! Thanks for a sharing. Lots of good little nuggets of information here. – SushiGrass Jacob Apr 21 '15 at 20:44
  • @SushiGrassJacob I can't open the gist link. Just a blank browser page. Should I do something special to open it? – Ali Apr 22 '15 at 14:23
  • @Ali, try it now. Sorry! – SushiGrass Jacob Apr 24 '15 at 13:31
  • @SushiGrassJacob Thank you for sharing this code. I solved the problem by using a boolean however as explained in my comment to MikeSwanson below. – Ali Apr 25 '15 at 13:27