My NSMutable Array isn't adding a button - objective-c

I'm trying to add button to a scroll view using a NSMutableArray. The code works fine in a twitter example I am using as a reference, but for some reason it is not working in this case. In the twitter search case the code is implemented in the main class. While in this case I am implementing it in the flipside view. Could anyone help me out since I am a bit new to Objective c.
Thanks in Advance.
Here is the init where the NSMutableArray Buttons is initialized
- (id)init
{
self = [super init]; // initialize the superclass members
if (self != nil) // if the superclass initialized properly
{
// creates list of valid directories for saving a file
NSArray *paths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
// get the first directory
NSString *dir = [paths objectAtIndex:0];
// concatenate the file name "tagsIndex.plist" to the path
filePath = [[NSString alloc] initWithString:
[dir stringByAppendingPathComponent:#"tagsIndex.plist"]];
NSFileManager *fileManager = [NSFileManager defaultManager];
// if the file does not exist, create an empty NSMutableDictionary;
// otherwise, initialize an NSDictionary with the file's contents
if ([fileManager fileExistsAtPath:filePath] == NO)
{
tags = [[NSMutableDictionary alloc] init];
} // end if
else
{
tags = [[NSMutableDictionary alloc]
initWithContentsOfFile:filePath];
} // end else
buttons = [[NSMutableArray alloc] init]; // create array
infoButtons = [[NSMutableArray alloc] init]; // create array
}
return self; // if self == nil, object not initialized properly
} // end method init
Here we have the method where we are adding the object to the buttons array but it stays empty.
- (void)addNewButtonWithTitle:(NSString *)title
{
// create a new button
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
// give the button the title of the tag
[button setTitle:title forState:UIControlStateNormal];
// tell the button to call buttonTouched: when it is touched
[button addTarget:self action:#selector(buttonTouched:)
forControlEvents:UIControlEventTouchUpInside];
[buttons addObject:button]; // add the UIButton to the end of the array
//This Doesn't add it stays 0;
// sort the NSMutableArray by the UIButton's titles
[buttons sortUsingSelector:#selector(compareButtonTitles:)];
[self refreshList]; // refresh the list of favorite search Buttons
// Adjust the content size of the view to include the new button. The
// view scrolls only when the content size is greater than its frame.
CGSize contentSize = CGSizeMake(scrollView.frame.size.width,
buttons.count * (BUTTON_HEIGHT + BUTTON_SPACING) + BUTTON_SPACING);
[scrollView setContentSize:contentSize];
}
Here is the dealloc where I release the buttons array.
- (void)dealloc {
[buttons release];
[super dealloc];
}
I can't Figure out where the mistake is.
Here is the Code that is supposed to add a new button.
- (IBAction)addTag:sender
{
// make the keyboard disappear
[ItemTitleField resignFirstResponder];
[QuantityField resignFirstResponder];
NSString *key = ItemTitleField.text; // get the text in tagField
NSString *value = QuantityField.text; // get the text in queryField
// test if either field is empty
if (value.length == 0 || key.length == 0)
return; // exit from the method
if ([tags valueForKey:key] == nil) // test if the tag already exists
[self addNewButtonWithTitle:key]; // if not, add a new button
[tags setValue:value forKey:key]; // add a new entry in tags
ItemTitleField.text = nil; // clear tagField of text
QuantityField.text = nil; // clear queryField of text
[tags writeToFile:filePath atomically:NO]; //save the data
} // end method addTag:

- (void)addNewButtonWithTitle:(NSString *)title
{
// create a new button
// UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
UIButton *button = [[UIButton alloc] init];
or
UIButton *button = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
// give the button the title of the tag
[button setTitle:title forState:UIControlStateNormal];
// tell the button to call buttonTouched: when it is touched
[button addTarget:self action:#selector(buttonTouched:)
forControlEvents:UIControlEventTouchUpInside];
[buttons addObject:button]; // add the UIButton to the end of the array
//This Doesn't add it stays 0;
// sort the NSMutableArray by the UIButton's titles
[buttons sortUsingSelector:#selector(compareButtonTitles:)];
[self refreshList]; // refresh the list of favorite search Buttons
// Adjust the content size of the view to include the new button. The
// view scrolls only when the content size is greater than its frame.
CGSize contentSize = CGSizeMake(scrollView.frame.size.width,
buttons.count * (BUTTON_HEIGHT + BUTTON_SPACING) + BUTTON_SPACING);
[scrollView setContentSize:contentSize];
}

Related

Saving state of UITableView cell accessory?

I have gesture recognisers set up on my table view.
Swipe to the right and the accessory changes to an image of a tick
Swipe to the left and is changes to a chevron image
If a cell is tapped, it loads a local HTML file.
If you swipe to the right, the tick appears as it should. However, if you then tap a cell to view a HTML file and come back to the table view, the image reverts to the chevron.
What's the best way to ensure the tick stays as it should?
EDIT
Further code:
From 'viewDidLoad':
UISwipeGestureRecognizer *recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:#selector(handleSwipeRight:)];
[recognizer setDirection:(UISwipeGestureRecognizerDirectionRight)];
[self.tableView addGestureRecognizer:recognizer];
recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
action:#selector(handleSwipeLeft:)];
//recognizer.delegate = self;
[recognizer setDirection:(UISwipeGestureRecognizerDirectionLeft)];
[self.tableView addGestureRecognizer:recognizer];
- (void)handleSwipeLeft:(UISwipeGestureRecognizer *)gestureRecognizer
{
//Get location of the swipe
CGPoint location = [gestureRecognizer locationInView:self.tableView];
//Get the corresponding index path within the table view
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
//Check if index path is valid
if(indexPath)
{
//Get the cell out of the table view
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
//Update the cell or model
cell.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"disclosure.png"]];
}
}
- (void)handleSwipeRight:(UISwipeGestureRecognizer *)gestureRecognizer
{
CGPoint location = [gestureRecognizer locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
if(indexPath)
{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
// cell.accessoryType = UITableViewCellAccessoryCheckmark;
cell.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"tick.png"]];
}
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"MFGCell";
MFGCell *cell = (MFGCell *) [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"MFGCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.itemTitle.text = [item objectAtIndex:indexPath.row];
cell.itemDescription.text = [description objectAtIndex:indexPath.row];
cell.itemImageView.image = [UIImage imageNamed:[icons objectAtIndex:indexPath.row]];
return cell;
}
In reaction to the user's swipe you should store the user's choice (e.g. in a private instance variable of type NSMutableArray). When the user comes back to the table view you can then reuse the information in your tableView:cellForRowAtIndexPath: to setup the cell with the correct accessory style.
Property declaration:
#property(nonatomic, retain) NSMutableArray* _accessoryStyle;
Synthesize the property. Then add this snippet to the bottom of handleSwipeLeft: to store the user's choice:
- (void)handleSwipeLeft:(UISwipeGestureRecognizer *)gestureRecognizer
{
[...]
NSNumber* number = [numberWithInt:0];
[_accessoryStyle replaceObjectAtIndex:indexPath.row withObject:number];
}
Add a similar snippet to the bottom of handleSwipeRight::
- (void)handleSwipeRight:(UISwipeGestureRecognizer *)gestureRecognizer
{
[...]
NSNumber* number = [numberWithInt:1];
[_accessoryStyle replaceObjectAtIndex:indexPath.row withObject:number];
}
In tableView:cellForRowAtIndexPath::
NSString* accessoryImageName;
NSNumber* number = [_accessoryStyle objectAtIndex:indexPath.row];
switch ([number intValue])
{
case 0:
accessoryImageName = #"disclosure.png";
break;
case 1:
accessoryImageName = #"tick.png";
break;
default:
// replace with your error handling code
return nil;
}
cell.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:accessoryImageName]];
For all this to work you need to initialize the _accessoryStyle array with the same number of elements that you expect your table view to have cells. For instance, in your view controller's viewDidLoad:
- (void) viewDidLoad
{
[super viewDidLoad];
self._accessoryStyle = [NSMutableArray arrayWithCapacity:0];
NSNumber* defaultAccessoryStyle = [numberWithInt:0];
int numberOfRows = 17; // get the real number from somewhere
for (int index = 0; index < numberOfCells; ++index)
[_accessoryStyle addObject:defaultAccessoryStyle];
}
And to balance this you need to add
- (void) viewDidUnload
{
[super viewDidUnload];
self._accessoryStyle = nil;
}
There is still much room for improvement:
Find better variable names
Use an enumeration for the different styles instead of just hardcoded numbers 0 and 1
Do not allocate a new UIImageView for each table view cell, just allocate two of them and use the right one depending on the accessory style
For your problem, there is an underlying logic issue because there is either a swipe left event firing when it should not or the views are just being unloaded and resetting to default. See if you can log when the events fire; otherwise the state of the view should be preserved. Also what I would do is add an extra state variable like int currentCellState that you change when you enter your different states to keep track of your states. Then in your viewDIdLoad make sure that all your data and your view are in sync, ie the value of currentCellState matches the state of your view.
The best way to do this is to put the images/buttons you have in an array, and each time the view loads it shows the item which index is selected..
in order to do this, the swipeMethode should be modified to something like this
-(void)swipeMethod: (UISwipeGestureRecognizer *) sender
{
if(sender.direction ==
UISwipeGestureRecognizerDirectionLeft && index < [myArray count]){
[self setSelectedIndex:index+1 animated:YES];
index++;
}else if (sender.direction == UISwipeGestureRecognizerDirectionRight && index > 0) {
[self setSelectedIndex:index-1 animated:YES];
index--;
}else {
return;
}
}
in the viewDidLoad add this code:
leftRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipeMethod:)];
[leftRecognizer setDirection: UISwipeGestureRecognizerDirectionLeft];
[self.tableView addGestureRecognizer:leftRecognizer];
rightRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipeMethod:)];
[rightRecognizer setDirection: UISwipeGestureRecognizerDirectionRight];
[self.tableView addGestureRecognizer:rightRecognizer];

