I currently have an array of buttons being generated which works great. Outside of this on touch up inside each button calls on a -(void)onTouch function where some math is done to determine an action. This all works great except I would like to store a history of the pressed buttons. I've tried many different ways to create an NSMutableArray and store the values of the pressed buttons, but because I can only declare the array within the -onTouch action, every time a button is pressed the array is reset so it never remembers more than one move. If I try to declare the array in my header and synthesize it outside I either get the error that nsmutable array is not a compile time thinger or it doesn't store anything (log output is "(null)". Can someone paste in some code on how to declare an array that can store and append the uibutton tags outside of where the uibutton press event happens? I'll post code later tonight if this isn't clear.
Cheers
You need to not only declare the array, but also initialise it. If you don't initialise, you won't necessarily get a warning, but you will get lots of nil data.
You only want to initialise the array once (as you have noticed) so viewDidLoad is a good place to do it. Another good place is in a custom accessor...
- (NSMutableArray*)historyArray
{
if (!_historyArray) {
_historyArray = [[NSMutableArray alloc] init];
}
return _historyArray;
}
Now the first time you try to [self.historyArray addObject:sender], the accessor will note the absence of a historyArray, create one and return it. Next time round it won't be recreated as it already exits.
#property(nonatomic,retain)NSMutableArray *tapCollection;
-(void)viewDidLoad{
self.tapCollection = [[NSMutableArray alloc] init];
}
-(void)onTouch:(id)sender{
UIButton *btnTapped = (UIButton *)sender;
[self.tapCollection addObject:[NSNumber numberWithInt:btn.tag]];
}
Related
Here it is: I am working with some legacy code. So I can't really (feasibly) change the architecture at this point.
I have one file that creates an array of strings, in VC1:
NSMutableArray *arrButtons = [NSMutableArray array];
[arrButtons addObject:data];
[arrButtons addObject:share];
[VC2 showButtons:arrButtons];
Then on my VC2 code , I have:
-(void)showButtons:(NSMutableArray *)arrButtons {
for (int i=0; i<arrButtons.count; i++) {
UIButton *btn = [_popupView viewWithTag:i+5000];
[btn setTitle:[arrButtons objectAtIndex:i] forState:UIControlStateNormal];
//this is the code I am trying out, I just need to addtarget to data, not the rest of the array.
if ([arrButtons containsObject:data]) {
//this is adding to all buttons, not just data. Figure out a way to add this action to only data.
btn.[index: data]
[btn addTarget:self action:#selector(arrayButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
}
}
It would make sense to just add the target in VC1 when we first add it to the array. BUT I can't because when it is created, it is just a string.
I'd like to point out that the button DOES show up on screen. But I don't know how to access that specific button in the array to add a target to it.
The best solution I can come up with is that I need to addTarget but if anyone else has any pointers or ideas on how something like this can be resolved, I would really appreciate it.
p.s. I know how to connect IBActions from IB, the problem is this is a button created 100% programmatically, and when created, it is really just a string, not a button. So addTarget is not available.
I think that the problem in VC2 is in:
if ([arrButtons containsObject:data]) {
If I'm understanding this correctly, arrButons will always contain the data string. Assuming that data is the variable representing title of the button you want to add a target to, I'd put this instead:
if ([arrButtons[i] isEqualToString: data]) {
Please let us know how it goes.
I'm a beginner developer for iOS. I'm using objective c to develop an app. I want a button to be able to read a string from an array. What I mean by this is NOT to set the string as what the button displays. I want it to be able to get the string so I can use AVSpeechSynthesizser to read the string aloud. Thanks in advance
You don't provide any code or details about your problem.
I have to make assumption that you just want to read something from array while button is tapped.
Either using storyboard to create the button object and its handler or manually add the handler.
Let's say you have the button object named 'exampleButton', if you choose manually add the handler,
[exampleButton addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
Let's say your array name is exampleArray, and you want to access the first element.
EDIT:
use firstObject instead objectAtIndex:0 since the latter one will crash the app if the array is empty.
- (IBAction)buttonTapped:(id)sender {
// becareful, if the array is empty, firstObject will return nil.
// If you use [exampleArray objectAtIndex:0], it will crash
id obj = [exampleArray firstObject];
if ([obj isKindOfClass:[NSString class]]) {
NSLog(#"%#", obj); // now you have the string object.
}
}
You have to learn more if you still cannot get yourself started with above code.
In my IOS app I have a button that the user can assign a custom action to, but I don't know what the best way is to dynamically set actions to the button. My first idea was to change the [sender tag] of the button, and then after being pressed it will read a series of "if" statements that say "if (sender tag=1){then do this} if(sender tag=2){then do this}, and so on." But the problem with this is that i will have thousands of possible actions, so if someone chooses an action who's sender tag is 2000, then the method will have to read through every single question before it reads the "if (sender tag=2000)", which is a waste of time. How can I dynamically set an action to a Button?
You have lots of options. Buttons support target/action, so you can change the button's action using
removeTarget:action:forControlEvents: and addTarget:action:forControlEvents:.
Or you could use tags as you say. Tags are numeric, so you could use a switch statement rather than a chain of if/then/else. Better yet, though, would be to use an NSArray of blocks and invoke the appropriate block for the user-selected action.
Example:
In your header:
typedef void (^myButtonBlock)(id sender);
In your implementation:
myButtonBlock aBlock = ^(UIButton *sender)
{
NSLog(#"Button block triggered for button with tag %d", sender.tag);
}
NSMutableArray *blocksArray = [NSMutableArray new];
[blocksArray addObject: aMethod];
okay below is a standard example of creating a datepicker
- (void)viewDidLoad {
CGRect pickerFrame = CGRectMake(0,250,100,100);
UIDatePicker *myPicker = [[UIDatePicker alloc] initWithFrame:pickerFrame];
[myPicker addTarget:self action:#selector(pickerChanged:) forControlEvents:UIControlEventValueChanged];
[self.view addSubview:myPicker];
[myPicker release];
}
- (void)pickerChanged:(id)sender
{
NSLog(#"value: %#",[sender date]);
}
this is all good and well. I'm a little used to creating elements in IB so when I create an object programatically I'm not sure how to access the data.
What I mean is.. should I assign myPicker to a class property and then access it as _myPicker?
Or lets say I want to access the date inside of the pickerChanged method without calling another method. Should I assign an NSDate property and re-assign it every time the picker is changed?
I ran into some memory issues when I was trying to do it that way. I had another method grabbing _theDate, and it probably tried to access it at the same time pickerChanged was modifying it?
Anyway, what I'm getting at is "whats the proper workflow when creating things like action sheets, and pickers programmatically". When these things are changed, how should the resulting data be saved so the rest of the class can access it?
Bonus question:
Is there a difference between this?
for(UILabel *myLabel in view.subviews){
NSLog(myLabel.text);
}
and this? Do I need to check the class all the time if i know my view only contains a certain kind of object?
for((id) myLabel in view.subviews){
if([myLabel isKindOfClass:[UILabel class]){
UILabel *theLabel = myLabel;
NSLog(myLabel.text);
}
}
Generally, you will just define properties if you'll need to access them more than once. You can do this in the .m file's interface:
#interface MyObject()
#property (weak, nonatomic) UIDatePicker *myPicker;
#end
You will then be able to access it by either _myPicker or self.myPicker.
You shouldn't need another NSDate property in your class because you can access the set date at any time:
_myPicker.date
For your last question: the latter of the two is merely extra sanity checks. While you're writing your own code, and you should know what subviews you're adding in, it can't hurt to double check the type of the subviews incase anything should go wrong and you try to access selectors that don't exist. This is a larger programming question though and not necessarily objective-c or iOS specific.
The documented approach is to intercept the UIControlEventValueChanged event, as per your example.
You would then typically copy the [sender date] value to a property in your pickerChanged: method.
If the user hits a save button, then the object that presented the view containing the picker should be able to retrieve the selected date via the property.
It's not considered good practice to use isKindOfClass:. You should structure your code such that you always know what class you're dealing with.
Also, you should really switch to ARC so you don't need to worry about calling release
You need to declare a UIDatePicker property to hold one instance of your child controller
This is what you need to add in your .h file:
#property (strong, nonatomic) UIDatePicker *myPicker;
And then in your .m file you need to add a data source method for this date picker. something like what rdelmar has instructed above:
self.myPicker = [[UIDatePicker alloc] init];
I am polling an HTTP API - it returns one item at a time, in real-time (about every 4 seconds). As each item is received, I would like a new UITableView cell to be populated. The full list of received items must remain in a class property, I'm guessing an NSMutableArray. What is the best way to initialize an NSMutableArray as a class property, update it as new information comes in, and then use the count to update a new UITableViewCell?
Here's how I'm adding content to an NSMutableDictionary:
NSMutableDictionary *messageContents = [[NSMutableDictionary alloc] init];
[messageContents retain];
[messageContents setValue:messageText forKey:#"text"];
[messageContents setValue:image forKey:#"image"];
[self addMessageToDataArray:messageContents];
Here's the method stuffing objects into the array:
- (void)addMessageToDataArray:(NSArray *)messageDictionary {
[self.messageDataArray addObject:messageDictionary];
NSLog(#"count = %#", [self.messageDataArray count]);
[self reloadTableData];
}
At this point, calling count on the messageDataArray class property crashes the application. I'm very used to working with arrays in Actionscript, Obj-C is obviously totally different. Please explain the method for instantiating an NSMutableArray as a class property, filling it with NSMutableDictionary's and then finding the NSMutableArray count (which will be dynamically updating in real-time) so I can use that info to update a UITableView (on the fly).
Or... tell me I'm being silly and suggest a much easier solution.
From your description I would guess you're not allocating the messageDataArray before using it.
The init function for your table view (controller?) class should have a line like this
messageDataArray = [[NSMutableArray alloc] initWithCapacity:20];
It's also worth checking that you have [messageDataArray release]; in your dealloc method.