I am trying to learn Objective-C for iOS. i am currently following the "coding together" on iTunesU. Although i have got stuck since i can't get my controller to call on a method from another class. Can't find what i am doing wrong and thought that StackOverflow might have the solution to it!
The method "flipCardAtIndex" is the one that isn't working. I have debugged using nslog and from the method "flipCard" i get an output. But when i put in the implementation for flipCardAtIndex i don't get anything.. So my guess is that it never calls it...
I have made the code a bit shorter so it is only the parts i think is important, this is controller:
#import "ViewController.h"
#import "PlayingCardDeck.h"
#import "CardMatchingGame.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UILabel *flipsLabel;
#property (nonatomic) int flipCount;
#property (weak, nonatomic) IBOutlet UILabel *scoreLabel;
#property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *cardButtons;
#property (strong, nonatomic) CardMatchingGame *game;
#end
#implementation ViewController
- (CardMatchingGame *) game{
if (_game) _game = [[CardMatchingGame alloc] initWithCardCount:[self.cardButtons count]
usingDeck:[[PlayingCardDeck alloc] init]];
return _game;
}
- (IBAction)flipCard:(UIButton *)sender {
[self.game flipCardAtIndex:[self.cardButtons indexOfObject:sender]];
self.flipCount++;
[self updateUI];
}
And implementation:
- (void)flipCardAtIndex:(NSUInteger)index
{
NSLog(#"ALL YOUR BASE ARE BELONG TO US");
Card *card = [self cardAtIndex:index];
}
Fix?
- (CardMatchingGame *) game{
if (!_game) _game = [[CardMatchingGame alloc] initWithCardCount:[self.cardButtons count] usingDeck:[[PlayingCardDeck alloc] init]];
return _game;
}
Related
I have an app where I need to present an overlay for feedback. I started by simply creating the exact thing I wanted within the UIViewController I wanted. However this presents to 2 problems. 1) I can't reuse this in another view (As I need to now) and 2) Because it's an overlay, it covers the entire UIViewController on the storyboard so I can't see the controls beneath it.
I looked at moving to an external UIView .xib file and loading dynamically which worked great, except whatever I did, I couldn't never get a handle on the labels within the nib to update the text.
Then I decided that making it a class and creating a delegate method for it would probably be the best way forward.
I have created a very simply .xib and laid it out as well as a .h and .m file (overlayView) and wired it all in an it looks good, except when trying to present the overlayView I get a exc_bad_access on the line
[window addSubview:self];
And I can't work out why. Full code below:
overlayView.h
#import <UIKit/UIKit.h>
#class overlayView;
#protocol overlayDelegate;
#interface overlayView : UIView
#property (nonatomic, strong) id <overlayDelegate> delagate;
-(instancetype)initWithTitle:(NSString *)title
dateFrom:(NSString *)dateFrom
dateTo:(NSString *)dateTo
description:(NSString *)description;
#property (strong, nonatomic) IBOutlet UILabel *overlayTitleLbl;
#property (strong, nonatomic) IBOutlet UILabel *overlayDateFromLbl;
#property (strong, nonatomic) IBOutlet UILabel *overlayDateToLbl;
#property (strong, nonatomic) IBOutlet UILabel *overlayDescLbl;
#property (strong, nonatomic) IBOutlet UILabel *overlayIcon;
-(void)showOverlay;
-(void)dismissOverlay;
#end
#protocol overlayDelegate <NSObject>
#optional
#end
overlayView.m
#import "overlayView.h"
#import "NSString+FontAwesome.h"
#implementation overlayView
- (instancetype)initWithTitle:(NSString *)title dateFrom:(NSString *)dateFrom dateTo:(NSString *)dateTo description:(NSString *)description {
self.overlayViewTitleLbl.text = title;
self.overlayViewDateFromLbl.text = dateFrom;
self.overlayViewDateToLbl.text = dateTo;
self.overlayViewDescLbl.text = description;
self.overlayViewIcon.text = [NSString fontAwesomeIconStringForIconIdentifier:#"fa-calendar"];
return self;
}
-(void)showOverlay {
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
[window addSubview:self]; <-- Code causing issue
[window makeKeyAndVisible];
}
-(void)dismissOverlay {
// Not wired in yet
}
#end
The being called in my main view controller like:
overlay = [[overlayView alloc] initWithTitle:[tmpDict objectForKeyedSubscript:#"Title"] dateFrom:startDate dateTo:stopDate description:[tmpDict objectForKeyedSubscript:#"Desc"]];
[overlay showOverlay];
Any ideas why this doesn't want to play ball? I have breakpointed the initWithTitle method and all information is being passed correctly so I think I am very close to what I am trying to achieve.
you need to initiate your view first, you're returning self without initiating it
- (instancetype)initWithTitle:(NSString *)title dateFrom:(NSString *)dateFrom dateTo:(NSString *)dateTo description:(NSString *)description {
self = [super init];
if (self) {
self.overlayViewTitleLbl.text = title;
self.overlayViewDateFromLbl.text = dateFrom;
self.overlayViewDateToLbl.text = dateTo;
self.overlayViewDescLbl.text = description;
self.overlayViewIcon.text = [NSString fontAwesomeIconStringForIconIdentifier:#"fa-calendar"];
}
return self;
}
I am creatinng an instance of a View like this...
- (IBAction)findPatientTapped:(id)sender {
RequestDialogViewController *findPatientViewController = [[RequestDialogViewController alloc] initWithNibName:#"RequestDialogViewController" bundle:nil];
findPatientViewController.delegate = self;
findPatientViewController.autoCorrectOff = YES;
findPatientViewController.defaultResponse = #"";
findPatientViewController.keyboardType = #"ASCII";
findPatientViewController.returnKeyType = #"Go";
findPatientViewController.tag = findRequestTag;
findPatientViewController.editSet = YES;
findPatientViewController.titleText = #"Find Patient";
findPatientViewController.messageText =#"Enter all or a portion of the patient's name...";
findPatientViewController.messageText2 = #"Last Name...";
findPatientViewController.messageText3 = #"First Name...";
findPatientViewController.showResponse2 = YES;
findPatientViewController.showNavBarSaveButton = NO;
findPatientViewController.infoTextFile = #"";
findPatientViewController.view.frame = CGRectMake(280, 230, 480, 300);
[self.view addSubview:findPatientViewController.view];
}
The view contains 2 UITextField fields for entry by the user. This all worked fine before running the convert to ARC tool. After the conversion the view crashes when trying to enter a value in either of the 2 fields with...
-[RequestDialogViewController respondsToSelector:]: message sent to deallocated instance
Here is the .h file of the UIViewController after running the conversion to ARC tool...
#import <UIKit/UIKit.h>
#protocol RequestDialogViewControllerDelegate;
#interface RequestDialogViewController : UIViewController <UITextFieldDelegate>{
id<RequestDialogViewControllerDelegate> __weak delegate;
IBOutlet UINavigationBar *navBar;
IBOutlet UITextField *response;
IBOutlet UITextField *response2;
IBOutlet UILabel *message;
IBOutlet UILabel *message2;
IBOutlet UILabel *message3;
IBOutlet UIButton *saveButton;
NSTimer *selectAllTimer;
NSString *defaultResponse;
NSString *titleText, *infoTextFile;
NSString *messageText, *messageText2, *messageText3;
NSString *placeHolderText;
NSString *keyboardType, *returnKeyType;
BOOL editSet, showSaveButton,showNavBarSaveButton, showResponse2, autoCorrectOff;
int tag;
}
#property (weak) id<RequestDialogViewControllerDelegate> delegate;
#property (nonatomic, strong)IBOutlet UITextField *response;
#property (nonatomic, strong) IBOutlet UITextField *response2;
#property (nonatomic, strong)IBOutlet UILabel *message;
#property (nonatomic, strong)IBOutlet UILabel *message2;
#property (nonatomic, strong) IBOutlet UILabel *message3;
#property (nonatomic, strong)IBOutlet UIButton *saveButton;
#property (nonatomic, strong)NSString *defaultResponse;
#property (nonatomic, strong)NSString *titleText, *infoTextFile;
#property (nonatomic, strong)NSString *messageText, *messageText2, *messageText3;
#property (nonatomic, strong)NSString *placeHolderText;
#property (nonatomic, strong)NSString *keyboardType, *returnKeyType;
#property (nonatomic, strong)IBOutlet UINavigationBar *navBar;
#property (readwrite)BOOL editSet, showSaveButton, showNavBarSaveButton, showResponse2, autoCorrectOff;
#property (readwrite)int tag;
#property (nonatomic, strong) NSTimer *selectAllTimer;
- (IBAction)saveButtonPressed:(id)sender;
- (void)selectAll;
- (IBAction)editingDidEnd:(id)sender;
#end
#protocol RequestDialogViewControllerDelegate <NSObject>
#optional
- (void)requestDialogViewDidDismiss:(RequestDialogViewController *)controller withResponse:(NSString*)reqResponse response2:(NSString*)reqResponse2;
#end
Here is the pertenant references in the .h file of the class creating the instance of the RequestDialog view...
#import "RequestDialogViewController.h"
#interface OrthoViewController : UIViewController <RequestDialogViewControllerDelegate>{
}
- (void)requestDialogViewDidDismiss:(RequestDialogViewController *)controller withResponse:(NSString*)reqResponse response2:(NSString*)reqResponse2;
#end
What do I need to do to make this work under ARC? I am thinking it might have something to do with how the protocol is formed.
Thanks,
John
**** Thanks to Dan I resolved this by making findPatientViewController a property of the calling class...
RequestDialogViewController *findPatientViewController;
#implementation OrthoViewController
- (IBAction)findPatientTapped:(id)sender {
findPatientViewController = [[RequestDialogViewController alloc] initWithNibName:#"RequestDialogViewController" bundle:nil];
//set all the properties and add the subview
}
#end
Your findPatientViewController has nothing retaining it, so it gets deallocated at the end of the method where you create it. Then, when something in it's view tries to call a delegate method on it you get that crash.
If findPatientTapped is a method in a view controller then you should add the findPatientViewController as a child view controller. If it's in a view, then you need to least store the findPatientViewController in a property so it doesn't get deallocated while you are still using it.
Your code didn't really work properly before ARC, you just had a memory leak.
I have the class LearnfestItem.h :
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface LearnfestItem : NSObject
#property (strong, nonatomic) NSString * itemId;
#property (strong, nonatomic) NSString * itemTitle;
#property (strong, nonatomic) NSString * itemDescription;
#property (strong, nonatomic) NSString * itemContent;
#property (strong, nonatomic) NSString * itemType;
#property (strong, nonatomic) UIImage * itemImage;
#property (strong, nonatomic) NSDate * itemRegistered;
-(id)initWithData:(NSDictionary *)data andImage:(UIImage *)image;
#end
& Object.m :
#import "LearnfestItem.h"
#import "Defaults.h"
#implementation LearnfestItem
-(id)init
{
self = [self initWithData:nil andImage:nil];
return self;
}
-(id)initWithData:(NSDictionary *)data andImage:(UIImage *)image
{
self = [super init];
self.itemId = data[ITEM_ID];
self.itemTitle = data[ITEM_TITLE];
self.itemDescription = data[ITEM_DESCRIPTION];
self.itemContent = data[ITEM_CONTENT];
self.itemType = data[ITEM_TYPE];
self.itemImage = image;
self.itemRegistered = data[ITEM_REGISTERED];
return self;
}
#end
In my UIViewController I have a UITableView that creates a NSMutableArray of LearnfestItems within the cellForRowAtIndexPath method:
LearnfestItem * createLearnfestItem = [[LearnfestItem alloc] initWithData:learnfestItemDictionary andImage:learnfestItemImage];
NSLog(#"Insert learnfest item with id: %# at index %li", createLearnfestItem.itemId, (long)row);
[self.learnfestItemObjects insertObject:createLearnfestItem atIndex:row];
On didSelectRowAtIndexPath I want to receive the LearnfestItem from the NSMutableArray I do this by calling:
self.selectedLearnfestItem = [self.learnfestItemObjects objectAtIndex:indexPath.row];
Then I want to send it to another view controller to present the data I do this in the prepareForSegue segement:
LearnfestItemViewController * learnfestVC = [segue destinationViewController];
NSLog(#"Sending learnfest item with id: %#", self.selectedLearnfestItem.itemId);
learnfestVC.item = self.selectedLearnfestItem;
When I try to access my LearnfestItem's properties within cellForRowAtIndexPath. All I get is null... and so forth in my other table view delegate methods.. Can anyone spot what i'm doing wrong? Thanks
Code you show is missing all error and validity checking. When using NSAssert() and item.length/item.count checks, you will know what's going on.
However, based on your code I'd suggest two things:
All objects which also have mutable version should use "copy" properties. Now you have "strong" pointer to data received via init method, but if you reset the originally given data variable to point elsewhere (e.g. reusing one data variable inside for loop to init multiple items), then... I don't know what your LearnfestItem item properties will point to.
Make sure your LearnfestItemViewController *item is a copy, too.
My guess is that everything is initialised correctly, but the data disappears afterwards.
For example:
#property (nonatomic, copy) NSString * itemId; // Safe
#property (nonatomic, strong) NSString * itemId; // Not safe
When your property class has mutable variations (e.g. NSString vs. NSMutableString vs. MyMutableString), copy is safer. Using strong will create a pointer to original data, which could have been a mutable instance and could be modified afterwards. Using strong will always point to original data, even after it has been changed.
Second part:
learnfestVC.item = self.selectedLearnfestItem;
Your LearnfestItemViewController contains some property related to LearnfestItem class. Make sure it's a copy, too. When using segues, the calling object quite often just disappears. Make sure your new controller has local copy of all needed data (or use a protocol delegate, but that's a longer discussion)
Adding error and data validity checking will make your task a lot easier. Instead of trying to figure out afterwards why something doesn't work, you'll get notifications when something isn't as you're expecting.
Here's quick and dirty "maintenance" for your code. What you should get out of this is some ideas what to check, where and how. In normal situations this is overkill, but now you have a mysterious problem and need to find it. It can be hard and monotonous work.
#import Foundation;
#import UIKit;
#interface LearnfestItem : NSObject
#property (copy, nonatomic) NSString *itemId;
#property (copy, nonatomic) NSString *itemTitle;
#property (copy, nonatomic) NSString *itemDescription;
#property (copy, nonatomic) NSString *itemContent;
#property (copy, nonatomic) NSString *itemType;
#property (strong, nonatomic) UIImage *itemImage;
#property (strong, nonatomic) NSDate *itemRegistered;
- (instancetype)initWithData:(NSDictionary *)data andImage:(UIImage *)image;
#end
Object.m :
#import "Defaults.h"
#import "LearnfestItem.h"
#implementation LearnfestItem
- (instancetype)init
{
self = [self initWithData:nil andImage:nil];
return self;
}
- (instancetype)initWithData:(NSDictionary *)data andImage:(UIImage *)image
{
NSAssert(data.length, #"My Assert: missing data");
NSAssert(image, #"My Assert: missing image");
if ((self = [super init]))
{
// TODO: nil ok, if doesn't exist?
_itemId = data[ITEM_ID];
_itemTitle = data[ITEM_TITLE];
_itemDescription = data[ITEM_DESCRIPTION];
_itemContent = data[ITEM_CONTENT];
_itemType = data[ITEM_TYPE];
_itemImage = image;
_itemRegistered = data[ITEM_REGISTERED];
}
return self;
}
#end
"(original text) In my UIViewController I have a UITableView that creates a NSMutableArray of LearnfestItems within the cellForRowAtIndexPath method:"
NSAssert(learnfestItemDictionary.count, #"My Assert: missing dict");
NSAssert(learnfestItemImage, #"My Assert: missing image");
LearnfestItem *createLearnfestItem = [[LearnfestItem alloc] initWithData:learnfestItemDictionary andImage:learnfestItemImage];
NSLog(#"Insert learnfest item with id: %# at index %#", createLearnfestItem.itemId, #(row));
NSAssert(createLearnfestItem, #"My Assert: missing item");
NSAssert(self.learnfestItemObjects.count > row, #"My Assert: learnfestItemObjects");
self.learnfestItemObjects[row] = createLearnfestItem;
"(original text) On didSelectRowAtIndexPath I want to receive the LearnfestItem from the NSMutableArray I do this by calling:"
NSAssert(self.learnfestItemObjects.count > indexPath.row, #"My Assert: learnfestItemObjects");
self.selectedLearnfestItem = self.learnfestItemObjects[indexPath.row];
"(original text) Then I want to send it to another view controller to present the data I do this in the prepareForSegue segment:"
LearnfestItemViewController *learnfestVC = (LearnfestItemViewController *)[segue destinationViewController];
NSLog(#"Sending learnfest item with id: %#", self.selectedLearnfestItem.itemId);
learnfestVC.item = self.selectedLearnfestItem;
Hi everyone I have been building an app and have met some problems.
My app has two viewControllers. One is MenuViewController and another one is MainViewController.
I want to pass a string from MainViewController to a mutableArray in MenuViewController, but have no idea how.
Here are my codes:
<MenuViewController.h>
#import <UIKit/UIKit.h>
#interface MenuViewController : UITableViewController {
NSMutableArray *secondFavourite;
}
#property (nonatomic, strong) NSMutableArray *secondFavourite;
#end
.
<MenuViewController.m>
#import "MenuViewController.h"
#import "MainViewController.h"
#interface MenuViewController ()
#property (strong, nonatomic) NSArray *menu;
#end
#implementation MenuViewController
#synthesize menu;
#synthesize secondFavourite;
- (void)viewDidLoad
{
[super viewDidLoad];
self.secondFavourite = [[NSMutableArray alloc] init];
self.menu = self.secondFavourite;
}
.
<MainViewController.h>
#import <UIKit/UIKit.h>
#import <social/Social.h>
#interface MainViewController : UIViewController {
IBOutlet UIImageView *imagepost;
UILabel *predictionLabel;
}
- (IBAction)sampleSelector:(UIButton *)sender;
- (IBAction)showAllClick:(id)sender;
#property (nonatomic, retain) IBOutlet UILabel *predictionLabel;
#property (strong, nonatomic) NSArray *predictionArray;
#property (strong, nonatomic) UIButton *menuBtn;
#property (strong, nonatomic) NSMutableArray *fav;
#property (strong, nonatomic) IBOutlet UILabel *favLabel;
#property (strong, nonatomic) IBOutlet UITableView* tableView;
#property (nonatomic, strong) NSMutableArray *favourite;
#end
.
<MainViewController.m>
- (void)viewDidLoad
{
[super viewDidLoad];
self.predictionArray = [[NSArray alloc] initWithObjects:#"Hey gurl", nil];
}
//Add to favourite
- (void) addToFav {
self.favourite = [[NSMutableArray alloc] init];
[self.favourite addObject:self.predictionLabel.text];
[self.tableView reloadData];
NSLog(#"%#", self.favourite);
}
//add to favourite button action
- (IBAction)addToFavButton:(id)sender {
[self addToFav];
//pass data from favourite here to secondFacourite in MenuViewController (found on stack overflow)
MenuViewController *menuViewController = [[MenuViewController alloc]initWithNibName:#"MenuViewController" bundle:nil];
menuViewController.secondFavourite = [[NSMutableArray alloc]initWithArray:self.favourite];
[self.navigationController pushViewController:menuViewController animated:YES];
}
I used NSLog to check that the menuViewController.secondFavourite in MainViewController successfully added the string into the array, isn't the array the same array in MenuViewController? Why doesn't the menu.tableView update and show the new string added? I am very confused and I hope someone will help me out.
Thanks for reading this.
This has to do with the fact that your menu viewDidLoad is overwriting the value in these two lines:
self.secondFavourite = [[NSMutableArray alloc] init];
self.menu = self.secondFavourite;
That first line is setting your secondFavourite property to an empty NSMutableArray instance. And since viewDidLoad will be called only after the view has been loaded into memory (in this case, when you try to push the view controller onto the stack), your initial values in the secondFavourite property will be lost.
Instead, you should move that initialization code into the an init method.
In my main view controller, I have a UITextField, and I am trying to save the text input into it to a NSString in my Homework model(class).
Homework.h
#import <Foundation/Foundation.h>
#interface Homework : NSObject
#property (nonatomic, strong) NSString *className;
#property (nonatomic, strong) NSString *assignmentTitle;
#end
Homework.m
#import "Homework.h"
#implementation Homework
#synthesize className = _className;
#synthesize assignmentTitle = _assignmentTitle;
#end
In my assignmentViewController, I create an object of type Homework and try to set it equal to whatever is entered into the the UITextField when the Save Button is pressed.
Assignment View Controller
#import <UIKit/UIKit.h>
#import "Homework.h"
#interface Assignment : UIViewController {
}
#property(nonatomic) IBOutlet UITextField *ClassNameField;
#property(nonatomic) IBOutlet UILabel *ClassNameLabel;
#property (weak, nonatomic) IBOutlet UIButton *SaveButton;
#property (nonatomic, strong) Homework *homeworkAssignment;
- (IBAction)Save:(UIButton *)sender;
#end
AssignmentViewController.m
- (IBAction)Save:(UIButton *)sender {
self.homeworkAssignment.className = self.ClassNameField.text;
NSLog(#"SaveButtonPressed %#", self.homeworkAssignment.className);
}
The NSLog prints out that className is (null). Can anyone help me figure out what I am doing wrong? This is my first ever iOS app (besides Hello World).
Edit: This is using ARC
Edit: I tried changing
self.homeworkAssignment.className = self.ClassNameField.text; to
self.homeworkAssignment.className = #"TEST";
and the log still shows (Null).
Double check you properly linked ClassNameField outlet and that you're initializing homeworkAssignment. Something like.-
self.homeworkAssignment = [[Homework alloc] init];
By the way, you should consider using camelCase notation for your variable names :)
Well to be honest the first steps are always hard but you should learn it the right way, héhé
First of all synthesize this way:
#synthesize labelAssignmentTitle,labelClassName;
or
#synthesize labelAssignmentTitle;
#synthesize labelClassName;
there is no need to do the following:
#synthesize className = _className;
#synthesize assignmentTitle = _assignmentTitle;
Now if you initialize the right way from the the start you'll find it a lot easier later!
HomeWork.h
#interface HomeWork : NSObject
#property (nonatomic, strong) NSString *className;
#property (nonatomic, strong) NSString *assignmentTitle;
-(id)initWithClassName:(NSString *)newClassName andAssignmentTitle:(NSString*)newAssigmentTitle;
HomeWork.m
#implementation HomeWork
#synthesize assignmentTitle,className;
-(id)initWithClassName:(NSString *)newClassName andAssignmentTitle:(NSString*)newAssigmentTitle {
self = [super init];
if(self){
assignmentTitle = newAssigmentTitle;
className = newClass;
}
return self;
}
#end
ViewController.m
- (IBAction)saveIt:(id)sender {
HomeWork *newHomeWork = [[HomeWork alloc]initWithClassName:[labelClassName text]andAssignmentTitle:[labelAssignmentTitle text]];
}
Because of this, you directly make a newHomeWork object with the parameters given by your two UITextFields.
Now print it out in your logmessage and see what happends ;)