I'm trying to write an app that takes photo from contact named Jan Kowalski and shows on screen.
I'm pretty new to iOS and xCode and so I got errors in my code.
Here's my .m file
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
//#synthesize labelNewImage;
#synthesize labelOldImage;
//#synthesize imageViewNew;
#synthesize imageViewOld;
- (void) changePositionOfView:(UIView *)paramView to:(CGFloat)paramY
{
CGRect viewFrame = paramView.frame;
viewFrame.origin.y = paramY;
paramView.frame = viewFrame;
}
- (void) createLabelAndImageViewForOldImage
{
self.labelOldImage = [[UILabel alloc] initWithFrame:CGRectZero];
self.labelOldImage.text = #"Obraz";
self.labelOldImage.font = [UIFont systemFontOfSize:16.0f];
[self.labelOldImage sizeToFit];
self.labelOldImage.center = self.view.center;
[self.view addSubview:self.labelOldImage];
[self changeYPositionOfView:[self.labelOldImage
to:80.0f]];// error - no visible #interface for 'UILabel' that declares the selector to
self.imageViewOld = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)];
self.imageViewOld.center = self.view.center;
self.imageViewOld.contentMode = UIViewContentModeScaleAspectFit;
[self.view addSubview:self.imageViewOld];
[self changeYPositionOfView:[self.imageViewOld to:105.0f]];
}
- (ABRecordRef)getPersonWithFirstName:(NSString *)paramFirstName
lastName:(NSString *)paramLastName
inAddressBook:(ABRecordRef)paramAddressBook
{
ABRecordRef result = NULL;
if (paramAddressBook == NULL) {
NSLog(#"Ksiazka ma wartosc NULL");
return NULL;
}
NSArray *allPeople = (__bridge_transfer NSArray *)
ABAddressBookCopyArrayOfAllPeople(paramAddressBook);
NSUInteger peopleCounter = 0;
for (peopleCounter=0; peopleCounter < [allPeople count]; peopleCounter++) {
ABRecordRef person = (__bridge ABRecordRef)
[allPeople objectAtIndex:peopleCounter];
NSString *firstName = (__bridge_transfer NSString *)
ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSString *lastName = (__bridge_transfer NSString *)
ABRecordCopyValue(person, kABPersonLastNameProperty);
BOOL firstNameIsEqual = NO;
BOOL lastNameIsEqual = NO;
if ([firstName length] == 0 && [paramFirstName length] == 0)
{
firstNameIsEqual = YES;
}
else if ([firstName isEqualToString:paramFirstName])
{
firstNameIsEqual = YES;
}
if ([lastName length] == 0 && [paramLastName length] == 0)
{
lastNameIsEqual = YES;
}
else if ([lastName isEqualToString:paramLastName])
{
lastNameIsEqual = YES;
}
if (firstNameIsEqual && lastNameIsEqual) {
return person;
}
}
return result;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor blackColor];
[self createLabelAndImageViewForOldImage];
ABAddressBookRef addressBook = ABAddressBookCreate();
if (addressBook !=NULL) {
ABRecordRef janKowalski = [self getPersonWithFirstName:#"Jan" lastName:#"Kowalski" inAddressBook:addressBook];
if (janKowalski == NULL) {
NSLog(#"Nie znaleziono kontaktu tworzenie nowego.");
janKowalski = [self newPersonWithFirstName:#"Jan" // error - no visible #interface for 'ViewController' that declares the selector 'newPersonWithFirstName
lastName:#"Kowalski"
inAddressBook:addressBook];
}
if (janKowalski == NULL) {
NSLog(#"Nie udało się utworzyć nowego rekordu dla tego kontaktu.");
CFRelease(addressBook);
return;
}
self.imageViewOld.image = [[self getPersonImage]:janKowalski]; // error - no visible #interface for 'ViewController' delcares the selector 'getPersonImage'
}
}
and my .h file:
#import <UIKit/UIKit.h>
#import <AddressBook/AddressBook.h>
#interface ViewController : UIViewController
#property (strong, nonatomic) UILabel *labelOldImage;
#property (strong, nonatomic) UIImageView *imageViewOld;
#end
I'm getting these "no visible #interface for 'UILabel' that declares the selector to" errors.
Could you guys get me on the right way how to solve this problem?
Thank you so much!
It's really just an error in placing square brackets. You've defined a method with two parameters...
changePositionOfView:(UIView *)paramView to:(CGFloat)paramY
...but you're calling it with only one...
[self changeYPositionOfView:[self.labelOldImage to:80.0f]];
Check the matching of the brackets.
The compiler thinks you want to call [self.labelOldImage to:80.0f] and pass the result to a single-parameter method called justchangeYPositionOfView:.
Pass values to both parameters by fixing the nesting:
[self changeYPositionOfView:self.labelOldImage to:80.0f];
The problem:
- changePositionOfView:(UIView *)paramView to:(CGFloat)paramY
But you send this:
[self changeYPositionOfView:[self.labelOldImage
to:80.0f]];// error - no visible #interface for 'UILabel' that declares the selector to
Make it with 2 params.
And yeah, when you use #synthesize, after that call label/image with labelOldImager, not self.labelOldImage.
Related
I tried to use the core data model class as my custom annotation, but somehow the viewForAnnotation never gets called.
Here is my view controller, which contains all the core data snippet and how I implement the delegate method
#import "SkyCastViewController.h"
#import "AppDelegate.h"
#import "Pixel-Swift.h"
#import "Photo+Annotation.h"
static NSString* const NavigationBarTitleFontName = #"Avenir-Heavy";
static CGFloat const NavigationBarTitleFontSize = 17;
static NSString* const MapViewReuseIdentifier = #"AnnotationViweIden";
#interface SkyCastViewController () <MKMapViewDelegate>
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
#property (strong, nonatomic) NSArray* photos;
#property (strong, nonatomic) UIManagedDocument* document;
#end
#implementation SkyCastViewController
- (void) viewDidLoad{
[super viewDidLoad];
[self updateUI];
self.mapView.delegate = self;
}
- (void) viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
NSFileManager* fileManager = [NSFileManager defaultManager];
NSURL* docsDir = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].firstObject;
if(docsDir){
NSURL* url = [docsDir URLByAppendingPathComponent:#"storage"];
self.document = [[UIManagedDocument alloc] initWithFileURL: url];
if(self.document.documentState != UIDocumentStateNormal){
if([[NSFileManager defaultManager] fileExistsAtPath: url.path]){
//the document exists, open it
[self.document openWithCompletionHandler:^(BOOL success){
if(success){
[self documentInit];
}
}];
}else{
//the document does not exist, create one
[self.document saveToURL:url forSaveOperation: UIDocumentSaveForCreating completionHandler:^(BOOL success){
//post a notification that document is ready
if(success){
NSLog(#"saveToURL succeed");
}else{
NSLog(#"saveToURL falied");
}
}];
}
}
}
}
// document ready observer
- (void) documentInit{
dispatch_async(dispatch_get_main_queue(), ^{
NSManagedObjectContext* context = self.document.managedObjectContext;
NSError* error;
//fetch objects
NSFetchRequest* request = [[NSFetchRequest alloc] initWithEntityName:#"User"];
request.fetchBatchSize = 10;
request.fetchLimit = 100;
NSString* nameAttr = #"name";
NSString* nameValue = #"Kesong Xie";
request.predicate = [NSPredicate predicateWithFormat:#"%K like %#", nameAttr, nameValue];
NSArray* users = [context executeFetchRequest:request error: &error];
if(error != nil){
NSLog(#"%#", error.localizedDescription);
}else{
if([users count] > 0){
for(User* user in users){
self.photos = (NSArray<Photo<MKAnnotation>*>*)user.photo.allObjects;
[self.mapView addAnnotations:self.photos];
}
}
}
NSLog(#"%#", self.photos);
// [self.mapView showAnnotations: self.photos animated: YES];
});
}
//MARK: - UPATE UI
- (void) updateUI{
[self.navigationController.navigationBar setBarTintColor: [UIColor blackColor]];
UIFont* titleFont = [UIFont fontWithName: NavigationBarTitleFontName size: NavigationBarTitleFontSize];
[self.navigationController.navigationBar setTitleTextAttributes:#{NSFontAttributeName: titleFont, NSForegroundColorAttributeName: [UIColor whiteColor]}];
}
- (UIStatusBarStyle) preferredStatusBarStyle{
return UIStatusBarStyleLightContent;
}
//MARK: - MKMapViewDelegate
-(MKAnnotationView*) mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
NSLog(#"called from viewForAnnotation");
if([annotation isKindOfClass:[ MKUserLocation class]]){
return nil;
}
MKAnnotationView* annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier: MapViewReuseIdentifier];
if(!annotationView){
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:MapViewReuseIdentifier];
}else{
annotationView.annotation = annotation;
}
UIImage* shot = [[UIImage alloc] initWithContentsOfFile:#"shot3"];
annotationView.image = shot;
annotationView.frame = CGRectMake(0, 0, 60, 60);
annotationView.clipsToBounds = YES;
return annotationView;
}
#end
now the only output on the console is
Pixel[1832:440548] (
"<Pixel.Photo: 0xa03a400> (entity: Photo; id: 0x17e905f0 <x-coredata://FB9198FE-57D1-4C88-945B-0B04D49821C0/Photo/p42> ; data: {\n latitude = \"32.88831721994364\";\n longitude = \"-117.2413945199151\";\n thumbnailData = nil;\n time = nil;\n title = \"Beach Walking\";\n whoTook = \"0xa040660 <x-coredata://FB9198FE-57D1-4C88-945B-0B04D49821C0/User/p10>\";\n})",
"<Pixel.Photo: 0xa039440> (entity: Photo; id: 0xa038b40 <x-coredata://FB9198FE-57D1-4C88-945B-0B04D49821C0/Photo/p43> ; data: {\n latitude = \"32.88831721994364\";\n longitude = \"-117.2413945199151\";\n thumbnailData = nil;\n time = nil;\n title = \"Aerial Shots of Sedona Arizona\";\n whoTook = \"0xa040660 <x-coredata://FB9198FE-57D1-4C88-945B-0B04D49821C0/User/p10>\";\n})"
)
Can you please share your viewController code because as per your code shared I can assume that your MapView is initiating after this code.
if(error != nil){
NSLog(#"%#", error.localizedDescription);
}else{
if([users count] > 0){
for(User* user in users){
self.photos = (NSArray<Photo<MKAnnotation>*>*)user.photo.allObjects;
[self.mapView addAnnotations:self.photos];
}
}
}
NSLog(#"%#", self.photos);
Because in your console these line print right after your MapView is loaded
2016-12-09 16:19:42.101997 Pixel[1646:397556] viewForAnnotation called
2016-12-09 16:19:42.102120 Pixel[1646:397556] annotation is the MKUserLocation
These line should print before your CoreData results
I think I understand why I can't load my pin now. The line
self.photos = (NSArray<Photo<MKAnnotation>*>*)user.photo.allObjects;
attempted to assign all the photo(MKAnnotation)to the photosproperty, but due to the faulting of the core data, the actual "data" is not available until you access it.
Solution:
create a new MKAnnotation class called PhotoAnnotation
Here is the code for this class
PhotoAnnotation.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface PhotoAnnotation : NSObject
#property (nonatomic) CLLocationCoordinate2D coordinate;
#property (strong, nonatomic) NSString* thumbnailUrl;
- (id) initWithThumbnailUrl:(CLLocationCoordinate2D)coordinate url: (NSString*) thumbnailUrl;
#end
##PhotoAnnotation.m
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#import "PhotoAnnotation.h"
#interface PhotoAnnotation() <MKAnnotation>
#end
#implementation PhotoAnnotation
- (id) initWithThumbnailUrl:(CLLocationCoordinate2D)coordinate url: (NSString*) thumbnailUrl{
self = [super init];
if(self){
self.coordinate = coordinate;
self.thumbnailUrl = thumbnailUrl;
}
return self;
}
#end
Assigned the MKAnnotation like this
for(User* user in users){
self.photos = [[NSMutableArray alloc] init];
for(Photo* photo in user.photo){
CLLocationCoordinate2D location = CLLocationCoordinate2DMake(photo.latitude, photo.longitude);
PhotoAnnotation* myAnnotation = [[PhotoAnnotation alloc]initWithThumbnailUrl: location url: photo.thumbnailUrl];
[self.photos insertObject:myAnnotation atIndex:0];
}
}
This would successfully create the custom MKAnnotation on the map!
Hi everyone iam new in objective c, now i try to create the app like "What's the word". I find this tutorial and lerned it as well as i can. But i have some problems. I want when i click on buttons the currentTitle replace the lable in placesView. Button click method in LettersView.m named as "displayChar". I have success in getting currentTitle but now i don't know how to pass it to GameController and paste text on "places".
I will be grateful for any help!
Here is my code
LettersView.h
#import <UIKit/UIKit.h>
#class LettersView;
#protocol LetterClickDelegateProtocol <NSObject>
-(void)letterView:(LettersView*)letterView addChar:(NSString *)addChar;
#end
#interface LettersView : UIImageView
#property (strong, nonatomic, readonly) NSString* letter;
#property (assign, nonatomic) BOOL isMatched;
#property (strong, nonatomic) NSString *clickLetter;
#property (weak, nonatomic) id<LetterClickDelegateProtocol> clickDelegate;
#property (strong, nonatomic) UIButton *lblChar;
-(instancetype)initWithLetter:(NSString*)letter andSideLength:(float)sideLength;
#end
LettersView.m
#import "LettersView.h"
#import "config.h"
#implementation LettersView{
NSInteger _xOffset, _yOffset;
}
- (id)initWithFrame:(CGRect)frame
{
NSAssert(NO, #"Use initWithLetter:andSideLength instead");
return nil;
}
-(instancetype)initWithLetter:(NSString*)letter andSideLength:(float)sideLength
{
//the letter background
UIImage* img = [UIImage imageNamed:#"btn_letter#2x.png"];
//create a new object
self = [super initWithImage:img];
if (self != nil) {
//resize the letters
float scale = sideLength/img.size.width;
self.frame = CGRectMake(0,0,img.size.width*scale, img.size.height*scale);
UIButton *lblChar = [[UIButton alloc] initWithFrame:self.bounds];
lblChar.tintColor = [UIColor blackColor];
lblChar.backgroundColor = [UIColor clearColor];
[lblChar setTitle:letter forState:UIControlStateNormal];
[lblChar addTarget:self action:#selector(displaychar:)forControlEvents:UIControlEventTouchUpInside];
[self addSubview:lblChar];
self.isMatched = NO;
_letter = letter;
self.userInteractionEnabled = YES;
}
return self;
}
-(void)displayChar:(id)sender {
UIButton *lblChar = (UIButton *)sender;
NSLog(#" The button's title is %#.", lblChar.currentTitle);
_clickLetter = lblChar.currentTitle;
if (self.clickDelegate) {
[self.clickDelegate letterView:self addChar:lblChar.currentTitle];
}
NSLog(#"hu %#", _clickLetter);
}
PlacesView.h
// PlacesView.m
#import "PlacesView.h"
#import "config.h"
#implementation PlacesView
-(id)initWithFrame:(CGRect)frame {
NSAssert(NO, #"Use initwithletter");
return nil;
}
-(instancetype)initWithLetter:(NSString *)letter andSideLength:(float)sideLength
{
UIImage *img = [UIImage imageNamed:#"btn_input#2x.png"];
self = [super initWithImage: img];
if (self != nil) {
self.isMatched = NO;
float scale = sideLength/img.size.width;
self.frame = CGRectMake(0, 0, img.size.width*scale, img.size.height*scale);
//bullshit time
_fieldForLetter = [[UILabel alloc] initWithFrame:self.bounds];
_fieldForLetter.textAlignment = NSTextAlignmentCenter;
_fieldForLetter.textColor = [UIColor blackColor];
_fieldForLetter.backgroundColor = [UIColor clearColor];
_fieldForLetter.text = #"*"; // if button pressed button title placed here.
[self addSubview:_fieldForLetter];
_letter = letter;
}
return self;
}
#end
GameController.m
#import "GameController.h"
#import "config.h"
#import "LettersView.h"
#import "PlacesView.h"
#import "AppDelegate.h"
#implementation GameController {
//tile lists
NSMutableArray* _letters;
NSMutableArray* _places;
}
-(instancetype)init {
self = [super init];
if (self != nil) {
self.points = [[PointsController alloc] init];
self.audioController = [[AudioController alloc] init];
[self.audioController preloadAudioEffects: kAudioEffectFiles];
}
return self;
}
-(void)dealRandomWord {
NSAssert(self.level.words, #"Level not loaded");
// random word from plist
NSInteger randomIndex = arc4random()%[self.level.words count];
NSArray* anaPair = self.level.words[ randomIndex ];
NSString* word1 = anaPair[1]; // answer
NSString* word2 = anaPair[2]; // some letters
_helpstr = anaPair[3]; // helper
NSLog(#"qweqweq %# %#" , word1 , word2);
NSInteger word1len = [word1 length];
NSInteger word2len = [word2 length];
NSLog(#"phrase1[%li]: %#", (long)word1len, word1);
NSLog(#"phrase2[%li]: %#", (long)word2len, word2);
//calculate the letter size
float letterSide = ceilf( kScreenWidth*0.9 / (float)MAX(word1len, word2len) ) - kTileMargin;
//get the left margin for first letter
float xOffset = (kScreenWidth - MAX(word1len, word2len) * (letterSide + kTileMargin))/2;
//adjust for letter center
xOffset += letterSide/2;
float yOffset = 1.5* letterSide;
// init places list
_places = [NSMutableArray arrayWithCapacity: word1len];
// create places
for (NSInteger i = 0; i<word1len; i++){
NSString *letter = [word1 substringWithRange:NSMakeRange(i, 1)];
if (![letter isEqualToString:#" "]) {
PlacesView* place = [[PlacesView alloc] initWithLetter:letter andSideLength:letterSide];
place.center = CGPointMake(xOffset + i*(letterSide + kTileMargin), kScreenHeight/4);
[self.gameView addSubview:place];
[_places addObject: place];
}
}
//init letters list
_letters = [NSMutableArray arrayWithCapacity: word2len];
//create letter
for (NSInteger i=0;i<word2len;i++) {
NSString* letter = [word2 substringWithRange:NSMakeRange(i, 1)];
if (![letter isEqualToString:#" "]) {
LettersView* letv = [[LettersView alloc] initWithLetter:letter andSideLength:letterSide];
letv.center = CGPointMake(xOffset + i * (letterSide + kTileMargin), kScreenHeight); // "/3*4"
if (i > 6) {
letv.center = CGPointMake(-5.15 * xOffset + i * (letterSide + kTileMargin), kScreenHeight + yOffset); // "/3*4"
}
letv.clickDelegate = self;
[self.gameView addSubview:letv];
[_letters addObject: letter];
}
}
}
-(void)letterView:(LettersView *)letterView addChar:(NSString *)addChar
{
PlacesView* placesView = nil;
for (PlacesView* pl in _places) {
//if (CGRectContainsPoint(pl.frame, pt)) {
if () {
//placesView = pl;
placesView.fieldForLetter.text = letterView.lblChar.currentTitle;
break;
}
}
//1 check if target was found
if (placesView!=nil) {
//2 check if letter matches
if ([placesView.letter isEqualToString: letterView.letter]) {
[self placeLetter:letterView atTarget:placesView];
[self.audioController playEffect: kSoundLetterTap];
self.points.points += self.level.coinsPerLvl; //ne nado tak
NSLog(#"Current points %d" , self.points.points);
[self checkForSuccess];
} else {
[self.audioController playEffect:kSoundFail];
[self addAlert:#"ne success" andMessage:#"You lose!" andButton:#"eshe cyka"];
}
}
}
-(void)placeLetter:(LettersView*)letterView atTarget:(PlacesView*)placeView {
placeView.isMatched = YES;
letterView.isMatched = YES;
letterView.userInteractionEnabled = NO;
}
-(void)checkForSuccess {
for (PlacesView* p in _places) {
//no success, bail out
if (p.isMatched==NO) return;
}
NSLog(#"ya!");
[self addAlert:#"Success" andMessage:#"You win!" andButton:#"eshe cyka"];
[self.audioController playEffect:kSoundSuccess];
}
-(void)addAlert: (NSString *)addTitle andMessage: (NSString *)alertMessage andButton: (NSString *)alertButton {
dispatch_async(dispatch_get_main_queue(), ^{
UIWindow* window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
window.rootViewController = [UIViewController new];
window.windowLevel = UIWindowLevelAlert + 1;
UIAlertController *alert = [UIAlertController alertControllerWithTitle: addTitle message:alertMessage preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *defaultAction= [UIAlertAction actionWithTitle:alertButton style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
window.hidden = YES;
}];
[alert addAction:defaultAction];
[window makeKeyAndVisible];
[window.rootViewController presentViewController:alert animated:YES completion:nil];
});
}
#end
Your GameController needs to keep a reference to PlacesView. It can also assign the action into LettersView so when the button in LettersView is pressed, the GameController will fetch it and perform an action in PlacesView. GameController is what both the other classes have in common, so it can handle any actions between them.
Another option is to use NSNotificationCenter and post a message in LettersView when the button is pressed, and listen for it in PlacesView.
Yet anther way is using a delegates where GameController makes sure that PlacesView is set as the delegate. When LettersView's button is pressed, it will call the delegate method which PlacesView listens to.
I'd go with first option.
I have an Array with 10 Objects. I take the first Object and put it into my Label with a String.
Then I want to have a Method that increases the objectAtIndex by 1.
This is my Code :
//.m
#interface GameViewController () {
NSInteger _labelIndex;
}
#property (nonatomic) NSArray *playerArray;
#property (nonatomic) NSString *playerLabel;
- (void)viewDidLoad {
[super viewDidLoad];
self.playerArray = [NSArray arrayWithObjects:#"FIRST", #"SECOND", #"THIRD", #"FOURTH", #"FIFTH", #"SIXT", #"SEVENTH", #"EIGTH", #"NINTH", #"TENTH", nil];
_labelIndex = 0;
[self updateTurnLabel];
self.turnLabel.text = [NSString stringWithFormat:#"YOUR %# DRAW?", self.playerLabel];
}
Here I call the Method i another Method:
-(void) flipDraws {
self.boardView.userInteractionEnabled = NO;
[self updateTurnLabel];
CardView *cv1 = (CardView *) self.turnedDrawViews[0];
CardView *cv2 = (CardView *) self.turnedDrawViews[1];
}
This is my Method:
-(void) updateTurnLabel {
self.playerLabel = [self.playerArray objectAtIndex:_labelIndex % self.playerArray.count]; _labelIndex++;
}
I tried it with a for Loop but nothing happened. I tried it with just set the objectAtIndex:1 but my Method was not called.
What I am doing wrong?
{
int a;
}
- (void)viewDidLoad {
[super viewDidLoad];
a = 0;
self.playerArray = [NSArray arrayWithObjects:#"FIRST", #"SECOND", #"THIRD", #"FOURTH", #"FIFTH", #"SIXT", #"SEVENTH", #"EIGTH", #"NINTH", #"TENTH", nil];
self.playerLabel = [self.playerArray objectAtIndex:a];
self.turnLabel.text = [NSString stringWithFormat:#"YOUR %# DRAW?", self.playerLabel];
}
-(void) updateTurnLabel {
a +=1;
if (!a<[playerArray count])
{
a = 0;
}
self.playerLabel = [self.playerArray objectAtIndex:a];
}
call self.turnLabel.text = [NSString stringWithFormat:#"YOUR %# DRAW?", self.playerLabel]; after [self updateTurnLabel];
What are you adding +1 to in the method objectAtIndex:.
You should be maintaining a variable which tracks the current index being used, then in your method use this :
-(void) updateTurnLabel {
self.playerLabel = [self.playerArray objectAtIndex:currentIndex+1];
}
I was trying to solve assignment 2 from Stanford iOS7 development (Matchismo card game)
The game works fine. Now I have to add the Restart function. If the user press on the restart button, the game restarts (it deals new cards and it resets the score)
my game model is the #property (nonatomic, strong) CardMatchingGame *game;
this is the code for the CardMatchingGame.m:
#import "CardMatchingGame.h"
#import "PlayingCardDeck.h"
#interface CardMatchingGame()
#property (nonatomic, readwrite) NSInteger score;
#property (nonatomic, strong) NSMutableArray *cards;
#end
#implementation CardMatchingGame
static const int MATCH_BONUS = 4;
static const int MATCH_PENALTY = 2;
static const int COST_TO_CHOOSE = 1;
-(NSMutableArray *)cards{
if(!_cards) _cards = [[NSMutableArray alloc]init];
return _cards;
}
-(instancetype)initWithCardCount:(NSUInteger)count usingDeck:(Deck *)deck{
self = [super init];
if(self){
for(int i=0; i < count; i++){
Card *card = [deck drawRandomCard];
if(card){
[self.cards addObject:card];
} else{
self = nil;
break;
}
}
}
return self;
}
-(void)chooseCardAtIndex:(NSUInteger)index{
Card *card = [self cardAtIndex:index];
if(!card.isMatched){
if(card.isChosen){
card.chosen = NO;
} else{
for(Card *otherCard in self.cards){
if(otherCard.isChosen && !otherCard.isMatched){
int matchScore = [card match:#[otherCard]];
if(matchScore){
self.score += matchScore * MATCH_BONUS;
card.matched = YES;
otherCard.matched = YES;
} else{
self.score -= MATCH_PENALTY;
otherCard.chosen = NO;
}
break;
}
}
self.score -= COST_TO_CHOOSE;
card.chosen = YES;
}
}
}
-(Card *)cardAtIndex:(NSUInteger)index{
return (index < [self.cards count]) ? self.cards[index] : nil;
}
#end
here is my CardGameViewController.m:
#import "CardGameViewController.h"
#import "PlayingCardDeck.h"
#import "CardMatchingGame.h"
#interface CardGameViewController ()
#property (nonatomic, strong) Deck *deck;
#property (nonatomic, strong) CardMatchingGame *game;
#property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *cardsCollection;
#property (weak, nonatomic) IBOutlet UILabel *scoreLabel;
#end
#implementation CardGameViewController
#synthesize game = _game;
-(CardMatchingGame *)game{
if(!_game) _game = [[CardMatchingGame alloc] initWithCardCount:[self.cardsCollection count]
usingDeck:self.deck];
return _game;
}
-(Deck *)deck{
if(!_deck) _deck = [[PlayingCardDeck alloc] init];
return _deck;
}
- (IBAction)touchRestartButton:(id)sender {
self.game = nil;
[self updateUI];
}
- (IBAction)touchCardButton:(UIButton *)sender {
int chosenButtonIndex = [self.cardsCollection indexOfObject:sender];
[self.game chooseCardAtIndex:chosenButtonIndex];
[self updateUI];
}
-(void)updateUI{
for(UIButton *cardButton in self.cardsCollection){
int buttonIndex = [self.cardsCollection indexOfObject:cardButton];
Card *card = [self.game cardAtIndex:buttonIndex];
[cardButton setTitle:[self titleForCard:card] forState:UIControlStateNormal];
[cardButton setBackgroundImage:[self backgroundImageForCard:card] forState:UIControlStateNormal];
cardButton.enabled = !card.isMatched;
}
self.scoreLabel.text = [NSString stringWithFormat:#"Score: %d", self.game.score];
}
-(NSString *)titleForCard:(Card *)card{
return card.isChosen ? card.contents : #"";
}
-(UIImage *)backgroundImageForCard:(Card *)card{
return [UIImage imageNamed: card.isChosen ? #"cardfront" : #"cardback"];
}
#end
In order to restart the game, I think I should simply re-initialize the property CardMatchingGame *game.
This is what I tried to do, by setting self.game = nil; Then it should automatically be re-initialized in the getter of game.
This is, indeed, the solution that I found on the internet. However, in my program it doesn't work. *game is set to nil and never restored, so the game ends when you click restart.
Could you please help me to figure out why self.game = nil doesn't work in my case?
- (IBAction)startover:(UIButton *)sender {
self.game= [[CardMatchingGame alloc] initWithCardCount:[self.cardButtons count] usingDeck:[self createDeck]];
[self updateUI];
}
If your program has the lazy init style recommended, there is no need for a new method such as start over. You are correct to set self.game to nil, now a call the the getter will start a new instance of the game. I did it by making a call to UIUpdate right after the self.game = nil. That has a call to the getter and lazily inits a new instance of the game.
Newbie error?
Im working on a quiz app that uses answer results from a view contoller, creates NSMutableArray objects for totalCorrect and totalTried values in a KeepScore model .m file and the model returns a score back to my view controller. The view controller then updates my score label in my view. I then use the UINavigationController to segue to my next view controller and then wait for the user to input the next quiz question answer. Problem is that the next time i call my KeepScore model .m file from my view controller 2, the NSMutableArray objects in KeepScore model .m have reverted to Null values. I made sure my NSMutableArray object values were correct (non Null) right before returning from my KeepScore .m model to my view controller 1. Im working iOS 5.1 with Xcode 4.3.1 with ARC. My NSMutableArray property in my private API declaration in .m model file is strong. Shouldn't the KeepScore model .m keep my array object values when i return to my view controller so that i can use them again when i call my model from my next view controller? I wanted to ask in wording first to see if anyone catches any syupid logic I have. I'll post code if helpful.
Here is my KeepScore.h model
#import
#interface KeepScore : NSObject
- (float)score:(BOOL)questionCorrect:(BOOL)questionTotal:(BOOL)firstScoringPass;
#end
Here is my KeepScore.m model implementation:
#import "KeepScore.h"
#interface KeepScore()
#property (nonatomic, strong) NSMutableArray *correctScoreValues;
#end
#implementation KeepScore
#synthesize correctScoreValues = _correctScoreValues;
- (NSMutableArray *)correctScoreValues
{
if (_correctScoreValues == nil) _correctScoreValues = [[NSMutableArray alloc] init];
return _correctScoreValues;
}
- (float)score:(BOOL)questionCorrect:(BOOL)questionTotal:(BOOL)firstScoringPass {
int totalTried;
int totalCorrect;
NSLog(#"firstScoringPass = %#", firstScoringPass ? #"YES" : #"NO");
NSLog(#"questionCorrect = %#", questionCorrect ? #"YES" : #"NO");
NSLog(#"questionTotal = %#", questionTotal ? #"YES" : #"NO");
if (firstScoringPass) {
int totalTriedInt = 0;
int totalCorrectInt = 0;
NSNumber *correct = [NSNumber numberWithInt:totalCorrectInt];
NSNumber *tried = [NSNumber numberWithInt:totalTriedInt];
[self.correctScoreValues addObject:correct];
[self.correctScoreValues addObject:tried];
firstScoringPass = NO;
}
if (questionCorrect) {
totalCorrect = ([[self.correctScoreValues objectAtIndex:0] intValue] + 1);
NSLog(#"totalCorrect = %d", totalCorrect);
NSNumber *correct = [NSNumber numberWithInt:totalCorrect];
NSLog(#"correct = %#", correct);
[self.correctScoreValues replaceObjectAtIndex:0 withObject:correct];
NSLog(#"correct value in array = %d",[[self.correctScoreValues objectAtIndex:0] intValue]);
} else {
totalCorrect = [[self.correctScoreValues objectAtIndex:0] intValue];
}
NSLog(#"totalCorrect = %d", totalCorrect);
if (questionTotal) {
NSLog(#"tried value in array = %d", [[self.correctScoreValues objectAtIndex:1] intValue]);
totalTried = ([[self.correctScoreValues objectAtIndex:1] intValue] + 1);
NSNumber *tried = [NSNumber numberWithInt:totalTried];
[self.correctScoreValues replaceObjectAtIndex:1 withObject:tried];
NSLog(#"tried value in array = %d", [[self.correctScoreValues objectAtIndex:1] intValue]);
}
float score = (totalCorrect/totalTried);
NSLog(#"score = %0.2f", score);
NSLog(#"totalCorrect = %d, totalTried = %d", totalCorrect, totalTried);
NSLog(#"correct value in array = %d", [[self.correctScoreValues objectAtIndex:0] intValue]);
NSLog(#"tried value in array = %d", [[self.correctScoreValues objectAtIndex:1] intValue]);
return score;
}
#end
Here is one of my ViewControllers .m files:
#import "MapQuizViewController.h"
#import "KeepScore.h"
#interface MapQuizViewController()
#property (nonatomic, strong) KeepScore *scoreModel;
#property (nonatomic) BOOL questionTotal;
#property (nonatomic) BOOL questionCorrect;
#property (nonatomic) BOOL firstScoringPass;
#end
#implementation MapQuizViewController
#synthesize gradeLabel = _gradeLabel;
#synthesize scoreLabel = _scoreLabel;
#synthesize scoreModel = _scoreModel;
#synthesize questionTotal = _questionTotal;
#synthesize questionCorrect = _questionCorrect;
#synthesize firstScoringPass = _firstScoringPass;
- (KeepScore *)scoreModel;
{
if (!_scoreModel) _scoreModel = [[KeepScore alloc] init];
return _scoreModel;
}
- (IBAction)answerButtonPressed:(UIButton *)sender
{
NSString *answerChoice = sender.currentTitle;
NSString *correct = [NSString stringWithFormat:#"Correct!"];
NSString *incorrect = [NSString stringWithFormat:#"Incorrect"];
self.questionTotal = YES;
self.firstScoringPass = YES;
if ([answerChoice isEqualToString:#"Australia"]) {
self.gradeLabel.textColor = [UIColor blueColor];
self.gradeLabel.text = [NSString stringWithFormat:#"%#",correct];
self.questionCorrect = YES;
self.scoreLabel.text = [NSString stringWithFormat:#"%0.2f",[self.scoreModel score:_questionCorrect :_questionTotal :_firstScoringPass]];
} else if ([answerChoice isEqualToString:#"U.S."]) {
self.gradeLabel.textColor = [UIColor redColor];
self.gradeLabel.text = [NSString stringWithFormat:#"%#", incorrect];
self.questionCorrect = NO;
self.scoreLabel.text = [NSString stringWithFormat:#"%0.2f",[self.scoreModel score:_questionCorrect :_questionTotal :_firstScoringPass]];
} else if ([answerChoice isEqualToString:#"China"]) {
self.gradeLabel.textColor = [UIColor redColor];
self.gradeLabel.text = [NSString stringWithFormat:#"%#", incorrect];
self.questionCorrect = NO;
self.scoreLabel.text = [NSString stringWithFormat:#"%0.2f",[self.scoreModel score:_questionCorrect :_questionTotal :_firstScoringPass]];
}
}
- (void)viewDidUnload {
[self setScoreLabel:nil];
[super viewDidUnload];
}
#end
First, a single .m file means nothing. I think you assumed that there is one instance of a Class per .m file which is not true.
for example
// in .m file
static int *myInt; // only one `myInt` exist
// in .h or .m file
#interface MyClass : NSObject {
int myVar; // each MyClass instance will have its own `myVar` which are different
}
// somewhere else
MyClass *a = [[MyClass alloc] init];
MyClass *b = [[MyClass alloc] init];
a->myVar = 1;
b->myVar = 2;
// a->myVar is 1 and b->myVar is 2
If you didn't pass your KeepScore instance around, newly created one will not have previous one's data.
You have to either to make KeepScore singleton or pass it from old view controller to new view controller