retain array data outside of method objective-c - ios

I have an array, players, with two strings inside it: player1 and player2. Here is my .h file:
#import <UIKit/UIKit.h>
#interface hardOne : UIViewController {
UISwitch *hard1ON;
NSMutableArray *players;
NSString *player1, *player2;
}
#property (nonatomic, retain) IBOutlet UISwitch *hard1ON;
#property (nonatomic) BOOL switchState;
#property (nonatomic, retain) NSMutableArray *players;
- (IBAction) switchValueChanged;
#end
The array is initialized in the viewDidLoad then the data is entered into that array in two IBActions in my .m file:
#import "hardOne.h"
#interface hardOne () <UITextFieldDelegate>
#property (nonatomic, strong) IBOutlet UITextField *textFieldOne;
#property (nonatomic, strong) IBOutlet UITextField *textFieldTwo;
#end
#implementation hardOne
#synthesize hard1ON;
#synthesize players;
#synthesize textFieldOne;
#synthesize textFieldTwo;
BOOL switchState;
int counter = 0;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[hard1ON setOn:switchState animated:NO];
//read player names to user defaults
[textFieldOne setText:[[NSUserDefaults standardUserDefaults] stringForKey:#"player1"]];
[textFieldTwo setText:[[NSUserDefaults standardUserDefaults] stringForKey:#"player2"]];
self.players = [[NSMutableArray alloc] init];
NSLog(#"%#",self.players);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction) switchValueChanged
{
counter += 1;
if (counter % 2 == 0) {
switchState = 0;
} else {
switchState = 1;
}
if (hard1ON.on) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"theChange" object:nil];
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:#"theChange2" object:nil];
}
}
- (IBAction) returnKey1
{
player1 = [textFieldOne text];
[self.players addObject:(player1)];
//set player1's name to user defaults
[[NSUserDefaults standardUserDefaults] setValue:[textFieldOne text] forKey:#"player1"];
}
- (IBAction) returnKey2
{
player2 = [textFieldTwo text];
[self.players addObject:(player2)];
//set player2's name to user defaults
[[NSUserDefaults standardUserDefaults] setValue:[textFieldTwo text] forKey:#"player2"];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return NO;
}
#end
If I use NSLog in the second IBAction, once it is complete, the array is correctly displayed in the console with the strings player1 and player2, however if I try to use the array anywhere else it is null. Could anyone point me in the right direction?

You've got two definitions for players.
One is a property. It's never initialized and so it's null. You use it as self.players and backed by the instance variable _players.
One is an instance variable. It's initialized in viewDidLoad. It's not nil.
This is almost surely a mistake.

I would try adding the array as a private instance variable i.e add it to the .m file in the #interface with
NSMutableArray *players;
then you should be able to access the array just by using "players" instead of self.players. This should be then be available throughought the whole of your class. If this doesn't work then I would say the problem doesn't lie within the scope of your variable but rather the with some other code.

Related

How to split lengthy uiviewcontroller class?

I have a long UIViewController with lots of sections and rows. In spite of having separate methods for each of these sections, it still becomes cumbersome jumping to different methods. Is there a best way to design such class? I was thinking of having a category for each of these sections? is it a good idea?
I've found that code related to delegate methods become a large part of my view controllers. One strategy, described in this article at objc.io, is to move data source delegate methods to their own class.
In the last year I started to move out as much code as possible from my view controllers, attempting to create «Lighter View Controllers». There-for you can use objects that implement one — and only one — certain aspect of the functionality that the view controller will have. You could call it Sub Controller, but I and others use the name «Intentions» to express the fact, that each of this objects has one intention.
I experimented with different kinds, from target/action to block based, that I finally found to be the most useful.
An example from real code:
I habe a cash register app that need to communicate via wifi with thermal printers.
To add a printer I have the AddPrinterViewController with textfields for ip address, port and name/location.
Instead of implementing the view controller as delegate for all the textfields I create on class that will serve as delegate for one textfield and has a block-based validation.
#interface TextfieldDelegateIntention : NSObject
#property(nonatomic, weak, readonly) UITextField *textField;
#property (nonatomic, copy) BOOL (^validationBlock)(UITextField *textField);
-(instancetype)initWithTextField:(UITextField *)textField
validationBlock: (BOOL (^)(UITextField *textField)) validationBlock;
-(BOOL)validate;
#end
#import "TextfieldDelegateIntention.h"
#interface TextfieldDelegateIntention ()<UITextFieldDelegate>
#property(nonatomic, weak) UITextField *textField;
#end
#implementation TextfieldDelegateIntention
-(instancetype)initWithTextField:(UITextField *)textField
validationBlock: (BOOL (^)(UITextField *textField)) validationBlock
{
self = [super init];
if(self){
self.validationBlock = validationBlock;
self.textField = textField;
self.textField.delegate = self;
}
return self;
}
-(BOOL)validate
{
if (self.validationBlock) {
return self.validationBlock(self.textField);
}
return NO;
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
-(void)textFieldDidEndEditing:(UITextField *)textField
{
[textField resignFirstResponder];
}
#end
I setup the view controller with the three textfields in the storyboard.
The IP (version 4 only) address' textfield could look like this:
TextfieldDelegateIntention *ipAddresIntention = [[TextfieldDelegateIntention alloc] initWithTextField:self.ipTextField validationBlock:^BOOL(UITextField *textField) {
NSArray *components = [textField.text componentsSeparatedByString:#"."];
if ([components count] == 4) {
__block BOOL compsAreValidNumbers = YES;
[components enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj integerValue] > -1 && [obj integerValue] < 256 ) {
} else {
compsAreValidNumbers = NO;
*stop = YES;
}
}];
return compsAreValidNumbers;
}
return NO;
}];
A overly simple validation, but useful to assist the user with the correct input.
The complete view controller's code looks like
#import <UIKit/UIKit.h>
#import "BaseModalViewController.h"
#class NamendPOSNetworkPrinter;
#class PrinterProvider;
#interface AddPrinterViewController : BaseModalViewController
#property (nonatomic, strong) PrinterProvider *printerProvider;
#property (nonatomic, copy) void(^printerPreferecesEntered)(NSDictionary *printerDict);
#end
#import "AddPrinterViewController.h"
#import "TextfieldDelegateIntention.h"
#import "ButtonIntention.h"
#import "PrinterProvider.h"
#interface AddPrinterViewController ()
#property (weak, nonatomic) IBOutlet UITextField *nameTextField;
#property (weak, nonatomic) IBOutlet UITextField *ipTextField;
#property (weak, nonatomic) IBOutlet UITextField *portTextField;
#property (nonatomic, strong) NSArray *textFieldIntentions;
#property (nonatomic, strong) ButtonIntention *okIntention;
#property (weak, nonatomic) IBOutlet UIButton *okButton;
#end
#implementation AddPrinterViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
TextfieldDelegateIntention *nameIntention = [[TextfieldDelegateIntention alloc] initWithTextField:self.nameTextField validationBlock:^BOOL(UITextField *textField) {
if ([textField.text length] > 0) {
return YES;
}
return NO;
}];
TextfieldDelegateIntention *ipAddresIntention = [[TextfieldDelegateIntention alloc] initWithTextField:self.ipTextField validationBlock:^BOOL(UITextField *textField) {
NSArray *components = [textField.text componentsSeparatedByString:#"."];
if ([components count] == 4) {
__block BOOL compsAreValidNumbers = YES;
[components enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj integerValue] > -1 && [obj integerValue] < 256 ) {
} else {
compsAreValidNumbers = NO;
*stop = YES;
}
}];
return compsAreValidNumbers;
}
return NO;
}];
TextfieldDelegateIntention *portIntention = [[TextfieldDelegateIntention alloc] initWithTextField:self.portTextField validationBlock:^BOOL(UITextField *textField) {
if ([textField.text integerValue] > 1023 && [textField.text integerValue]< 65536) {
return YES;
}
return NO;
}];
self.textFieldIntentions = #[nameIntention, ipAddresIntention, portIntention];
__block typeof(self) weakSelf = self;
ButtonIntention *okIntention = [[ButtonIntention alloc] initWithButton:self.okButton actionBlock:^(UIButton *button) {
typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
__block BOOL formIsValid = YES;
[strongSelf.textFieldIntentions enumerateObjectsUsingBlock:^(TextfieldDelegateIntention *intention, NSUInteger idx, BOOL *stop) {
BOOL isValid = [intention validate];
if (!isValid) {
intention.textField.backgroundColor = [UIColor redColor];
formIsValid = NO;
} else {
intention.textField.backgroundColor = [UIColor greenColor];
}
}];
if (formIsValid) {
if (self.printerPreferecesEntered) {
self.printerPreferecesEntered(#{
#"name": strongSelf.nameTextField.text,
#"ipAddress": strongSelf.ipTextField.text,
#"port": #([strongSelf.portTextField.text integerValue])
});
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[strongSelf dismissViewControllerAnimated:YES completion:NULL];
});
}
}
}];
self.okIntention = okIntention;
}
#end
As you see I can use simple blocks to add different behavior to the three textfields and only one method is overwritten — no other added.
Another example with email validation from my blog: Lighter ViewControllers with Block-based Intentions
A tableview's datasource is also very easy implemented as Intention.
#import <UIKit/UIKit.h>
#class PrinterProvider;
#interface PrinterDataSource : NSObject <UITableViewDataSource>
#property (nonatomic, strong) PrinterProvider *printerProvider;
#end
#import "PrinterDataSource.h"
#import "VSPOSNetworkPrinter.h"
#import "PrinterProvider.h"
#interface PrinterDataSource ()
#end
#implementation PrinterDataSource
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[self.printerProvider allPrinters] count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"PrinterCell" forIndexPath:indexPath];
cell.textLabel.text = [[self.printerProvider allPrinters][indexPath.row] ipAddress];
cell.detailTextLabel.text = [[self.printerProvider allPrinters][indexPath.row] name];
return cell;
}
#end
Use it as
#import "PrinterViewController.h"
#import "AddPrinterViewController.h"
#import "ButtonIntention.h"
#import "BarButtomItemIntention.h"
#import "NamendPOSNetworkPrinter.h"
#import "PrinterProvider.h"
#import "PrinterDataSource.h"
#interface PrinterViewController ()
#property (weak, nonatomic) IBOutlet UITableView *tableView;
#property (weak, nonatomic) IBOutlet UIBarButtonItem *addButton;
#property (nonatomic, strong) BarButtomItemIntention *addIntention;
#property (nonatomic, strong) AddPrinterViewController *addPrinterViewController;
#property (strong, nonatomic) IBOutlet PrinterDataSource *printerDataSource;
#property (nonatomic, strong) PrinterProvider *printerProvider;
#end
#implementation PrinterViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.printerDataSource.printerProvider = self.printerProvider;
self.addPrinterViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"AddPrinterViewController"];
typeof(self) weakSelf = self;
[self.addPrinterViewController setPrinterPreferecesEntered:^(NSDictionary *printerDict) {
NamendPOSNetworkPrinter *printer = [[NamendPOSNetworkPrinter alloc] initWithName:printerDict[#"name"]
ipAddress:printerDict[#"ipAddress"]
port:printerDict[#"port"]];
typeof(self) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf.printerProvider addPrinter:printer];
[strongSelf.tableView reloadData];
}
}];
self.addPrinterViewController.printerProvider = self.printerProvider;
self.addIntention = [[BarButtomItemIntention alloc] initWithButtonItem:self.addButton
actionBlock:^(UIBarButtonItem *buttonItem)
{
typeof(weakSelf) strongSelf = weakSelf;
if(strongSelf){
[strongSelf presentViewController:strongSelf.addPrinterViewController
animated:YES completion:^{
}];
}
}];
}
#end
The charm about this approach is that you can independently create and subclass view controller and intentions, and that I can rearrange them as needed easily. Actually apple started to advertise a similar approach in the recent wwdc video «Advanced User Interfaces with Collection Views»
I would suggest using #pragma mark [divider name] to split up your class into sections so you can easily group methods. Otherwise creating subclasses where it makes sense to handle contained functionality is you're only option. I've seen 8,000+ line view controller classes, so sometimes it's unavoidable if you're too deep to break it all apart.
This is more like a programming philosophy.
Objective-C has lengthy methods (name and declaration), so the best way to ease the code is to delegate any repetitive pattern to an external class.
For example, I create a class reserved solely of returning numerical operations, such as arithmetic operations on NSNumbers, strings, etc. Another one that just returns booleans on an input.
For example, if you verify the validity of a number and increment it, or set it to 1, in even just two places in your code, rather than having somethings like this:
if (variableName != nil && variableName.intValue > 0)
{
variableName2 = [NSNumber numberWithInt:(variableName.intValue + 1)];
}
else
{
variableName2 = [NSNumber numberWithInt:1];
}
It would be easier to have this function outside (in an "Arithmetics" class for example), and call it like follow:
variableName2 = [Arithmetics incrementNumber:variableName];
If you replace all your code like this, by having well named classes and easy to understand methods (names), your code will be more logically readable and easy to follow, which is the philosophy of Objective-C (you get also the bonus of being able to reuse these methods in all your code, keeping the same logic everywhere).

Button values appearing as "null"

I'm trying to have two buttons in my SearchCategoryChooserViewcontroller to display titles based on values being returned by a function. However, the buttons seem to be showing up with "null" for a title, even though the topCategoriesArray is successfully being returned. Here's my setup:
SearchCategoryChooserViewController.m:
#import "SearchCategoryChooserViewController.h"
#import "SearchViewController.h"
#interface SearchCategoryChooserViewController ()
#end
#implementation SearchCategoryChooserViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
UIButton *category1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
category1.frame = CGRectMake(10, 120, 300, 35);
[category1 setTitle: [NSString stringWithFormat:#"%#", self.topCategory1] forState:UIControlStateNormal];
[category1 addTarget:self action:#selector(myButtonClick:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview: category1];
UIButton *category2 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
category2.frame = CGRectMake(10, 180, 300, 35);
[category2 setTitle: [NSString stringWithFormat:#"%#", self.topCategory2] forState:UIControlStateNormal];
[category2 addTarget:self action:#selector(myButtonClick:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview: category2];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#end
SearchCategoryChooserViewController.h:
#import <UIKit/UIKit.h>
#import "SearchViewController.h"
#interface SearchCategoryChooserViewController : SearchViewController
#end
SearchViewController.h:
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#import <Parse/PFCloud.h>
#import "CriteriaViewController.h"
#interface SearchViewController : UIViewController
#property (weak, nonatomic) IBOutlet UIButton *nextButtonOutlet;
#property (weak, nonatomic) NSString *topCategory1;
#property (weak, nonatomic) NSString *topCategory2;
#end
SearchViewController.m:
#import "SearchViewController.h"
#interface SearchViewController ()
#property (weak, nonatomic) IBOutlet UITextField *itemSearch;
#property (weak, nonatomic) IBOutlet UIButton *nextButton;
#end
#implementation SearchViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.nextButtonOutlet addTarget:self action:#selector(nextButton:) forControlEvents:UIControlEventTouchUpInside];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)nextButton:(id)sender
{
if (self.itemSearch.text.length > 0) {
[PFCloud callFunctionInBackground:#"eBayCategorySearch"
withParameters:#{#"item": self.itemSearch.text}
block:^(NSDictionary *result, NSError *error) {
NSLog(#"'%#'", result);
NSArray *resultArray = [result objectForKey:#"results"];
NSDictionary *dictionary0 = [resultArray objectAtIndex:0];
NSNumber *numberOfTopCategories = [dictionary0 objectForKey:#"Number of top categories"];
NSDictionary *dictionary1 = [resultArray objectAtIndex:1];
NSNumber *topCategories = [dictionary1 objectForKey:#"Top categories"];
NSDictionary *dictionary2 = [resultArray objectAtIndex:2];
NSNumber *numberOfMatches = [dictionary2 objectForKey:#"Number of matches"];
NSDictionary *dictionary3 = [resultArray objectAtIndex:3];
NSNumber *userCategoriesThatMatchSearch = [dictionary3 objectForKey:#"User categories that match search"];
NSArray *topCategoriesArray = [dictionary1 objectForKey:#"Top categories"];
NSString *topCategory1 = [topCategoriesArray objectAtIndex:0];
NSString *topCategory2 = [topCategoriesArray objectAtIndex:1];
if (!error) {
// if 1 match found clear categoryResults and top2 array
if ([numberOfMatches intValue] == 1 ){
[self performSegueWithIdentifier:#"ShowMatchCenterSegue" sender:self];
}
// if 2 matches found
else if ([numberOfMatches intValue] == 2){
[self performSegueWithIdentifier:#"ShowUserCategoryChooserSegue" sender:self];
//default to selected categories criteria -> send to matchcenter -> clear categoryResults and top2 array
}
// if no matches found, and 1 top category is returned
else if ([numberOfMatches intValue] == 0 && [numberOfTopCategories intValue] == 1) {
[self performSegueWithIdentifier:#"ShowCriteriaSegue" sender:self];
}
// if no matches are found, and 2 top categories are returned
else if ([numberOfMatches intValue] == 0 && [numberOfTopCategories intValue] == 2) {
[self performSegueWithIdentifier:#"ShowSearchCategoryChooserSegue" sender:self];
}
}
}];
}
}
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// if([segue.identifier isEqualToString:#"ShowSearchCategoryChooserSegue"]){
// SearchCategoryChooserViewController *controller = (SearchCategoryChooserViewController *) segue.destinationViewController;
// controller.itemSearch.text = self.itemSearch.text;
// }
}
#end
There are multiple issues:
You should change the code:
#property (weak, nonatomic) NSString *topCategory1;
#property (weak, nonatomic) NSString *topCategory2;
to
#property (strong, nonatomic, copy) NSString *topCategory1;
#property (strong, nonatomic, copy) NSString *topCategory2;
or
//because strong is default
#property (nonatomic, copy) NSString *topCategory1;
#property (nonatomic, copy) NSString *topCategory2;
It is better if you add the ´copy´ attribute for the NSString properties in case of using NSMutableString value.
Because with ARC if there is not at least one strong reference for an object it will be deallocated. If your Object have only a weak reference to an another object, it is not own it and it will be deallocated even if you use it in your Object. But with strong reference your Object own the another Object and it will not be deallocated until your Object lives.
And you should be set the property values before perform a segue with change this code
NSString *topCategory1 = [topCategoriesArray objectAtIndex:0];
NSString *topCategory2 = [topCategoriesArray objectAtIndex:1];
to
self.topCategory1 = [topCategoriesArray objectAtIndex:0];
self.topCategory2 = [topCategoriesArray objectAtIndex:1];
and use the values in prepareForSegue:
if([segue.identifier isEqualToString:#"ShowSearchCategoryChooserSegue"]){
SearchCategoryChooserViewController *controller = (SearchCategoryChooserViewController *) segue.destinationViewController;
controller.itemSearch.text = self.itemSearch.text;
controller.topCategory1 = self.topCategory1;
controller.topCategory2 = self.topCategory1;
}
It appears to me (from the code you've pasted into your question) that you are never setting your "topCategory1" string property in your SearchCategoryChooserViewController object.
Which would explain why you are seeing "null" in the label.

Add up all values in NSMutableArray and multiply by value depending on Plain Segmented Control

What i want to do is multiply the latest NSMutableArray array entry (entered using a UITextField) by 3 when Moderate intensity is selected using the Plain Segmented Control and 6 when vigorous is selected and then display the total value of all entries in the array after the multiplications have occurred. E.g. If there User selects Moderate using the Plain Segmented Control and enters 120 in the UITextField, I need a value of 360 to be displayed and for that value to increment as more entries are made.
So far I'm storing the array values in a table like below which works fine.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"SecondViewControllerSegue"]) {
SecondViewController *secondViewController
= [segue destinationViewController];
//secondViewController.infoRequest = self.nameField.text;
NSString* style = (styleSeg.selectedSegmentIndex == 0) ? #"Moderate intensity for" : #"Vigourous intensity for";
[activities addObject:[[NSString alloc]initWithFormat:#"Your activity: %#", self.activityField.text]];
secondViewController.activities = activities;
[activities addObject:[[NSString alloc]initWithFormat:#"%#: %# minutes", style, self.nameField.text]];
secondViewController.activities = activities;
}
}
I just can't seem to multiply and output the values. I've been playing around with something like
if(styleseg.selectedSegmentIndex == 0){
3x(what the user entered in duration)
}
if(styleseg.selectedSegmentIndex == 1){
6x(what the user entered in duration)
}
And a loop attempting to add up the total values in the array which is just outputting 0.
int result = 0;
for(int i=0;i<[activities count];i++)
result += [[activities objectAtIndex:i] intValue];
NSLog(#"result = %d", result);
I'm just having trouble blending the two together to do what I want. Any help is greatly appreciated.
NEW EDIT
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController {
IBOutlet UIView *nameView;
IBOutlet UITextField *nameField;
IBOutlet UITextField *activityField;
IBOutlet UISegmentedControl *styleSeg;
}
#property UIView *nameView;
#property UITextField *nameField;
#property UITextField *activityField;
#property (strong,nonatomic) NSMutableArray *activities;
- (IBAction)submitButtonTapped:(id)sender;
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#import "SecondViewController.h"
#import "MyActivity.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize nameView;
#synthesize nameField;
#synthesize activityField;
#synthesize activities;
- (void)viewDidLoad
{
[super viewDidLoad];
activities = [[NSMutableArray alloc] init];
//activityName = [[NSMutableArray alloc] init];
}
-(void)viewWillAppear:(BOOL)animated
{
self.nameField.text = #"";
self.activityField.text = #"";
styleSeg.selectedSegmentIndex = 0;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)submitButtonTapped:(id)sender {
NSLog(#"The submit button was clicked.");
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"SecondViewControllerSegue"]) {
SecondViewController *secondViewController
= [segue destinationViewController];
secondViewController.infoRequest = self.nameField.text;
/* This was my initial code as in the intial question
NSString* style = (styleSeg.selectedSegmentIndex == 0) ? #"Moderate intensity for" : #"Vigourous intensity for";
[activities addObject:[[NSString alloc]initWithFormat:#"Your activity: %#", self.activityField.text]];
secondViewController.activities = activities;
[activities addObject:[[NSString alloc]initWithFormat:#"%#: %# minutes", style, self.nameField.text]];
secondViewController.activities = activities;
*/
// New code
MyActivity *activity=[[MyActivity alloc]init];
activity.description=self.activityField.text;
activity.duration=[self.nameField.text intValue];
activity.intensity=(styleSeg.selectedSegmentIndex == 0) ? 3:6;
[self.activities addObject:activity];
secondViewController.activities = activities;
}
}
#end
SecondViewController.h
#import <UIKit/UIKit.h>
#interface SecondViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
{
IBOutlet UIView *secondView;
IBOutlet UILabel *nameLabel;
}
#property IBOutlet UITableView *activityTableView;
#property NSMutableArray* activities;
//#property NSMutableArray* activityName;
#property UIView *secondView;
#property UILabel *nameLabel;
#property (weak, nonatomic) IBOutlet UILabel *nameLabel2;
#property id infoRequest;
-(IBAction)goBack:(id)sender;
#end
SecondViewController.m
#import "SecondViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
#synthesize secondView;
#synthesize nameLabel;
#synthesize nameLabel2;
#synthesize activities;
#synthesize infoRequest;
#synthesize activityTableView;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.nameLabel.text = [self.infoRequest description];
self.nameLabel2.text = [self.infoRequest description];
// activities = [[NSArray alloc] init];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(IBAction)goBack:(id)sender
{
UINavigationController* parent = (UINavigationController*)[self parentViewController];
[parent popViewControllerAnimated:YES];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Number of rows is the number of time zones in the region for the specified section.
return [activities count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellReuseIdentifier = #"CellReuseIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellReuseIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellReuseIdentifier];
}
NSString* s = [activities objectAtIndex:indexPath.row];
cell.textLabel.text = s;
return cell;
}
Your array seems to hold arbitrary strings, not integers represented as strings (the intValue of "10" is 10, but the intValue of "Moderate intensity for: 10 minutes" is 0). Also you are adding multiple elements for each instance of an activity - the activity description and the activity duration.
I would create another object class to encapsulate the activity -
MyActivity.h
#interface MyActivity : NSObject
#property (copy,nonatomic) NSString *description;
#property int duration;
#property int intensity;
#end
Create a UIButton and set it's touch up inside event to doAddToArray. This will add an entry to the array for each new activate. Allocate a new MyActivity and set the appropriate properties before adding it to the array -
In ViewController.m
-(IBAction)doAddToArray:(id)sender {
MyActivity *activity=[[MyActivity alloc]init];
activity.description=self.activityField.text;
activity.duration=[self.nameField.text intValue];
activity.intensity=(styleSeg.selectedSegmentIndex == 0) ? 3:6;
[self.activities addObject:activity];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"SecondViewControllerSegue"]) {
SecondViewController *secondViewController
= [segue destinationViewController];
secondViewController.activities = activities;
}
Then in SecondViewController.h
#import <UIKit/UIKit.h>
#interface SecondViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
{
IBOutlet UIView *secondView;
IBOutlet UILabel *nameLabel;
}
#property IBOutlet UITableView *activityTableView;
#property UIView *secondView;
#property UILabel *nameLabel;
#property (weak, nonatomic) IBOutlet UILabel *nameLabel2;
#property (weak, nonatomic) NSArray *activities; // This is used to provide content to the UITableView
#property id infoRequest;
-(IBAction)goBack:(id)sender;
#end
For brevity I won't include the full SecondViewController.m, but you have a number of places where you use activities that should be self.activities
Then to total the array (wherever you need to)-
int total=0;
for (MyActivity *activity in self.activities)
{
total+=activity.duration*activity.intensity;
}

accessing class property which is added by frord declaration from another class

I have a class
#interface AppRecord : NSObject
#property (nonatomic, retain) NSString * urlSingle;
#property (nonatomic, retain) NSArray * image_url;
#end
It is included in another class
#class AppRecord;
#interface IconDownloader : NSObject
#property (nonatomic, strong) AppRecord *appRecord;
#end
This is my root view controller
#import "IconDownloader.h"
#implementation RootViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.imageDownloadsInProgress = [NSMutableDictionary dictionary];
}
- (void)startIconDownload:(AppRecord *)appRecord forIndexPath:(NSIndexPath *)indexPath
{
IconDownloader *iconDownloader = [self.imageDownloadsInProgress objectForKey:indexPath];
if (iconDownloader == nil)
{
iconDownloader = [[IconDownloader alloc] init];
int imgArrCount=[appRecord.image_url count];
NSLog(#"Image array is********************** %#",appRecord.image_url);
for(int i=0;i<imgArrCount;i++)
{
iconDownloader.appRecord.urlSingle=[appRecord.image_url objectAtIndex:i];
NSLog(#"iconDownloader.appRecord.urlSingle---------------------%#",iconDownloader.appRecord.urlSingle);
}
}
}
#end
Can i assign iconDownloader.appRecord.urlSingle here, I am having null value.Please help.
This has nothing to do with forward declaration. When you forward declare a class, you should #import the .h file before using any of the class properties/methods.
The problem is property appRecord in iconDownloader is not created yet and hence is nil. In your code you should do this.
- (void)startIconDownload:(AppRecord *)appRecord forIndexPath:(NSIndexPath *)indexPath
//...
for(int i=0;i<imgArrCount;i++)
{
// First assign to the property so that it is not nil
iconDownloader.appRecord = appRecord;
// If required then make this assignment
iconDownloader.appRecord.urlSingle=[appRecord.image_url objectAtIndex:i];
}
//...
}
Alternately, you can also override the init in IconDownloader class and create the appRecord property inside it, so that it is not nil when you are assigning values.
Hope that helps!
You didnt initializing the appRecord object. thats why you get null value. Just initialize appRecord in your init method like:
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
appRecord = [[AppRecord alloc]init];
}
return self;
}
Similiarly you have to initialize the urlSingle variable inside the init definition:
-(id)init
{
self = [super init];
if (self) {
urlSingle = URL_STRING_HERE;
}
return self;
}
Now you try

dynamically generated UIButton in iOS 5 ARC causes deallocated instance crash

I have a a class I created to generate UIButton's I add to my UIView. This worked great until my conversion to ARC yesterday, not I get the following error:
-[OrderTypeButton performSelector:withObject:withObject:]: message sent to deallocated instance 0x12449f70
Here is the code to add the button to my UIView (actually a subview in my main UIView):
OrderTypeButton *btn = [[OrderTypeButton alloc]initWithOrderType:#"All Orders" withOrderCount:[NSString stringWithFormat:#"%i",[self.ordersPlacedList count]] hasOpenOrder:NO];
btn.view.tag = 6969;
btn.delegate = self;
[btn.view setFrame:CGRectMake((col * width)+ colspacer, rowHeight + (row * height), frameWidth, frameHeight)];
[self.statsView addSubview:btn.view];
And here is my class header:
#import <UIKit/UIKit.h>
#protocol OrderTypeButtonDelegate
-(void) tapped:(id)sender withOrderType:(NSString*) orderType;
#end
#interface OrderTypeButton : UIViewController {
id<OrderTypeButtonDelegate> __unsafe_unretained delegate;
IBOutlet UILabel *lblOrderType;
IBOutlet UILabel *lblOrderCount;
NSString *orderType;
NSString *orderCount;
BOOL hasOpenOrder;
}
#property (nonatomic, strong) IBOutlet UIButton *orderButton;
#property (nonatomic, strong) IBOutlet UILabel *lblOrderType;
#property (nonatomic, strong) IBOutlet UILabel *lblOrderCount;
#property (nonatomic, strong) NSString *orderType;
#property (nonatomic, strong) NSString *orderCount;
#property (nonatomic, assign) BOOL hasOpenOrder;
#property (nonatomic, unsafe_unretained) id<OrderTypeButtonDelegate> delegate;
-(id) initWithOrderType: (NSString *) anOrderType withOrderCount: (NSString *) anOrderCount hasOpenOrder: (BOOL) openOrder;
-(IBAction)btnTapped:(id)sender;
#end
Implementation:
#import "OrderTypeButton.h"
#implementation OrderTypeButton
#synthesize orderButton;
#synthesize lblOrderType, lblOrderCount, orderType, orderCount, hasOpenOrder, delegate;
-(id) initWithOrderType: (NSString *) anOrderType withOrderCount: (NSString *) anOrderCount hasOpenOrder: (BOOL) openOrder {
if ((self = [super init])) {
self.orderType = anOrderType;
self.orderCount = anOrderCount;
self.hasOpenOrder = openOrder;
}
return self;
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.lblOrderType.text =[NSString stringWithFormat:#"%#", self.orderType];
self.lblOrderCount.text = [NSString stringWithFormat:#"%#", self.orderCount];
if (self.hasOpenOrder) {
[self.orderButton setBackgroundImage:[UIImage imageNamed:#"background-order-btn-red.png"] forState:UIControlStateNormal];
self.lblOrderType.textColor = [UIColor whiteColor];
self.lblOrderCount.textColor = [UIColor whiteColor];
}
}
-(IBAction)btnTapped:(id)sender {
NSLog(#"TAPPED");
if ([self delegate] ) {
[delegate tapped:sender withOrderType:self.orderType];
}
}
- (void)viewDidUnload
{
[self setOrderButton:nil];
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
#end
This seems fairly simple what I am doing here, not sure what changed with ARC that is causing me problems.
Maybe ARC autorelease created button, try to store created buttons in Array
//.h file
#property (nonatomic, strong) NSArray *buttonsArray
//.m file
#synthesize buttonsArray
...
- (void)viewDidLoad {
buttonsArray = [NSArray array];
...
OrderTypeButton *btn = [[OrderTypeButton alloc]initWithOrderType:#"All Orders"
withOrderCount:[NSString stringWithFormat:#"%i",[self.ordersPlacedList count]]
hasOpenOrder:NO];
btn.view.tag = 6969;
btn.delegate = self;
[btn.view setFrame:CGRectMake((col * width)+ colspacer, rowHeight + (row * height), frameWidth, frameHeight)];
[self.statsView addSubview:btn.view];
//Add button to array
[buttonsArray addObject:btn];
Also this approach will help if you want to change buttons, or remove some specific button from view

Resources