Use voices in app [closed] - ios

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
I'm developing a little app where in this app, have to have a voice who say some words.
Can I use the voice that some programs uses like "Google Translate", "Vozme" or similar? If not, how can I do this?

AVSpeechSynthesizer Class Reference is available from iOS7. The documentation is very good. Be sure to link the AVFoundation framework to your project.
Here is a working example that speaks text entered from a UITextField when a UIButton is tapped - (assuming a UIViewController sub-class named YOURViewController)
in .h
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#interface YOURViewController : UIViewController <AVSpeechSynthesizerDelegate, UITextFieldDelegate> {
IBOutlet UITextField *textFieldInput;// connect to a UITextField in IB
}
- (IBAction)speakTheText:(id)sender;// connect to a UIButton in IB
#end
and in .m
#import "YOURViewController.h"
#interface YOURViewController ()
#end
#implementation YOURViewController
- (IBAction)speakTheText:(id)sender {
// create string of text to talk
NSString *talkText = textFieldInput.text;
// convert string
AVSpeechUtterance *speechUtterance = [self convertTextToSpeak:talkText];
// speak it...!
[self speak:speechUtterance];
}
- (void)viewDidLoad {
[super viewDidLoad];
}
- (AVSpeechUtterance*)convertTextToSpeak:(NSString*)textToSpeak {
AVSpeechUtterance *speechUtterance = [[AVSpeechUtterance alloc] initWithString:textToSpeak];
speechUtterance.rate = 0.2; // default = 0.5 ; min = 0.0 ; max = 1.0
speechUtterance.pitchMultiplier = 1.0; // default = 1.0 ; range of 0.5 - 2.0
speechUtterance.voice = [self customiseVoice];
return speechUtterance;
}
- (AVSpeechSynthesisVoice*)customiseVoice {
NSArray *arrayVoices = [AVSpeechSynthesisVoice speechVoices];
NSUInteger numVoices = [arrayVoices count];
AVSpeechSynthesisVoice *voice = nil;
for (int k = 0; k < numVoices; k++) {
AVSpeechSynthesisVoice *availCustomVoice = [arrayVoices objectAtIndex:k];
if ([availCustomVoice.language isEqual: #"en-GB"]) {
voice = [arrayVoices objectAtIndex:k];
}
// This logs the codes for different nationality voices available
// Note that the index that they appear differs from 32bit and 64bit architectures
NSLog(#"#%d %#", k, availCustomVoice.language);
}
return voice;
}
- (void)speak:(AVSpeechUtterance*)speechUtterance {
AVSpeechSynthesizer *speechSynthesizer = [[AVSpeechSynthesizer alloc] init];
speechSynthesizer.delegate = self;// all methods are optional
[speechSynthesizer speakUtterance:speechUtterance];
}
#end

Related

How do I display an alternate message if an object does not exist in my array? Objective C

The object of this application is to translate between english and spanish words.
(Checks text input against all array values to see if it's there and then compares that index to the second array, and displays the parallel value).
That part is working. If the word entered does not exist in the array, I am supposed to have a message like 'No translation available' display in the label. My problem is, I can either get the message to display for nothing or everything - rather than just when it is supposed to.
#import "TranslatorViewController.h"
#interface TranslatorViewController ()
#property (weak, nonatomic) IBOutlet UITextField *textField;
#property (weak, nonatomic) IBOutlet UILabel *label;
- (IBAction)translate:(id)sender;
#property (nonatomic, copy) NSArray *english;
#property (nonatomic, copy) NSArray *spanish;
#end
#implementation TranslatorViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_textField.delegate = self;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//make the keyboard go away
-(BOOL) textFieldShouldReturn:(UITextField *)textField {
{[textField resignFirstResponder];}
return YES;
}
- (instancetype)initWithCoder:(NSCoder*)aDecoder {  self = [super initWithCoder:aDecoder]; if(self) {  // Add your custom init code here
self.english = #[#"phone",
#"dog",
#"sad",
#"happy",
#"crocodile"];
self.spanish = #[#"telefono",
#"perro",
#"triste",
#"felize",
#"cocodrilo"];
}  return self; }
- (IBAction)translate:(id)sender {
//loop through the array
NSString *englishWord = self.textField.text;
for (int index=0; index<[self.english count]; index++)
if([[self.english objectAtIndex:index]
isEqualToString:englishWord])
//retrieve the accompanying spanish word if english word exists in the array
{NSString *spanishWord = [self.spanish objectAtIndex:index];
//and display it in the label
self.label.text = spanishWord;}
//Need code to display 'no translation' if word was not found.
}
#end
The simplest way to do this is probably to set the label's text field to "No translation" before entering the loop. If no match is found, the label will never be re-set to anything else.
There are lots of other ways to structure logic to give you this result. I might tighten up that last loop of code by doing this instead:
NSString * englishWord = self.textField.text;
NSUInteger spanishWordIndex = [self.english indexOfObject:englishWord];
if (spanishWordIndex == NSNotFound) {
self.label.text = #"No translation";
} else {
self.label.text = self.spanish[spanishWordIndex];
}
Why not use an NSDictionary?
- (instancetype)initWithCoder:(NSCoder*)aDecoder {
self = [super initWithCoder:aDecoder];
if(self) {  // Add your custom init code here
self.translationDict = #{#"phone":#"telefono",
#"dog":#"perro",
#"sad": #"triste",
#"happy": #"felize",
#"crocodile": #"cocodrilo"]; // self.translationDict is an NSDictionary
}
return self;
}
- (IBAction)translate:(id)sender {
NSString *englishWord = self.textField.text;
NSString *spanishWord=self.translationDict[englishWord];
if (spanishWord == nil) {
spanishWord="No Translation";
}
self.label.text=spanishWord;
}
I put
self.label.text = #"No translation available";
before the if statement which is I believe what Ben Zotto was trying to say! I just wasn't sure how to actually do it at first.
I'm a newb with this stuff.
But that did what I needed it to.
Thanks all!
Reformatted the answer to include your entire code. You should be able to just copy paste into your code.
- (IBAction)translate:(id)sender {
NSString *englishWord = self.textField.text;
NSString *spanishWord = #"No translation found.";
for (int index=0; index<[self.english count]; index++)
{
if([[self.english objectAtIndex:index] isEqualToString:englishWord])
{
//retrieve the accompanying spanish word if english word exists in the array
spanishWord = [self.spanish objectAtIndex:index];
}
}
self.label.text = spanishWord;
}

Object only available in if statement in Obj - C?

probably an easy question...I'm a beginner programmer, so bear with me :)
I've made my first 'real' iPhone app that draws cards from a deck until a Joker is drawn, then the game is over.
So far, so good and it all works, except I have to use a work-around which I really hate.
My Deck object is only available where I declare it, which seems normal but yet irritating.
Basically, I have to make an entire new deck every time the user hits the "Draw Card" button...
- (IBAction)drawCard:(id)sender {
Deck *deck = [[Deck alloc]init];
deck.generate;
unsigned int randomIndex = arc4random_uniform(deck.deckArray.count);
Card *topCard = [deck.deckArray objectAtIndex:randomIndex - 1];
if (topCard.value == 11) {
NSString *cardInfo = [NSString stringWithFormat:#"You drew a Jack of %#", topCard.suit];
self.cardDrawn.text = cardInfo;
}else if (topCard.value == 12) {
NSString *cardInfo = [NSString stringWithFormat:#"You drew a Queen of %#", topCard.suit];
self.cardDrawn.text = cardInfo;
}else if (topCard.value == 13) {
NSString *cardInfo = [NSString stringWithFormat:#"You drew a King of %#", topCard.suit];
self.cardDrawn.text = cardInfo;
}else {
NSString *cardInfo = [NSString stringWithFormat:#"You drew a %d of %#", topCard.value, topCard.suit];
self.cardDrawn.text = cardInfo;
}
[deck removeCard];
if (topCard.value == 14) {
self.cardDrawn.text = #"You drew the Joker Bomb!";
//Destroy deck
deck = nil;
[drawCard setEnabled:NO];
[playAgain setEnabled:YES];
[playAgain setHidden:NO];
}
}
- (IBAction)playAgain:(id)sender {
self.cardDrawn.text = nil;
[drawCard setEnabled:YES];
[playAgain setEnabled:NO];
[playAgain setHidden:YES];
}
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self =[super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
Deck *deck = [[Deck alloc]init];
deck.generate;
}
return self;
}
#end
If I take away Deck *deck = [[Deck alloc]init]; in my drawCard button method, I get a bunch of errors and the app won't build. I've tried #synthesizeing the deck object, but that doesn't seem to work either, and I also tried making a pointer to deck but that was kind of useless and did not work as well.
I wanted to implement a label that would show how many cards are left in the deck, but that would be impossible since a new deck is being generated every time the user taps drawCard.
Sorry if this is a stupid question & thanks!
#interface ViewControllerTheOPDidntProvideANameFor ()
#property (nonatomic, strong) Deck *deck;
#end
#implementation ViewControllerTheOPDidntProvideANameFor
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self =[super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
self.deck = [[Deck alloc]init];
[self.deck generate];
return self;
}
#end
There are some problems in your code. First of all what does "deck.generate;" do? That's not a valid Objective-C syntax.
The other thing is that you're realocating a deck everytime the drawCard method is called. Instead of using:
Deck *deck = [[Deck alloc]init];
You could declare Deck as an instance variable inside your interface and change the line above with:
deck = [Deck new];
In your .h file, declare something like this:
#property (nonatomic, strong) Deck *deck;
Then, in your .m file, initialize the deck (most likely in viewDidLoad or viewWillAppear) using something like this:
self.deck = [[Deck alloc] init];
Then, you'll be able to use the same deck property using self.deck like this:
[self.deck generate]; //Good practice to use brackets instead of dot notation for function calls in Objective-C since dot operators indicate properties
Hope this helps!
EDIT:
Per DarkDust's remark, you can use an instance variable as well:
//.h
#interface YourViewControllerName : UIViewController {
Deck *deck;
}
//.m
- (void)viewDidLoad
{
[super viewDidLoad];
//Initialize the deck
deck = [[Deck alloc] init];
}
Your deck will then be accessible using deck.

Viewcontrollers best practice [closed]

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
I am just about to start a new project where I have a custom menu that I need to display on everyview that I have. I dont want to use tab bars as this menu is custom designed and may have some animation added to it at some point.
Is there a simple way of creating this menu in one place so that I dont have to build it into every xib file??
Thanks
The tab bar controller is a system provided container controller. If you're using iOS 5 and later, you can make your own custom container view controller:
See Custom Container View Controllers discussion in the View Controller Programming Guide.
The key methods you need are enumerated in the UIViewController Class Reference, too.
I'd also suggest checking out WWDC 2011 #102 - Implementing UIViewController Containment.
Update:
If you want to write your own custom menu, you could do something like the following. I'm not doing anything fancy, but I'm just adding three colored subviews that might correspond to your custom buttons. And I have a tap gesture recognizer on each, which you can obviously handle as you see fit:
NSInteger const kHeight = 50;
NSInteger const kCount = 3;
#interface CustomMenu ()
#property (nonatomic, strong) NSMutableArray *menuViews;
#end
#implementation CustomMenu
- (id)init
{
self = [super init];
if (self)
{
_menuViews = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < kCount; i++)
{
UIView *subview = [[UIView alloc] init];
subview.tag = i;
[self addSubview:subview];
[_menuViews addObject:subview];
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
[subview addGestureRecognizer:recognizer];
}
[_menuViews[0] setBackgroundColor:[UIColor blueColor]];
[_menuViews[1] setBackgroundColor:[UIColor redColor]];
[_menuViews[2] setBackgroundColor:[UIColor greenColor]];
}
return self;
}
- (void)layoutSubviews
{
CGFloat width = self.superview.bounds.size.width;
CGFloat height = self.superview.bounds.size.height;
CGFloat menuChoiceWidth = width / kCount;
self.frame = CGRectMake(0, height - kHeight, width, kHeight);
NSInteger subviewIndex = 0;
for (UIView *subview in self.menuViews)
{
subview.frame = CGRectMake(subviewIndex * menuChoiceWidth, 0,
menuChoiceWidth, kHeight);
subviewIndex++;
}
}
- (void)handleTap:(UITapGestureRecognizer *)recognizer
{
NSLog(#"%s tapped on %d", __FUNCTION__, recognizer.view.tag);
}
#end
Then, you various view controllers just need to make sure to add the CustomMenu to the view:
#interface ViewController ()
#property (nonatomic, strong) CustomMenu *menu;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.menu = [[CustomMenu alloc] init];
[self.view addSubview:self.menu];
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[self.menu layoutSubviews];
}
#end
I confess that I've given up on iOS 4.3 support (it just isn't worth the heartache and the size of the 4.3 audience is pretty small nowadays), so I don't deal with this silliness any more, but hopefully this gives you a sense of what one possible solution might look like.

Is customizing UIAlertView is allowed by Apple? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 5 years ago.
Improve this question
I'm preparing to submit such a customization by subclassing UIAlerView. It's layout is entirely based on the given topography of the UIAlertView, have no read any private property. Is this kind of customization acceptable by App Store review process?
BGAlertViewWithSwitch.h
//
// BGAlertViewWithSwitch.h
// BGAlertViewWithSwitch
//
// Created by Borbas Geri on 11/7/11.
// Copyright 2011 ©ompactApps. All rights reserved.
//
#import <Foundation/Foundation.h>
//An assumed value.
#define ALERT_VIEW_LINE_HEIGHT 20.0
#define ALERT_VIEW_LABEL_PADDING 5.0
#define ALERT_VIEW_LABEL_ALPHA 0.5
#define kAlertSwitchLabelTag 42
#interface BGAlertViewWithSwitch : UIAlertView
{
UISwitch *_alertSwitch;
UILabel *_alertSwitchLabel;
}
#property (nonatomic, retain) UISwitch *alertSwitch;
#property (nonatomic, retain) UILabel *alertSwitchLabel;
#property (nonatomic, readonly, getter=isOn) BOOL on;
-(id)initWithTitle:(NSString*) title
message:(NSString*) message
switchMessage:(NSString*) switchMessage
delegate:(id) delegate
cancelButtonTitle:(NSString*) cancelButtonTitle
okButtonTitle:(NSString*) okButtonTitle;
#end
BGAlertViewWithSwitch.m
//
// BGAlertViewWithSwitch.m
// BGAlertViewWithSwitch
//
// Created by Borbas Geri on 11/7/11.
// Copyright 2011 ©ompactApps. All rights reserved.
//
#import "BGAlertViewWithSwitch.h"
#implementation BGAlertViewWithSwitch
#synthesize alertSwitch = _alertSwitch;
#synthesize alertSwitchLabel = _alertSwitchLabel;
#pragma mark - UISwitch Accessor
-(BOOL)isOn
{
return self.alertSwitch.isOn;
}
#pragma mark - View lifecycle
-(id)initWithTitle:(NSString*) title
message:(NSString*) message
switchMessage:(NSString*) switchMessage
delegate:(id) delegate
cancelButtonTitle:(NSString*) cancelButtonTitle
okButtonTitle:(NSString*) okButtonTitle
{
//For testing layout
NSString *placeHolder = #"";
//Append a line to the message that leaves the place for the switch.
NSString *_expandedMessage = [NSString stringWithFormat:#"%#\n%#\n%#\n", message, placeHolder, placeHolder];
if (self = [self initWithTitle:title
message:_expandedMessage
delegate:delegate
cancelButtonTitle:cancelButtonTitle
otherButtonTitles:okButtonTitle, nil])
{
//Add switch.
self.alertSwitch = [[UISwitch alloc] init];
self.alertSwitch.on = YES;
[self addSubview:self.alertSwitch];
//Add label.
self.alertSwitchLabel = [[UILabel alloc] init];
self.alertSwitchLabel.text = switchMessage;
self.alertSwitchLabel.tag = kAlertSwitchLabelTag;
[self addSubview:self.alertSwitchLabel];
}
return self;
}
- (void)dealloc
{
self.alertSwitch = nil;
self.alertSwitchLabel = nil;
[super dealloc];
}
#pragma mark - Topography
- (void)layoutSubviews
{
NSLog(#"layoutSubviews to (%#)", NSStringFromCGRect(self.frame));
//Weak link to the message label.
UILabel *messageLabel;
//Enumerate subviews to find message label (the base of the topography).
for (UIView *eachSubview in self.subviews)
if ([[eachSubview class] isEqual:[UILabel class]])
{
UILabel *eachLabel = (UILabel*)eachSubview;
if (eachLabel.tag != kAlertSwitchLabelTag)
{
messageLabel = eachLabel;
NSLog(#"Each label frame (%#), saying '%#'", NSStringFromCGRect(eachLabel.frame), eachLabel.text);
}
}
//Center new content.
CGSize alertSwitchLabelSize = [self.alertSwitchLabel.text sizeWithFont:messageLabel.font];
float horizontalCentering = (messageLabel.frame.size.width - (alertSwitchLabelSize.width + ALERT_VIEW_LABEL_PADDING + self.alertSwitch.frame.size.width)) / 2;
//Switch goes to the bottom right.
float switchVerticalCentering = ((ALERT_VIEW_LINE_HEIGHT * 2 + 1) - self.alertSwitch.frame.size.height ) / 2;
CGRect alertSwitchFrame = CGRectMake(messageLabel.frame.origin.x + messageLabel.frame.size.width - self.alertSwitch.frame.size.width - horizontalCentering,
messageLabel.frame.origin.y + messageLabel.frame.size.height - self.alertSwitch.frame.size.height - switchVerticalCentering,
self.alertSwitch.frame.size.width,
self.alertSwitch.frame.size.height);
self.alertSwitch.frame = alertSwitchFrame;
//Label goes to the bottom left.
float switchLabelVerticalCentering = ((ALERT_VIEW_LINE_HEIGHT * 2 + 1) - ALERT_VIEW_LINE_HEIGHT ) / 2;
CGRect alertSwitchLabelFrame = CGRectMake(round( messageLabel.frame.origin.x + horizontalCentering ),
round( messageLabel.frame.origin.y + messageLabel.frame.size.height - ALERT_VIEW_LINE_HEIGHT - switchLabelVerticalCentering ),
messageLabel.frame.size.width - self.alertSwitch.frame.size.width,
ALERT_VIEW_LINE_HEIGHT); //self.alertSwitchLabel.frame.size.height);
self.alertSwitchLabel.frame = alertSwitchLabelFrame;
//Copy message label properties.
self.alertSwitchLabel.backgroundColor = [UIColor clearColor];
self.alertSwitchLabel.textColor = messageLabel.textColor;
self.alertSwitchLabel.font = messageLabel.font;
self.alertSwitchLabel.shadowColor = messageLabel.shadowColor;
self.alertSwitchLabel.shadowOffset = messageLabel.shadowOffset;
//Weaken.
self.alertSwitchLabel.alpha = ALERT_VIEW_LABEL_ALPHA;
[super layoutSubviews];
}
#end
The actual answer to the question is no -- Apple does not allow UIAlertView to be subclassed. From the UIAlertView docs:
Subclassing Notes
The UIAlertView class is intended to be used as-is and does not
support subclassing. The view hierarchy for this class is private and
must not be modified.
Found here:
Subclassing UIAlertView
No one but Apple can adequately answer this question, so the best thing is to put it to the test. I think the main question you have to ask yourself is: Have I violated any provisions in the Apple Developer Agreement? If not, then submit your app. If you are worried about rejection, think of another way in which this could be done, as a backup, and be prepared to submit that in case of a problem.
Not that you have asked, but I would also opine that this change to Apple's design is not very intuitive. Do you mean the switch to mean "also delete from moquus?" as you already have a big delete button there. If the switch is off, then what does the delete button delete?

iPhone - Need some help with EXC_BAD_ACCESS that drives me crazy (full source code included)

I have an app that uses a GoogleMap view.
Into that app, I want to display some custom anotations, and a custom view for the userLocation. When the location manager gives a heading, I want to display that custom view for userLocation, if it fails to deliver one, I want the default blue animated dot to come back, etc...
Project and Source code available HERE
Well, there are 2 problems here :
1st problem, THE REALLY BAD ONE. You can play for hours with that tiny app. But... Launch it, wait it loads, send it to the background, put your iPhone in sleep mode, wait a minute, wake up your iPhone, launch the app (that wakes it up) : crash, EXC_BAD_ACCESS. Do the same thing not putting the iPhone in sleep mode : no crash. Not waiting for a minute but a few seconds, no crash. 10 seconds, no crash, 20 seconds, no crash, nears 30 seconds, perhaps a crash, near a minute, crash !
2nd problem (bonus :-) ), that can be linked with the first one, but that is not really the heart of this question : the solution I use to achieve the userPinLocation to update from/to the blue pin seems to have a really bad side effect : the userLocation does not move on the map as you move in the street. For any answer for that 2nd problem, if it's not directly linked with the crash, please use comments not answers. This is not the heart of the question... I think...
To find the faulty code, I've reduced my app at its minimum. That gives the following code (some tips in the source code as comments) :
EXC_BAD_ACCESS_TestViewController.h
#import <MapKit/MapKit.h>
#interface EXC_BAD_ACCESS_TestViewController : UIViewController<CLLocationManagerDelegate> {
CLLocationManager* locationMgr;
NSMutableArray* customAnnotations;
CLLocationDirection userHeading;
MKMapView* myMapView;
}
#property(nonatomic, retain) CLLocationManager* locationMgr;
#property(nonatomic, retain) NSMutableArray* customAnnotations;
#property(nonatomic, assign) CLLocationDirection userHeading;
#property(nonatomic, retain) IBOutlet MKMapView* myMapView;
- (void) loadAnnotations;
#end
EXC_BAD_ACCESS_TestViewController.m
// On first launch, due to code simplification, the app may crash when asked authorization to use geo hardware. Just kill/relaunch after accepting.
// Breakpoints on each method entry : EXC_BAD_ACCESS crash without any break-stop
#import "EXC_BAD_ACCESS_TestViewController.h"
#import "CustomAnnotation.h"
#implementation EXC_BAD_ACCESS_TestViewController
#synthesize locationMgr, customAnnotations, myMapView, userHeading;
// ===========================================================================================================
-(id) initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (!self) return nil;
self.userHeading = -1;
return self;
}
// ===========================================================================================================
- (void) viewDidLoad
{
[self loadAnnotations];
self.myMapView.mapType = MKMapTypeStandard;
self.myMapView.showsUserLocation = YES;
// -----------------------------------------------
// Just comment this sole line : no more crash
// -----------------------------------------------
for (CustomAnnotation* pin in self.customAnnotations) [self.myMapView addAnnotation:pin];
// locationManager
self.locationMgr = [[CLLocationManager alloc] init];
self.locationMgr.desiredAccuracy = kCLLocationAccuracyBest;
self.locationMgr.distanceFilter = 1.0;
self.locationMgr.headingFilter = 1.0;
self.locationMgr.purpose = #"Some purpose.";
self.locationMgr.delegate = self;
[self.locationMgr startUpdatingHeading];
[super viewDidLoad];
}
// ===========================================================================================================
- (void) loadAnnotations
{
// Code for testing, real code gets the datas using another way of doing, as simple as this one
self.customAnnotations = [NSMutableArray array];
double latitude = 45.0;
double longitude = -45.0;
for (int i=1; i<=400; i++) {
CLLocationCoordinate2D pinLocation = CLLocationCoordinate2DMake(latitude, longitude);
CustomAnnotation* pin = [[[CustomAnnotation alloc] initWithCoordinate:pinLocation] autorelease];
[self.customAnnotations addObject:pin];
latitude += 0.01;
longitude += 0.01;
}
}
// ===========================================================================================================
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation
{
MKAnnotationView* pinView = nil;
NSString* annotationIdentifier;
UIImage* pinImage;
BOOL canShowCallout;
// User location
if ([annotation isKindOfClass:[MKUserLocation class]] && self.userHeading >= 0) {
annotationIdentifier = #"UserLocationAnnotation";
pinImage = [UIImage imageNamed:#"custom_userlocation.png"];
canShowCallout = NO;
}
// Custom Pin
else if ([annotation isKindOfClass:[CustomAnnotation class]]) {
annotationIdentifier = #"CustomAnnotation";
pinImage = [UIImage imageNamed:#"custom_pin.png"];
canShowCallout = YES;
}
// Others
else {
return nil;
}
pinView = (MKAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:annotationIdentifier];
if (pinView) {
pinView.annotation = annotation;
}
else {
pinView = [[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annotationIdentifier] autorelease];
if (pinView) {
pinView.image = pinImage;
pinView.canShowCallout = canShowCallout;
if (canShowCallout) {
pinView.calloutOffset = CGPointMake(-5, 5);
pinView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
}
}
}
return pinView;
}
// -----------------------------------------------
// Just comment this method : no more crash
// -----------------------------------------------
// ===========================================================================================================
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading
{
if (!self.myMapView) return;
if (!self.myMapView.userLocation) return;
CLLocationDirection compassHeading_True = newHeading.trueHeading;
CLLocationDirection compassHeading_Magnetic = newHeading.magneticHeading;
CLLocationDirection heading;
if (compassHeading_True == -1) heading = compassHeading_Magnetic;
else if (newHeading.headingAccuracy >= 0) heading = compassHeading_True;
else heading = -1;
// If we get/loose the heading, update pin image
// I didn't found any other solution to force the user pin to update its display.
if ((self.userHeading == -1 || heading == -1) && self.userHeading != heading) {
[self.myMapView removeAnnotation:self.myMapView.userLocation];
self.userHeading = heading;
[self.myMapView addAnnotation:self.myMapView.userLocation];
}
self.userHeading = heading;
// ------------------------------------
// Some non bugued code was there
// ------------------------------------
}
// ===========================================================================================================
- (void) dealloc
{
self.myMapView = nil;
self.customAnnotations = nil;
self.locationMgr = nil;
[super dealloc];
}
#end
CustomAnnotation.h
#import <MapKit/MapKit.h>
#interface CustomAnnotation : NSObject<MKAnnotation> {
CLLocationCoordinate2D coordinate;
}
#property(nonatomic, assign) CLLocationCoordinate2D coordinate;
- (id)initWithCoordinate:(CLLocationCoordinate2D) c;
#end
CustomAnnotation.m
#import "CustomAnnotation.h"
#implementation CustomAnnotation
#synthesize coordinate;
// ===========================================================================================================
- (id)initWithCoordinate:(CLLocationCoordinate2D) c
{
self = [super init];
if (!self) return nil;
self.coordinate = c;
return self;
}
// ===========================================================================================================
- (NSString*)subtitle
{
return #"";
}
// ===========================================================================================================
- (NSString*)title
{
return #"";
}
#end
I tried breakpoints in each methods, trying to enable Zombies with environment variables (but as this needs to be ran on the device, I'm not sure they've been really activated), without any start of solution... I still don't have any idea of what's going wrong.
Do you see how to solve that EXC_BAD_ACCESS problem ?
For any answer for the 2nd problem, please use comments and not answers if it don't solve the crash. This problem is not the heart of the question as far as I've seen.
Tip : I've put 2 comments in the code above where I found some start of solution. There are 2 places, that does not seems connected, that can be put out of the code one OR the other to solve the problem. What makes me crazy is the OR.
Runned on an iPhone 4 ios 4.2.1
[self.myMapView removeAnnotation:self.myMapView.userLocation];
This line is causing the crash from stacktrace this is what I've seen.
When I saw exc_bad_access I did a bt in gdb. I have added Guard Malloc breakpoint. Looking at the stack it said about removing annotation.
You can try overriding a method called didReceiveMemoryWarning in your view controller .m file.
What happens sometimes is that memory on the device gets low due to running apps and IOS sends a message to the app. Now the trouble is when you don't override memory warning method, its default action is to remove any subviews from memory which are not visible. This can lead to zombie variables when your application runs.
Check out the apple doc

Resources