Retrieving tag from IBOutletCollection of UIButtons - ios

I have an IBOutletCollection of UIButtons. What is the most efficient way of retrieving the tags of any index in my mutableArray of buttons.
#property(retain) IBOutletCollection(UIButton) NSMutableArray *emptySpaces;
This is how my buttons are declared
#property (strong, nonatomic) IBOutlet UIButton *position1;
Ive tried the following. What is it I'm doing wrong? Thank you
if(emptySpaces[0].tag == 1){
}
Or
if([emptySpaces objectAtIndex:0].tag == 1){
}

To answer your initial question, an id which is what NSMutableArray -objectAtIndex: returns, doesn't have a receiver named tag. You should first cast your result to a UIButton before sending the tag message. Like this: ((UIButton *)self.emptySpaces[0]).tag
You could try something like this:
for (UIButton *button in self.emptySpaces) {
if (button.tag == 1) {
}
}

Related

WKInterfaceTable how to identify button in each of the row?

I had for loop out 3 buttons in the table row and it will redirect to the related details when pressed.
Problem is how to identify which button users click? I have tried setAccessibilityLabel and setValue forKey but both do not work.
You need to use delegate in your CustomRow Class.
In CustomRow.h file:
#protocol CustomRowDelegate;
#interface CustomRow : NSObject
#property (weak, nonatomic) id <CustomRowDelegate> deleagte;
#property (assign, nonatomic) NSInteger index;
#property (weak, nonatomic) IBOutlet WKInterfaceButton *button;
#end
#protocol CustomRowDelegate <NSObject>
- (void)didSelectButton:(WKInterfaceButton *)button onCellWithIndex:(NSInteger)index;
#end
In CustomRow.m file you need to add IBAction connected to your button in IB. Then handle this action:
- (IBAction)buttonAction {
[self.deleagte didSelectButton:self.button onCellWithIndex:self.index];
}
In YourInterfaceController.m class in method where you configure rows:
- (void)configureRows {
NSArray *items = #[*anyArrayWithData*];
[self.tableView setNumberOfRows:items.count withRowType:#"Row"];
NSInteger rowCount = self.tableView.numberOfRows;
for (NSInteger i = 0; i < rowCount; i++) {
CustomRow* row = [self.tableView rowControllerAtIndex:i];
row.deleagte = self;
row.index = i;
}
}
Now you have just to implement your delegate method:
- (void)didSelectButton:(WKInterfaceButton *)button onCellWithIndex:(NSInteger)index {
NSLog(#" button pressed on row at index: %d", index);
}
If a button is pressed, WatchKit will call the following method:
- (void)table:(WKInterfaceTable *)table didSelectRowAtIndex:(NSInteger)rowIndex
Use the rowIndex parameter to decide which action should be done then.
try to put (sender: UIButton) in your IBAction like this:
#IBAction func buttonPressed(sender: UIButton)

Modifying a NSMutableArray from a TableCell

I have a TableView (named myTableView) that contains prototype cells (named: TableCell) inside a ViewController (not TableViewController, named: ViewController).
Each cell has 4 different textfields that display data from different NSMutableArrays (declared in ViewController.h and named: Legajos, Nombres, Cbus, Sueldos, Contribuciones), so for example the cell at row 0 will display the objects at index 0 from Legajos, Nombres, etc... All of this is working, I am even able to add and delete rows with no problem.
Now my problem. I have textfields (declared TableCell.h) that display text from NSMutableArrays (declared in ViewController.h), but I was only able to make it a "one way trip", that is to say, the textfields only show text from the NSMutableArrays but I also need them to modify the NSMutableArray so I can save that array and then load it.
The NSMutableArrays declaration: (ViewController.h)
#property (strong, nonatomic) NSMutableArray *Legajos;
#property (strong, nonatomic) NSMutableArray *Nombres;
#property (strong, nonatomic) NSMutableArray *Cbus;
#property (strong, nonatomic) NSMutableArray *Sueldos;
#property (strong, nonatomic) NSMutableArray *Contribuciones;
Here is how the cells get their text: (ViewController.m)
cell.legajo.text = [Legajos objectAtIndex:indexPath.row];
cell.nombre.text = [Nombres objectAtIndex:indexPath.row];
cell.cbu.text = [Cbus objectAtIndex:indexPath.row];
cell.sueldo.text = [Sueldos objectAtIndex:indexPath.row];
cell.contribucion.text = [Contribuciones objectAtIndex:indexPath.row];
And this are the IBOutlets of the cells: (TableCells.h)
#property (strong, nonatomic) IBOutlet UILabel *legajo;
#property (strong, nonatomic) IBOutlet UITextField *nombre;
#property (strong, nonatomic) IBOutlet UITextField *cbu;
#property (strong, nonatomic) IBOutlet UITextField *sueldo;
#property (strong, nonatomic) IBOutlet UITextField *contribucion;
Sorry if its hard to understand. Thanks for any help given.
Easieast way would probably be to add (void) textFieldDidEndEditing:(UITextField *)textField method to your UITextField delegate. You can either assign a tag to each UITextField upon creation so you know which array to edit or I believe NSIndexPath *indexPath = [self.tableView indexPathForCell:(CustomCell*)[[textField superview] superview]];might do the trick.
(void) textFieldDidEndEditing:(UITextField *)textField {
NSIndexPath *indexPath = [self.tableView indexPathForCell:(CustomCell*)[[textField superview] superview]];
switch(indexPath.section)
{
case '0':
// edit object in first array at index indexPath.row
[firstArray replaceObjectAtIndex:indexPath.row withObject:textField.text];
break;
case '1':
// edit object in second array at index indexPath.row
[secondArray replaceObjectAtIndex:indexPath.row withObject:textField.text];
break;
case '2':
// edit object in third array at index indexPath.row
[thirdArray replaceObjectAtIndex:indexPath.row withObject:textField.text];
break;
case '3':
// edit object in fourth array at index indexPath.row
[fourthArray replaceObjectAtIndex:indexPath.row withObject:textField.text];
break;
case '0':
// edit object in fifth array at index indexPath.row
[fifthArray replaceObjectAtIndex:indexPath.row withObject:textField.text];
break;
default :
}
}
Here is a quick example I've typed up.

Create views based on amount of items in an array

I am working on a quiz application, where I get the quiz items from an api. Right now, each question consist of an image and 4 possible answers. Currently, I have two views attached to my view controller(front view and back view), and they change to one and other, when an answer button is clicked(I am only displaying the first two questions this way).
Right now with each api call, I get 7 items(meaning 1 image and 4 answers for each item). But amount of questions can change anytime, so I am trying to implement a way to automate the view creation process.
Based on what I searched this is what I was able to come up with it:
QuizMainViewController.h
#import <UIKit/UIKit.h>
#interface QuizMainViewController: UIViewController <UIScrollViewDelegate>
#property (nonatomic,retain)NSArray *results;
#property (nonatomic,assign)NSUInteger currentItemIndex;
#property(nonatomic,assign)NSUInteger numberOfItemsIntheResultsArray;
QuizMainViewController.m
#import "QuizMainViewController.m"
#import "AppDelegate.h"
#import "Base64.h"
#import "Item.h"
#import "SVProgressHUD.h"
#import <UIKit/UIKit.h>
#interface QuizMainViewController ()
#property(nonatomic,strong)IBOutlet UIView *frontView;
#property (nonatomic,strong)IBOutlet UIView *BackView;
//first view outlets
#property (strong, nonatomic) IBOutlet UIImageView *frontViewImage;
#property (nonatomic,weak) IBOutlet UIButton *frontViewAnswer1;
#property (nonatomic,weak) IBOutlet UIButton *frontViewAnswer2;
#property (nonatomic,weak)IBOutlet UIButton *frontViewAnswer3;
#property (nonatomic,weak) IBOutlet UIButton *frontViewAnswer4;
//second view outlets
#property (strong, nonatomic) IBOutlet UIImageView *backViewImage;
#property(nonatomic,weak) IBOutlet UIButton *backViewAnswer1;
#property(nonatomic,weak) IBOutlet UIButton *backViewAnswer2;
#property(nonatomic,weak) IBOutlet UIButton *backViewAnswer3;
#property(nonatomic,weak) IBOutlet UIButton *backViewAnswer4;
#implementation QuizMainViewController
//the method that I am making the api call
-(void)loadQuizObjectsMethod
{
// this is the part I make the call and try to set the views based on if the items.count is odd or even.
operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *req, NSHTTPURLResponse *response, id jsonObject)
{
NSArray *array = [jsonObject objectForKey:#"Items"];
NSLog(#"Item array: %#",array);
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
for (NSDictionary * itemDict in array) {
Item *myResponseItems = [[Item alloc] initWithDictionary:itemDict];
[tempArray addObject:myResponseItems];
}
_results = tempArray;
//this is where I need help with
Item *item = [_results objectAtIndex:_numberOfItemsIntheResultsArray];
for (_numberOfItemsIntheResultsArray in _results) {
if (_currentItemIndex %2 == 0) {
_currentItemIndex
//implement the front view
}else if (_currentItemIndex %2 == 1)
{
//implement the back view
}
}
I appreciate your help.
You need to use UIPageViewController class in such situations. First create a class called QuestionViewController as follows
QuestionViewController.h
#property (strong, nonatomic) IBOutlet UIImageView *image;
#property (nonatomic,weak) IBOutlet UIButton *answer1;
#property (nonatomic,weak) IBOutlet UIButton *answer2;
#property (nonatomic,weak)IBOutlet UIButton *answer3;
#property (nonatomic,weak) IBOutlet UIButton *answer4;
#property (strong, nonatomic) Item *questionDetailsFromJSON;
QuestionViewController.m
-(void)viewDidLoad
{
self.image = self.questionDetailsFromJSON.image
//set all remaining outlets similar to this.
}
In the storyboard or xib embed a UIPageViewController in your QuizMainViewController using aUIContainer. Set QuizMainViewController as the delegate to the page view controller.
Change your QuizMainViewController.m as follows
#interface QuizMainViewController ()
#property(nonatomic,weak)IBOutlet UIPageViewController *pvc;
#implementation QuizMainViewController
//Implement all of the other methods
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
int index = [self.results indexOfObject:[(QuestionViewController *)viewController question]];
index++;
QuestionViewController *qvc = nil;
if(index<self.results.count)
{
qvc = //Instantiate from story board or xib
qvc.questionDetailsFromJSON = [self.results objectAtIndex:index];
}
return qvc;
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
int index = [self.results indexOfObject:[(QuestionViewController *)viewController question]];
index--;
QuestionViewController *qvc = nil;
if(index>=0)
{
qvc = //Instantiate from story board or xib
qvc.questionDetailsFromJSON = [self.results objectAtIndex:index];
}
return qvc;
}
Follow this tutorial for further details.
The other poster's suggestion about using a page view controller is good. That would let you manage the pages with questions on them in a nice clean way.
As to your question of a variable number of fields on the form:
The simplest thing to do is to build your form for the maximum number of questions. Then, when you display it, hide those buttons/images that are not used for the current question.
You can create outlets for each button/image and then manipulate them directly, or you can assign tag numbers to them. If you use tag numbers it's a little easier to write code that shows the correct number of answer buttons.
Alternatively you could write code that creates and adds the buttons to the view controller hierarchy on the fly, but creating view objects directly in code is a little more work than doing it with IB. That's more than I can explain in detail in a forum post. For that, you should probably search for a tutorial on creating buttons through code rather than IB. It end up being like 4 or 5 lines of code per button.
A rough outline of the steps: You have to create a button using initWithFrame or the UIButton buttonWithType class method. You have to add a target/action to the button. You have to set it's title. You have to set it's frame, you have to save a pointer to it in an instance variable or an array, and you have to add it as a subview to your view controller's content view.

IBOutletCollection of UIButtons is empty after viewDidLoad

As said in the title, my IBOutletCollection of UIButtons is empty after viewDidLoad.
I created a IBOutletCollection of UILabels the same way, and this one is working perfectly.
Any idea how this can be fixed, or where i made a mistake?
Here is the Code:
#property (strong, nonatomic) IBOutletCollection(UILabel) NSArray *lbl_save;
#property (strong, nonatomic) IBOutletCollection(UILabel) NSArray *lbl_cancel;
#property (strong, nonatomic) IBOutletCollection(UILabel) NSArray *lbl_edit;
#property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *btn_changeData;
#property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *btn_save;
#property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *btn_cancel;
The buttons are placed in a xib and linked correctly to the corresponding outlets. Just like the labels.
The time i press the one of the Buttons is the first time, i want to access the Buttons in Code.
for (UIButton *btn in _btn_changeData) {
btn.hidden = NO;
btn.userInteractionEnabled = YES;
}
for (UIButton *btn in _btn_save) {
btn.hidden = YES;
btn.userInteractionEnabled = NO;
}
for (UIButton *btn in _btn_cancel) {
btn.hidden = YES;
btn.userInteractionEnabled = NO;
}
for (UILabel *lbl in _lbl_save) {
lbl.hidden = YES;
}
for (UILabel *lbl in _lbl_cancel) {
lbl.hidden = YES;
}
for (UILabel *lbl in _lbl_edit) {
lbl.hidden = NO;
}
That is also where i got the following Exception and realized, that my Button OUtletcollection is empty.
-[UIButton countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance 0xa8a8850
I neither overwrite the outletcollection, nor do i change attributes of the buttons.
Its just that the labels are there in the collection and the buttons not. And i have no idea why.
Thx in advance for any help.
Mav
First idea that comes to my mind is that the properties are not correctly synthesized. Is _btn_changeData really the ivar behind btn_changeData property?
Second idea is something I saw while debugging someone else's code. When outlets are incorrectly connected, for example, if the controller references itself, two controller instances can be create. Obviously only of of them will have the outlets connected. Make sure only of instance is created.
For debugging, implementing the setter by yourself might be a good idea.
Edit:
After rereading, the problem is actually different they you say in your question. The error message -[UIButton countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance 0xa8a8850
doesn't mean that _btn_changeData is an empty array. It means there is a UIButton instead of an array.
Having said this, you should check if you are not overwriting the data in _btn_changeData somewhere.

Objective C: Modify Label with name containing string

I want to modify a label in objective C like this:
When pushing a button named "Car" I want the label "pushedCar" to unhide.
Right now it looks like this but its static...
if ([buttonName isEqualToString:#"Car"]) {
self.pushedCar.hidden = NO;}
How can I write something like:
if ([buttonName isEqualToString:#"Car"]) {
self.pushed%#.hidden , buttonName = false;}
It's kind of a stupid example but I need it for something too complex to write down here.
Thanks in advance.
Michael
Change all your button titles like, Car, Van , Bus etc.
then make this as touch up inside method of all them,
-(IBAction) buttonPressed:(UIButton *) pressedButton{
NSString *buttonName = pressedButton.titleLabel.text;
UILabel *label = [self valueForKey:[NSString stringWithFormat:#"pushed%#", buttonName]];
label.hidden = NO;
}
like this?
if ([ButtonName isEqualToString:#"Car"]) {
self.pushedCar.hidden = !self.pushedCar.hidden;
}
use KVC
id name = nil
if(myButtonCurrentTitle isEqualTo:#"Car"]) name = #"Car";
assert(name);
UILabel *label = [self valueForKey:[NSString stringWithFormat:#"pushed%#", name]];
assert([label isKindOfClass:[UILabel class]]);
If you mean "How could I get an object that is related to a string" it would probably be simplest to put the buttons into an NSDictionary so you could do something like
MyButton *button = [buttonDict objectForKey:#"Car"];
but to be honest, your example code doesn't make much sense so I'm finding it hard to know how to write a decent example for you
If I understand it correctly, you want different buttons and labels (e.g. car, horse, cat).
In that case make an NSDictionary with the key being the buttonName (#"Car") and the the object for it the label.
You can write then something like:
UILabel *label = [myDictionary objectForKey:buttonName];
label.hidden = NO; // or label.hidden = !label.hidden;
Hope this helps!
I think you are trying to use Key Value Coding to get the button instance based on some arbitrary string.
You firstly need to expose the button instances using properties:
#interface MyClass : UIView
{
// These are connected using Interface Builder
IBOutlet UIButton *carButton;
IBOutlet UIButton *busButton;
IBOutlet UIButton *bikeButton;
}
#property (string, nonatomic, readonly) UIButton *carButton;
#property (string, nonatomic, readonly) UIButton *busButton;
#property (string, nonatomic, readonly) UIButton *bikeButton;
#end
And you can get the instance of the button using:
NSString *thing = "#car";
UIButton *button = [self valueForKey:[NSString stringWithFormat:#"%#Button", thing]];
[button setHidden:YES];

Resources