How can I change this code so that it doesn't infringe MVC principles?

I am making a quiz app for chemistry reactions. The app gives the user a question, and the user types in the reactants and products in two separate textfields.
I have a data class, which contains all the possible quiz questions. Then, based on specific parameters, the data class selects a certain amount of questions from the pool of possible questions and shuffles them into an array, quizQuestions.
I also have a view controller, quizController. For each question, a new instance of quizController is loaded. The vc needs the data to know which question to display, what the correct answer is, etc.
This is my original solution to communicate between the data and the vc's.
I create an instance of the data class, data.
I create vc1 for the first question, and set data as a property of vc1, and set its tag as 1, so that it loads the 1st question from the data.
After the user answers the first question, I create a new view controller, vc2, in a method of vc1, make the tag 1 more than the last vc's so that the second question loads, and pass data from vc1's property to vc2's.
And then I repeat for the other questions.
However, this is not very good design, and I am looking for a better solution. I don't think I can use a class method because the data is supposed to contain a random set of questions. Below I have the code for the data class.
// the data class as it is now, designed for instance methods
- (id)init {
self = [super init];
if (self) {
//Questions of the same type belong to a "ROW" (Reaction of Week)
//individual questions
// Items in array: (1)Question, (2)Reactants, (3)Products, (4)Elements for keyboard
NSArray *R1Q1 = [[NSArray alloc] initWithObjects:#"Methanol is burned completely in air", #"2CH₃OH(l) + 3O₂(g)", #"2CO₂(g) + 4H₂O", #"C,H,O", nil];
NSArray *R1Q2 = [[NSArray alloc] initWithObjects:#"Ammonia is burned in excess oxygen gas", #"4NH₃(g) + 7H₂O(l)", #"4NO₂(g) + 6H₂O(l)", #"N,H,O", nil];
NSArray *R1Q3 = [[NSArray alloc] initWithObjects:#"Hydrogen sulfide gas is burned in excess oxygen gas", #"2H₂S(g) + 3O₂(g)", #"CO₂(g) + 2SO₂(g)", #"H,S,O", nil];
NSArray *R2Q1 = [[NSArray alloc] initWithObjects:#"Solid potassium is added to a flask of oxygen gas", #"K(s) + O₂(g)", #"KO₂(s)", #"K,O", nil];
NSArray *R2Q2 = [[NSArray alloc] initWithObjects:#"Sodium metal is dropped into a flask of pure water", #"2Na(s) + H₂O(l)", #"2Na⁺(aq) + 2OH⁻(aq) + H₂(g)", #"Na,H,O", nil];
NSArray *R2Q3 = [[NSArray alloc] initWithObjects:#"A piece of lithium is heated strongly in oxygen", #"4Li(s) + O₂(g)", #"2Li₂O(s)", #"Li,O", nil];
NSArray *R3Q1 = [[NSArray alloc] initWithObjects:#"Solutions of potassium chloride and silver nitrate are mixed", #"Ag⁺(aq) + Cl⁻(aq)", #"AgCl(s)", #"K,Cl,Ag,N,O", nil];
NSArray *R3Q2 = [[NSArray alloc] initWithObjects:#"Solutions of iron(III) nitrate and sodium hydroxide are mixed", #"Fe³⁺(aq) + 3OH⁻(aq)", #"Fe(OH)₃(s)", #"Fe,N,O,Na,H", nil];
NSArray *R3Q3 = [[NSArray alloc] initWithObjects:#"Solutions of nickel iodide and barium hydroxide are mixed", #"Ni²⁺(aq) + 2OH⁻(aq)", #"Ni(OH)₂(s)", #"Ni,I,Ba,OH", nil];
// add rest
//organize questions into groups
row1 = [[NSArray alloc] initWithObjects:R1Q1, R1Q2, R1Q3, nil];
row2 = [[NSArray alloc] initWithObjects:R2Q1, R2Q2, R2Q3, nil];
row3 = [[NSArray alloc] initWithObjects:R3Q1, R3Q2, R3Q3, nil];
//add rest
// array containing all questions
allRows = [[NSMutableArray alloc] initWithObjects:row1, row2, row3, nil];
//in a real situation, needs to be given to class dynamically
self.maxRowNumber = 3;
self.questionsPerRow = 2;
}
return self;
}
- (void)selectQuestions {
self.quizQuestions = [[NSMutableArray alloc] init];
for (int j = 0; j<self.maxRowNumber; j++) {
//shuffle each row
NSMutableArray *row = [NSMutableArray arrayWithArray:[allRows objectAtIndex:j]];
[row shuffle];
//add questions from each row
for (int k = 0; k<self.questionsPerRow; k++)
[quizQuestions addObject:[row objectAtIndex:k]];
}
[quizQuestions shuffle];
}
View Controller code excerpts
# pragma mark Cell Setup
//1st cell in tableview
- (void) setUpEquationCell: (UITableView *) tableView {
equationCell = (EquationCell *) [tableView dequeueReusableCellWithIdentifier:#"equationCell"];
if (equationCell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"EquationCell" owner:self options:nil];
equationCell = (EquationCell*) [nib objectAtIndex:0];
[equationCell.leftButton addTarget:self action:#selector(addDissociateCell) forControlEvents:UIControlEventTouchUpInside];
[equationCell.rightButton addTarget:self action:#selector(addBalanceCell) forControlEvents:UIControlEventTouchUpInside];
}
}
- (void) setUpBalanceCell: (UITableView *) tableView {
//2nd cell in tableview
balanceCell = (BalanceCell *) [tableView dequeueReusableCellWithIdentifier:#"balanceCell"];
if (balanceCell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"BalanceCell" owner:self options:nil];
balanceCell = (BalanceCell*) [nib objectAtIndex:0];
// stores data from equation cell into model
for (FormulaLabel *label in equationCell.leftView.equationOrder)
[leftData.equation addObject:label.text];
for (FormulaLabel *label in equationCell.rightView.equationOrder)
[rightData.equation addObject:label.text];
[leftData setUpBalancedEquation];
[rightData setUpBalancedEquation];
[self setUpView:balanceCell.leftView fromArray:leftData.equation toArray:leftBalanceItems];
[self setUpView:balanceCell.rightView fromArray:rightData.equation toArray:rightBalanceItems];
[self addBalanceTapMethodInArray:leftBalanceItems Data:leftData];
[self addBalanceTapMethodInArray:rightBalanceItems Data:rightData];
}
}
- (void) setUpDissociateCell: (UITableView *) tableView {
dissCell = (DissociateCell *) [tableView dequeueReusableCellWithIdentifier:#"dissCell"];
if (dissCell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"DissociateCell" owner:self options:nil];
dissCell = (DissociateCell*) [nib objectAtIndex:0];
leftData.disEquation = [[NSMutableArray alloc] init];
rightData.disEquation = [[NSMutableArray alloc] init];
// stores data from equation cell into model
for (FormulaLabel *label in equationCell.leftView.equationOrder)
[leftData.disEquation addObject:label.text];
for (FormulaLabel *label in equationCell.rightView.equationOrder)
[rightData.disEquation addObject:label.text];
[self setUpView:dissCell.leftView fromArray:leftData.disEquation toArray:leftDisItems];
[self setUpView:dissCell.rightView fromArray:rightData.disEquation toArray:rightDisItems];
[self addDissTapToArray:leftDisItems fromData:leftData inView:dissCell.leftView];
[self addDissTapToArray:rightDisItems fromData:rightData inView:dissCell.rightView];
[dissCell.dissociateButton addTarget:self action:#selector(dissociate) forControlEvents:UIControlEventTouchUpInside];
}
[dissCell.rightButton addTarget:self action:#selector(addBalanceCell) forControlEvents:UIControlEventTouchUpInside];
}
- (void)addDissociateCell {
[cellOrder addObject:#"dissociateCell"];
[table reloadData];
NSIndexPath *myIndexPath = [NSIndexPath indexPathForRow:0 inSection:([cellOrder count]-1)];
[table scrollToRowAtIndexPath:myIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
- (void) addDissTapToArray:(NSMutableArray*)viewOrder fromData:(EquationData*)data inView:(UIView*)view {
NSString *leftOrRight;
if (view == dissCell.leftView)
leftOrRight = #"left";
else
leftOrRight = #"right";
for (int j=0; j < [viewOrder count]; j++) {
if (j%2==0) {
UIView *formulaView = [viewOrder objectAtIndex:j];
//dissociate method
FalseTarget *target = [[FalseTarget alloc] initWithVC:self leftOrRightView:leftOrRight];
UITapGestureRecognizer* tap = [[UITapGestureRecognizer alloc] initWithTarget:target action:#selector(dissTap:)];
[formulaView addGestureRecognizer:tap];
// cancelling method
FalseTarget *target2 = [[FalseTarget alloc] initWithVC:self Data:data ViewList:viewOrder view:view];
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:target2 action:#selector(dissLongTap:)];
[formulaView addGestureRecognizer:longPress];
}
}
}
- (void)addCompoundToLabel:(UIGestureRecognizer *)recognizer leftOrRight:(NSString*)leftRight{
if( [recognizer state] == UIGestureRecognizerStateEnded ) {
FormulaLabel* label = (FormulaLabel*)[recognizer view];
dissIndex = label.tag;
dissCell.unDissociated.text = label.text;
currentDissCellView = leftRight;
}
}
- (void)dissociate {
EquationData *data;
NSMutableArray *viewOrder;
UIView *view;
if ([currentDissCellView isEqualToString:#"left"]) {
data = leftData;
viewOrder = leftDisItems;
view = dissCell.leftView;
}
else {
data = rightData;
viewOrder = rightDisItems;
view = dissCell.rightView;
}
FormulaLabel *c1 = [dissCell.leftTextField.equationOrder objectAtIndex:0];
FormulaLabel *c2 = [dissCell.rightTextField.equationOrder objectAtIndex:0];
[data updateDisEquationAtIndex:dissIndex withCompound1:c1.text Compound2:c2.text];
for (UIView *view in viewOrder)
[view removeFromSuperview];
[viewOrder removeAllObjects];
[self setUpView:view fromArray:data.disEquation toArray:viewOrder];
[self addDissTapToArray:viewOrder fromData:data inView:view];
}
- (void) cancelIons:(id)sender fromData:(EquationData *)data inView:(UIView *)view withViewList:(NSMutableArray *)viewlist {
if( [sender state] == UIGestureRecognizerStateEnded ) {
FormulaLabel* label = (FormulaLabel*)[sender view];
int index = label.tag;
[data.disEquation removeObjectAtIndex:index];
for (UIView *formulaView in viewlist)
[formulaView removeFromSuperview];
[viewlist removeAllObjects];
[self setUpView:view fromArray:data.disEquation toArray:viewlist];
[self addDissTapToArray:viewlist fromData:data inView:view];
}
}
- (void)addBalanceCell {
[cellOrder addObject:#"balanceCell"];
[table reloadData];
NSIndexPath *myIndexPath = [NSIndexPath indexPathForRow:0 inSection:([cellOrder count]-1)];
[table scrollToRowAtIndexPath:myIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
leftBalanceItems = [[NSMutableArray alloc] init];
rightBalanceItems = [[NSMutableArray alloc] init];
}
- (void) addBalanceTapMethodInArray:(NSMutableArray *)balanceItems Data:(EquationData *)data {
FalseTarget *target = [[FalseTarget alloc] initWithVC:self Data:data ViewList:balanceItems view:nil];
for (UIView *view in balanceItems) {
UITapGestureRecognizer* tap = [[UITapGestureRecognizer alloc] initWithTarget:target action:#selector(tap:)];
[view addGestureRecognizer:tap];
}
}
- (void)updateBalanceLabelofSender:(UIGestureRecognizer*)sender fromData:(EquationData *)data inArray:(NSMutableArray *)balanceItems {
FormulaLabel* label = (FormulaLabel*)[sender view];
int oldWidth = label.frame.size.width;
label.text = [data updateBalancedatIndex:label.tag];
[label sizeToFit];
int newWidth = label.frame.size.width;
int difference = newWidth - oldWidth;
int labelIndex = label.tag * 2;
for (int j = 0; j<[balanceItems count]; j++) {
// adjusts coordinate of all views to the right of tapped item
if (j > labelIndex){
UIView *item = [balanceItems objectAtIndex:j];
item.frame = CGRectMake(item.frame.origin.x + difference, item.frame.origin.y, item.frame.size.width, item.frame.size.height);
}
}
}
- (void)setUpView:(UIView *)view fromArray:(NSMutableArray *)equationData toArray:(NSMutableArray *)balanceItems {
int labelCount = 0; //label #
int startingPoint = 5; //x vaiue where first label starts
for (NSString *equationText in equationData) {
//add text
FormulaLabel *tempLabel = [[FormulaLabel alloc] initWithFrame:CGRectMake(startingPoint, 2, 10, 22)];
tempLabel.text = equationText;
[tempLabel sizeToFit];
[view addSubview:tempLabel];
tempLabel.tag = labelCount;
[balanceItems addObject:tempLabel];
//set location of '+'
startingPoint = tempLabel.frame.origin.x + tempLabel.frame.size.width + 3;
if (labelCount != [equationData count]-1) { //not the last label
UILabel *plus = [[[UILabel alloc] initWithFrame:CGRectMake(startingPoint, 5, 10, 10)]autorelease];
plus.text = #"+";
plus.font = [UIFont systemFontOfSize:13];
[plus sizeToFit];
[view addSubview:plus];
startingPoint = plus.frame.origin.x + plus.frame.size.width + 3;
[balanceItems addObject:plus];
}
labelCount ++;
[tempLabel release];
}
}
Why can't you use one view controller class with a question-property where you overload the property's setter method to update the UI to the new data:
in QuestionViewController.h file:
#property (retain, nonatomic) Question* myQuestionData;
in QuestionViewController.m
-(void) setMyQuestionData:(Question*)newData {
[myQuestionData autorelease];
myQuestionData = [newData retain];
[self updateUIElements];
}
I wouldn't do like that, because you are forcing the UIViewController to know about the data model (while making it it's property). What I would do, would be a class and using class methods (and not object methods) would create the data source. You would still have to hold the data in the UIViewController somehow, but I think a NSArray or a NSDictionary would be good enough:
-(void)viewDidLoad{
[superViewDidLoad];
myArray = [MyModel createDataSourceForTag:myTag];
}
I guess here you should go with Model structure.
1. Model: Model represents to entity. Here in your case, there i can create two entities Round, Question
#interface Round: NSObject {
NSUInteger *roundId; // 1, 2 = if it is required to check then this mi
NSArray *questions; // Array that contains objects for Question entity
}
#interface Question:NSObject {
NSString *questionText;
NSString *options1Text;
NSString *options2Text;
NSString *options3Text;
}
#interface DataManager:NSObject {
NSArray *rounds; // Array that contains object for Round entity
}
+(void)sharedInstance;
2. DataManger: DataManager will manage your entity objects. This data manager will be an sharedInstance class. So while initializing manager, entities will be created and data will be inserted.
3. ViewController: Now in view controller you can directly use sharedInstace, like this:
[[DataManager sharedInstance] rounds]
You can easily get code for creating sharedInstance class.
Now you can use this rounds anywhere in the entire application.
So it will be quiet easy to access. For example, if you require 1st round 2nd question 3rd option then, write code for same way
Round 1
Round *r = [[[DataManager sharedInstance] rounds] objectAtIndex:0];
Question 2
Question *q = [[r questions] objectAtIndex:1];
Question Text and Option 3
NSLog(#"questionText : %# 3rd Option: %#", q.questionText, q.option3Text)
Enjoy Coding :)

UIImageView added to an NSArray doesn't respond

I am fairly new to Objective-C, this is the first time I really don't know what to do.
I have a class - Parser - which creates UIImageViews, inits them with images and adds them to the UIView. The class also adds these UIImageViews to NSMutableArray - imgArray, which is a property of another class - Page.
Then, on the click of a button, I call the Page class from a Manager class, loop through the imgArray and try to set a new images for the UIImageViews in the array.
It doesn't work. Actually nothing I do to the UIImageViews doesn't work.
I try [img removeFromSuperView], [img setHidden:YES] and more. It doesn't response.
Here I declare the Array property in Page:
#property (nonatomic, retain) NSMutableArray *imgArray;
Here is the code from Parser where I create the image and add it to the array:
UIImageView *img = [[UIImageView alloc] initWithFrame: frame];
NSString *name = [c imageSource];
[img setImage: [UIImage imageFromBook: name]];
[view addSubview: img];
[c setImage:img];
if (!page.imgArray)
{
page.imgArray = [[NSMutableArray alloc] init];
}
[page.imgArray addObject:img];
[img release];
Here is the loop code from the Manager:
- (void) set3D:(bool)is3D
{
Page *page = [[DataManager data] currentPage];
int count = [page.imgArray count];
for (int i = 0; i < count; i++)
{
UIImageView *img = [page.imgArray objectAtIndex:i];
[img setImage:[UIImage imageNamed:image3DSource]];
}
}
Thanks in advance for any help!
There is no need to store the UIImageView's in an array.
When the UIImageView's are created, you can tag them with an arbitrary number, like.
#define K_MIN_IMGV 100
#define K_MAX_IMGV 120
- (void) setupViews
{
for (NSInteger count = K_MIN_IMGV; count < K_MAX_IMGV; ++count)
{
UIImageView *imgV = // create image view, set properties
imgV.tag = count; // tag the views for easy retrieval later
...
[mainView addSubview: imgV]; // add subviews
[imgV release], imgV = nil; // mainView now manages, release our allocation
}
}
// how to set new images
- (void) setupNewImages
{
for (NSInteger count = K_MIN_IMGV; count < K_MAX_IMGV; ++count)
{
UIImageView *imgV = (UIImageView *) [mainView viewWithTag: count]; // retrieve imageView
[imgV setImage: newImage]; // set new image
}
}
// To remove the imageView, you can use
UIImageView *imgV = (UIImageView *) [mainView viewWithTag: 123];
[imgV removeFromSuperview];
Have you tried to enumerate through the array using:
for(UIImageView* imageView in page.imgArray)
{
//Do code
}
This will tell you if there any imageViews in page.imgArray.
How are you adding objects to NSMutableArray and how are you initialising your NSMutableArray.
I remember having a problem adding objects to NSMutableArray but setting my init to solved this:
NSMutableArray* mutableArray = [NSMutableArray array];
Also what properties have you set on imgArray in page? Are you retaining the values?
Need to see more code to gain a fuller understanding.

How to instantiate a view from a xib?

I have a view in the xib file, I want to put that view into a window.... I'm using this code:
- (void) popupNotificationWithTag:(NSString *)tag fade:(double)msFade lineOne:(NSString *)lineOneText lineTwo:(NSString *)lineTwoText
{
NotificationWindow *notificationWindow;
NotificationWindow *tmpWindow;
NSEnumerator *enumerator;
// Walk the notification windows in the array
enumerator = [self.notificationWindows objectEnumerator];
if(enumerator)
{
while((tmpWindow = [enumerator nextObject]))
{
if([tmpWindow.tag isEqualToString:tag])
{
notificationWindow = tmpWindow;
}
}
}
// Make a new notification window
if (!notificationWindow)
{
int width = [[NSScreen mainScreen] frame].size.width;
int height = [[NSScreen mainScreen] frame].size.height;
notificationWindow = [[NotificationWindow alloc] initWithRect:NSMakeRect(width - 420, height - 130, 400, 100)];
NSNib *nib = [[NSNib alloc] initWithNibNamed:#"Notification" bundle: nil];
NSArray *objects;
[nib instantiateNibWithOwner:self topLevelObjects:&objects];
[notificationWindow setContentView: [objects objectAtIndex:0]];
[notificationWindow setTag:tag];
[self.notificationWindows addObject:notificationWindow];
}
// Display window
[notificationWindow makeKeyAndOrderFront:nil];
[notificationWindow display];
notificationWindow.fadeOut = msFade;
[notificationWindow setPrimaryText:lineOneText];
}
The view is the only thing in the xib file so I would have though the objectAtIndex:0 would have been OK, but I get an -[NSApplication setFrame:]: unrecognized selector sent to instance 0x100508500 exception at that line.
Update I replaced the line with this block of code:
for (id obj in objects) {
if ([[obj class] isSubclassOfClass:[NSView class]])
[notificationWindow setContentView: obj];
}
I vaguely recall that nibs have a hidden 1st object that represents the first responder (or something). Try objectAtIndex:1 instead and see if that works.

Problems Removing Annotations from a MKMapView

I am trying to remove all the annotations of a map. The class that contains the MKMapView implements the MKMapViewDelegate. I am calling the remove all annotations method when a button (displayed in a modal view) is clicked. I am using the next code to remove all the annotations.
- (void)removeAllAnnotations {
NSMutableArray *annotationsToRemove = [NSMutableArray arrayWithCapacity:[self.mapView.annotations count]];
for (int i = 0; i < [self.mapView.annotations count]; i++) {
if ([[self.mapView.annotations objectAtIndex:i] isKindOfClass:[AddressAnnotation class]]) {
[annotationsToRemove addObject:[self.mapView.annotations objectAtIndex:i]];
}
}
[self.mapView removeAnnotations:annotationsToRemove];
}
The code works right but after calling the method, and when I try to add new annotations to the empty map, the class does not call to the viewForAnnotations method and the annotations do not drop down and do not show a disclosure button into the annotation view. Why is this happening?
Thanks for reading.
Edited:
The annotations are shown but with out calling the view for annotation method (with out dropping down and with out including a disclosure button into the annotation view). Here is the method that I use to add the annotations and the viewForAnnotation method.
- (void)loadAnnotations:(NSString *)type {
NSString *path = [[NSBundle mainBundle] pathForResource:#"PlacesInformation" ofType:#"plist"];
NSMutableArray *tmpArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
for (int i = 0; i < tmpArray.count; i++) {
// Breaks the string down even further to latitude and longitude fields.
NSString *coordinateString = [[tmpArray objectAtIndex:i] objectForKey:#"coordinates"];
NSString *option = [[tmpArray objectAtIndex:i] objectForKey:#"type"];
if ([option isEqualToString:type]) {
CLLocationCoordinate2D currentCoordinate = [self stringToCoordinate:coordinateString];
AddressAnnotation *annotation = [[[AddressAnnotation alloc] initWithCoordinate:currentCoordinate] autorelease];
[self.mapView addAnnotation:annotation];
}
}
}
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation {
// If it's the user location, just return nil.
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
else { // Handles the other annotations.
// Try to dequeue an existing pin view first.
static NSString *AnnotationIdentifier = #"AnnotationIdentifier";
MKPinAnnotationView *pinView = (MKPinAnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier:AnnotationIdentifier];
if (!pinView) {
// If an existing pin view was not available, creates one.
MKPinAnnotationView *customPinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier] autorelease];
customPinView.animatesDrop = YES;
customPinView.canShowCallout = YES;
// Adds a detail disclosure button.
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton addTarget:self action:#selector(showDetails:) forControlEvents:UIControlEventTouchUpInside];
customPinView.rightCalloutAccessoryView = rightButton;
return customPinView;
} else
pinView.annotation = annotation;
}
return nil;
}
In viewForAnnotation, in the case where you are reusing a view, you are currently returning nil.
This else part:
} else
pinView.annotation = annotation;
Should probably be:
} else {
pinView.annotation = annotation;
return pinView;
}