Can't set WKInterfaceButton title in table - ios

I am developing a simple Watch App. I have an InterfaceController with a table. In that table, every row has a WKInterfaceButton. I have created a NSObject class with the button of the Table Row Controller, and created the outlet of the button from the interface to the NSObject class (called SuggestionMessageRow). I also gave an identifier to the Table Row Controller (MySuggestionRow). All the outlets and the storyboard stuff are setup.
Now, in the interface controller, I do the following:
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
_suggestionsArray = [NSArray arrayWithObjects: #"object1", #"object2", #"object3", #"object4", #"object5", nil];
[self loadTable];
}
- (void)loadTable{
[_myTable setNumberOfRows:_suggestionsArray.count withRowType:#"MySuggestionRow"];
for(int i = 0; i < _suggestionsArray.count; i++){
NSObject *row = [_myTable rowControllerAtIndex:i];
SuggestionMessageRow *myRow = (SuggestionMessageRow *) row;
[myRow.myMessageButton setTitle:_suggestionsArray[i]];
[myRow.myMessageButton setBackgroundColor:[UIColor cyanColor]];
}
}
SuggestionMessageRow:
#import <Foundation/Foundation.h>
#import <WatchKit/WatchKit.h>
#interface SuggestionMessageRow : NSObject
#property (weak, nonatomic) IBOutlet WKInterfaceButton *myMessageButton;
#end
This does not change the buttons title inside the table rows (they keep the title set on the storyboard button). HOWEVER, it sets the background color to cyan or whatever color I set, which tells me all the connections, outlets and so are correctly set up.
Any idea? I've checked in Apple Documentation but I have not found anything, feels like a silly/tricky problem :(. I tried to load the table on willActivate method but got the same result.

Found the problem, posting it just in case someone has the same issue. There was a lost/disconnected outlet of another button that I was not using anymore. Once I solved that, it works.

Related

best way to message a different object in objective-c

I have a UITableView and inside each header, I have a gesture recognizer. Inside the gesture handler, I want to make changes to something inside the headerview.
-(void) sectionHeaderButtonPressed: (UIButton *)sender{
NSLog(#"Header Button pressed");
//UIView *mySubView = [sender.view.subviews objectAtIndex:0];
NSLog(#"Class: %#",[self class]);
int count = [self.view.subviews count];
NSLog(#"Self.view.subviews: %u",count);
Class buttonClass = NSClassFromString(#"UIButton");
for (int i=0; i < count; i++){
int subViewCount = [[self.view.subviews objectAtIndex:i] subviews].count;
Class className = [[self.view.subviews objectAtIndex:i] class];
NSLog(#"SubView (%u) Class:%# SubViews: %u",i,className,subViewCount);
for (int j=0; j < subViewCount; j++){
Class className01 = [[[self.view.subviews objectAtIndex:i].subviews objectAtIndex:j ] class];
NSLog(#"----SubSubView (%u) Class:%#",j,[[[self.view.subviews objectAtIndex:i].subviews objectAtIndex:j ] class]);
NSLog(#"Comparing Class: %# to Class: %#",className01, buttonClass);
if (className01 == buttonClass){
[[[self.view.subviews objectAtIndex:i].subviews objectAtIndex:j ] setTitle:#"ABCDE" forState:UIControlStateNormal];
NSLog(#"found button class");
}
}
}
}
The above code is the gesture handler and I can find the button and every other subview, but this is a real hassle.
I seem to be confused about how one object can call another object. I wanted to pass the headerview down to the gesture handler but can't figure out how to do that.
How can I directly send or access the headerview and/or it's subviews. Looking at the class name won't do much good if I have several of the same class type.
The question is about the proper way to invoke an action in another object. Running thru all the subviews doesn't seem to be the proper way. Basing the id on the class name is an error prone way to go as you might have many of the same class.
I want to have a "slide in from the side" selection menu and have that modify the header based on what the user selects from the menu. They'll tap on the header, that'll call the gesture handler, that'll call the slide out menu, the slide out menu will call the header to make it change it's contents.
It's the last part that's confusing, how do I get the menu to change the header view contents when they're completely different objects.
The best approach for this would be to use a delegate, and let your ViewController do te heavy lifting for you. Some examples on how to create a delegate can be found here.
Create a custom view with a custom delegate protocol
The first thing you would need to do is create a subclass of the HeaderView you want to implement. As you already said yourself, your code is becoming kind of a hassle, and this would be a good opportunity to create a custom view.
In this custom view, recreate your header as you want, and hook up some labels/buttons either using interface builder, or programmatically (remember to change all weak pointers for UI elements to strong if you do this programmatically, I assumed you will use interface builder)
Your .h should look something like this:
#import <UIKit/UIKit.h>
#protocol CustomHeaderViewDelegate;
#interface CustomHeaderView : UIView
#property (nonatomic, weak) UILabel *someLabel;
#property (nonatomic, weak) UILabel *someButton;
#property (nonatomic, weak) id<CustomHeaderViewDelegate> delegate;
#end
#protocol CustomHeaderViewDelegate <NSObject>
- (void)customHeaderView:(CustomHeaderView *)customheaderView receivedTouchOnButton:(UIButton *)button;
#end
and your .m file:
#import "CustomHeaderView.h"
#implementation CustomHeaderView
- (IBAction)buttonPressed:(id)sender {
if (self.delegate) {
[self.delegate customHeaderView:self receivedTouchOnButton:sender];
}
}
#end
Make the viewcontroller conform to the delegate protocol
In your view controller you want to implement the delegate protocol from your newly created CustomHeaderView. Start by making sure your viewController actually conforms to the newly created protocol by adding <CustomHeaderViewDelegate> to the interface declaration in your viewcontroller's .m file. It should look something like:
...
#import "CustomHeaderView.h"
#interface YOURVIEWCONTROLLER () <CustomHeaderViewDelegate>
...
Now somewhere in your viewController you can add the following method:
- (void)customHeaderView:(CustomHeaderView *)customheaderView receivedTouchOnButton:(UIButton *)button {
[button setTitle:#"ABCDE" forState:UIControlStateNormal];
}
Adding your header to the tableView
In your viewForHeaderInSection code do something like:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
CustomHeaderView headerView = [[CustomHeaderView alloc] init];
headerView.delegate = self;
/* do some more customisation */
return headerView;
}
Finding your button
If you want to find your button, or some other view in a section header, you can simply do the following:
CustomHeaderView *customHeaderView = (CustomHeaderView *)[self tableView:YOURTABLEVIEW viewForHeaderInSection:SOMESECTION];
customHeaderView.button;

Get current UIViewController from UIButton's class

I have custom UIButton which programmatically interacts (triggers) with ViewController methods via protocol. But the behaviour of the button has to be dependent on the ViewController placed on. I do this to minimise amount of code in ViewControllers itself, as the button has to remain the same and bear the same functions (navigation).
Is there any way in UIButton's custom class to get the ViewController it is placed on?
I'd follow #rmaddy advice in a specific way, borrowing from the SDK's style
// MyCutomButton.h
#protocol MyCustomButtonDatasource;
#interface MyCustomButton : UIButton
#property(weak,nonatomic) IBOutlet id<MyCustomButtonDatasource>datasource;
// etc
#end
#protocol MyCustomButtonDatasource <NSObject>
#optional
- (NSString *)howShouldIBehave:(MyCustomButton *)button;
#end
Now the button can have it's datasource set in IB. View controllers that include it will need a little additional code (sorry, it's unavoidable in a good design). They will declare themselves as implementing MyCustomButtonDatasource.
When MyCustomButton needs to behave conditionally based on where it's placed, it can ask its datasource...
// MyCustomButton.m
NSString *string = #"defaultBehavior"; // per #RichardTopchiy's suggestion
if ([self.datasource respondsToSelector:#selector(howShouldIBehave:)])
string = [self.datasource howShouldIBehave:self];
// string is just made-up here, have it answer something simple (int, BOOL)
// that lets the button proceed with the right behavior. Don't ask for
// anything that relies on specific knowledge of how MyCustomButton
// is implemented
EDIT - To create the relationship, if you've decorated the property as an IBOutlet (as shown above), you should be able to setup the relationship in IB. Declare your view controller as implementing <MyCustomButtonDatasource>. Select your custom button, then the connections inspector, then drag to your view controller.
Alternatively, make the button itself an IBOutlet property in the view controller and, in viewDidLoad, do:
self.customButton.datasource = self;
The last way to do it is give your button a tag, say, 128, then:
MyCustomButton *customButton = (MyCustomButton *)[self.view viewWithTag:128];
self.customButton.datasource = self;

How get data from View to UITabBarController

I have a UITabBarController, which has 3 UIViewControllers. Each controller has a few UITextFields. In UItabBarController is a button "save". Please, how can I get data from UITextFields in UIViewControllores? I need the data from all UITextFields and from all UIViewControllers in UITabBarController (in IBAction of save button). How can I do it? Thanks.
Here is picture of my TabBarController on Storyboard.
The first ViewController has a class StudentVC. The class has one text field. In StudentVC.m is this code:
#import <UIKit/UIKit.h>
#import "CoreDataHelper.h"
#interface StudentVC : UIViewController <UITextFieldDelegate>
#property (strong, nonatomic) NSManagedObjectID *selectedStudentID;
#property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
#property (strong, nonatomic) IBOutlet UITextField *studentNameTextField;
#end
The studentNameTextField is connected with field in Storyboard. Then I have a class for TabBar the class name is StudentTBC. In file StudentTBC.m I have this code:
- (void)viewDidLoad
{
if (debug == 1) {
NSLog(#"Running %# '%#'", self.class, NSStringFromSelector(_cmd));
}
[super viewDidLoad];
StudentVC *studentVC = [self.tabBarController.viewControllers objectAtIndex:0];
studentVC.studentNameTextField.text = #"asad";
}
So, when I go on TabBar I will see in studentNameTextField the text "asad", but the field is clear... So I added also in IBAction:save this code:
- (IBAction)save:(id)sender {
if (debug == 1) {
NSLog(#"Running %# '%#'", self.class, NSStringFromSelector(_cmd));
}
StudentVC *studentVC = [self.tabBarController.viewControllers objectAtIndex:0];
NSString *string = studentVC.studentNameTextField.text;
NSLog(#"string:%#", string);
But when I insert the field and press "ulozit" (its save in czech), I get a "string: null" instead of a "string: textWhichIWriteToField". I tried indexes 0,1,2 but nothing works. :/
Edited 3.4 15:20
I did a move of my application. In first part is my storyboard - UITabBarController and 3UIViewControllers. I deleted a Navagition Controllers. In second part I do again a Outlets. Third part is code of save function (the code could be write a content of textfield) and code of ViewDidLoad of TabBarController (code could be set a string of textfield). The last part is run application (the result is in console - null string and clear textfield).
I also tried print this:
NSInteger countOfViews = [self.tabBarController.viewControllers count];
NSLog(#"count:%ld", (long)countOfViews);
It has to be three? or? Because my result was 0.. Why? :/
My movie: https://www.youtube.com/watch?v=j6p__e48lLI&feature=youtu.be
EDITED 2.4 20:58 -------------------
Hello. I tried all options but nothing worked.. So I did a new SIMPLE application, where is only one UITabBarController with two UIViewControllers and one UITableViewController (because of navigation bar). Please download the project and try it yourself. Only run, then click on button "TabBar" and you are on the UITabBarController. When you press a Save button, it should display a count of UIViewsControllers and the value of textfields. Could you please try and tell me what's wrong? Thank you. Here is the project:
https://www.dropbox.com/s/r4jlzadr2us00ad/TBC.zip
To access to one of text field you can use code below:
- (IBAction)save {
YourUIViewController *vc = [self.tabBarController.viewControllers objectAtIndex:indexOfController];
NSString *str = vc.yourTextField.text;
}
EDIT. You have me how your controllers are connected and now i realized you got UINavigaitonController after UIViewController. Modified code should look like:
- (IBAction)save {
NSArray *navConArr = [self.tabBarController.viewControllers objectAtIndex:indexOfController];
for(UINavigationController *nc in nacConArr) {
UIViewController *vc = [nc.viewControllers objectAtIndex:0];
NSLog(#"And your text is = %#", vc.studentNameTextField.text);
}
}
Updated
I solved the problem finally... The right code for TabBarController
- (IBAction)save {
YourUIViewController *vc = [self.viewControllers objectAtIndex:indexOfController];
NSString *str = vc.yourTextField.text;
}
Please check outlets for UITextFields,
Also check output for
NSLog(#"StudentVC textField -- %#",studentVC.studentNameTextField); in save()
if it is also null, then there is definitely problem with your outlets.

How to bind the control on the form with variable

I am just start studying iOS developing watching Stanford iOS course, but it looks like I have already missing something.
I have a form with UILabel and UIButton. When an user press the button the title of the button must be added to the text of label.
Here is my current CalculatorViewController.h:
#import <UIKit/UIKit.h>
#interface CalculatorViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *display;
#end
and here is a CalculatorViewController.m:
#import "CalculatorViewController.h"
#implementation CalculatorViewController
#synthesize display = _display;
- (IBAction)digitPressed:(UIButton *)sender {
NSString *digit = [sender currentTitle];
UILabel *myDisplay = self.display;
myDisplay.text = [myDisplay.text stringByAppendingString:digit];
}
#end
The problem is that self.display (and myDisplay) variables have a nil value. Looks like I need to do something to link my variable with control on the form. What ?
Thanks in advance.
You need to link the control, the UILabel in Interface Builder to the variable in your CalculatorViewController class.
It is very likely that the file's owner (talking about the Xib file) is your CalculatorViewController, so you need to Control+drag the file's owner (or the object representing your VC) to the control and you will be shown a menu with the possible IBOutlet variables declared in your class, so you select the one you want to represent the control.
You can check if this link is properly set in two ways: (1) Select the UILabel in Interface Builder and see if there's a connection to the variable in the Connections Inspector, or (2) In your code you'll see a bullet near the IBOutlet declaration, if the bullet is hollow the connections is not set, if the bullet is filled the connection is set.
There is no need of this line
UILabel *myDisplay = self.display;
You have already declared your label in your interface file
- (IBAction)digitPressed:(UIButton *)sender
{
NSMutableString *string = [myDisplay.text stringByAppendingString:sender.titleLabel.text];
myDisplay.text = string;
}

Strongly Attributed Property in Master View Becomes Null After Callback Delegation from Detail View in Split View Controller

I have a Split View Controller with the master view containing a Tab Bar Controller, whose four tabs are each controlled by separate "MasterViewController" objects, which delegate to the detail view:
The MasterViewController class is as follows:
#interface MasterViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate>
#property (strong, nonatomic) IBOutlet UISearchBar *searchBar; // Not pictured above
#property (nonatomic, strong) NSMutableArray *displayData;
#property (nonatomic, strong) NSMutableArray *auditedData;
#property (strong, nonatomic) IBOutlet UITableView *tableView; // Each of the tabs contains a tableview
#property (strong, nonatomic) IBOutlet UITextField *itemCounter;
#end
and is initialized as follows:
MasterViewController *blahMasterController = (MasterViewController *)tabItem;
blahController.displayData = [[NSMutableArray alloc] init];
blahController.delegate = detailTableViewController;
blahDetailController.recallDelegate = blahController;
Functionality is such that when a user selects a table row in one of the tabs, the relevant data is displayed in the detail view controller. Then, when the user taps a button on the detail view controller, the entry in the master view tab is removed.
The former works well...the later is behaving strangely. Here is my "respondToDetailViewButton" method in the MasterViewController (the method delegated to by the detail controller):
- (void)respondToDetailViewButton
{
NSLog(#"Class: %#: %#", [self class], self);
NSLog(#"TableView: %#", self.displayMachinesTableView);
NSLog(#"Search Bar:%#", self.searchBar);
}
and its output:
Class: MasterViewController: /MasterViewController: 0x105b10a0/
TableView: /UITableView: 0xe960c00; frame = (0 0; 320 395);
clipsToBounds = YES; autoresize = TM+BM; tag = 1; gestureRecognizers =
; layer = /CALayer: 0x114b9950/; contentOffset:
{0, 0}/ Search Bar: (null)
From the above you can see that when the detail controller delegates back to its master, the searchBar reference, despite being strongly attributed, becomes null despite the references to MasterViewController's tableView remaining intact.
And the searchBar is verifiably non-nil at some point because in the MasterViewController object, the following call in didSelectRowAtIndexPath:
currentScope = [searchBar.scopeButtonTitles objectAtIndex:searchBar.selectedScopeButtonIndex];
NSLog(#"%#", currentScope);
renders "New" or "Old" or "Blah" depending on the scope I have selected.
Does anyone have an idea as to why the searchBar reference becomes null based on what I have provided? Many thanks in advance!!
Based on the comment conversation under the question one possibility is that inconsistent variable naming might be the problem. It looks like you might have "#synthesize idSearchBar = searchBar;" in your implementation so self.idSearchBar and searchBar are effectively the same thing.
The problem with this is a local variable called searchBar somewhere else in your class could overwrite your instance variable. So try this: Change the synthesize line to "#synthesize idSearchBar = _idSearchBar;" or remove the #synthesize line completely and let the compiler auto-generate that exact line for you (if using LLVM compiler 4.0). Change all searchBar usage that show up as an error to "self.idSearchBar" to be consistent throughout your class. Any leftover uses of searchBar will be local variables that should not be changed.
After that - do you still see the problem?

Resources