The location is working but the title isn't appearing, most strange.
location.latitude = (double) 51.501468;
location.longitude = (double) -0.141596;
// Add the annotation to our map view
MapViewAnnotation *newAnnotation = [[MapViewAnnotation alloc] initWithTitle:#"ABC" andCoordinate:location];
[self.mapView addAnnotation:newAnnotation];
// [newAnnotation release];
MapViewAnnotation.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface MapViewAnnotation : NSObject <MKAnnotation> {
NSString *title;
CLLocationCoordinate2D coordinate;
}
#property (nonatomic, copy) NSString *title;
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
- (id)initWithTitle:(NSString *)ttl andCoordinate:(CLLocationCoordinate2D)c2d;
#end
and MapViewAnnotation.m
#import "MapViewAnnotation.h"
#implementation MapViewAnnotation
#synthesize title, coordinate;
- (id)initWithTitle:(NSString *)ttl andCoordinate:(CLLocationCoordinate2D)c2d {
title = ttl;
coordinate = c2d;
return self;
}
#end
[newAnnotation release] is remmed out to keep the ARC happy :-)
Any ideas?
This did the trick:
[mapView selectAnnotation:newAnnotation animated:YES];
previously the title would only show if you clicked on the Pin.
you call annotation delegates refer this link, mapkit-example-in-iphone
That code looks fine (except for the non-standard implementation of the init method).
The most likely reason the title (callout) isn't appearing is that in your viewForAnnotation delegate method, you are not setting canShowCallout to YES (it defaults to NO).
In the viewForAnnotation delegate method, after you create the MKAnnotationView, set canShowCallout to YES.
Unrelated to the title/callout not showing, the init method in your MapViewAnnotation class should be implemented like this:
- (id)initWithTitle:(NSString *)ttl andCoordinate:(CLLocationCoordinate2D)c2d {
self = [super init];
if (self) {
title = ttl;
coordinate = c2d;
}
return self;
}
Related
App stops working after adding messageUI.framework
I have a fully working App created and I want to add some new features. I added the messageUI.framework and it stoped working. If I delete the Framework it works again, but this is not the idea.
The error I received is:
-[Account initWithCoder:]: unrecognized selector sent to instance 0x1cd953a0
I have a custom NSObject called Account and I get the error when loading it:
myAccounts = [[NSMutableArray alloc] init];
myAccounts = [NSKeyedUnarchiver unarchiveObjectWithFile:savePath]; //Crashes in this line
When it was previously saved with:
[NSKeyedArchiver archiveRootObject:myAccounts toFile:savePath];
Can it be caused by some overlapping variables or something like that?
Notes:
Account.h is like:
#define kAccName #"name"
#define kAccID #"identifier"
#define kAccInitAmount #"initAmount"
#define kAccActive #"active"
#import <Foundation/Foundation.h>
#import "Transfer.h"
#interface Account : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *identifier;
#property (nonatomic) double initAmount;
#property (nonatomic) BOOL active;
- (id)initWithID:(NSString *)ident;
#end
And Account.m is:
#import "Account.h"
#implementation Account
#synthesize name;
#synthesize identifier;
#synthesize initAmount;
#synthesize active;
- (id)init {
self = [super init];
if (self) {
self.active = TRUE;
}
return self;
}
- (id)initWithID:(NSString *)ident {
self = [super init];
if (self) {
self.active = TRUE;
self.identifier = ident;
}
return self;
}
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
self.name = [decoder decodeObjectForKey:kAccName];
self.identifier = [decoder decodeObjectForKey:kAccID];
self.initAmount = [decoder decodeDoubleForKey:kAccInitAmount];
self.active = [decoder decodeBoolForKey:kAccActive];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.name forKey:kAccName];
[encoder encodeObject:self.identifier forKey:kAccID];
[encoder encodeDouble:self.initAmount forKey:kAccInitAmount];
[encoder encodeBool:self.active forKey:kAccActive];
}
#end
I just hit this problem, I cannot tell you exactly why, but refactor your Account object to any other name, I used Accounts, and it works. Something at runtime in the MessageUI.framework is colliding with your Account object name.
I'm subclassing NSTextField
MultiTextField.h
#import <AppKit/AppKit.h>
#interface MultiTextField : NSTextField {
id storedObject;
}
#property (nonatomic, retain) id storedObject;
#end
MultiTextField.m
#import "MultiTextField.h"
#implementation MultiTextField
#synthesize storedObject;
#end
to store a pointer to an object, which I want to "rename".
I made this textfield editable and have a delegate which listens to controlTextDidChange: and works fine:
- (void)controlTextDidChange:(NSNotification *)aNotification {
NSTextView *textView = [[aNotification userInfo] objectForKey:#"NSFieldEditor"];
NSString *theString = [[textView textStorage] string];
if([theString length] > 0 ) {
MyObject *theObject = ???; // I need access to the MultiTextField.storedObject!
[theObject setName:theString];
}
}
the only problem is that I can't access the storedObject (see comment in the if-block).
So how do I access that storedObject?
Try this:
MyObject *theObject = [[aNotification object] storedObject];
I can't call the class "MapAnnotation" on XCode 4. I'm working on coding that will allow me to place pins in an MKMapView. I believe I've imported the right delegates. Here's what I've got:
MillersLocations.h
#import <Foundation/Foundation.h>
#import <MapKit/MKAnnotation.h>
#import <MapKit/MapKit.h>
#interface MillersLocations : NSObject <MKAnnotation> {
CLLocationCoordinate2D coordinate;
NSString *title;
NSString *subtitle;
}
#property (nonatomic, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *subtitle;
#end
MillersLocations.m
#import "MillersLocations.h"
#implementation MillersLocations
#synthesize coordinate, title, subtitle;
-(void)dealloc{
[title dealloc];
[subtitle dealloc];
[super dealloc];
}
#end
And here's my view controller for the map view:
MapViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#interface MapViewController : UIViewController <MKMapViewDelegate> {
IBOutlet MKMapView *mapView;
}
#end
MapViewController.m (just the segment I'm looking at)
#import "MapViewController.h"
#import "MillersLocations.h"
#implementation MapViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
}
return self;
}
//skipping forward
- (void)viewDidLoad
{
[super viewDidLoad];
MKCoordinateRegion store1;
store1.center.latitude = 36.8605679;
store1.center.longitude = -76.2866713;
store1.span.latitudeDelta = 0.1;
store1.span.longitudeDelta = 0.1;
[mapView setRegion:store1 animated:YES];
//this is where I'm trying to put this code in:
MapAnnotation* annotation = [[MapAnnotation alloc] initWithCoordinate:newCoord];
//BUT "MapAnnotation" isn't an option
}
I'm wondering if I haven't imported the rights classes or something. I've Googled it and can't seem to find where "MapAnnotation" lies. What do I need to import to get access to "MapAnnotation"? Everything works fine up until that point.
Thanks for the help. I'm just learning this stuff!
What makes you think that there is class called MapAnnotation? Your annotation class is called MillersLocations. You need to create instances of that class and call [mapView addAnnotation:millersLocationInstance]; (or something similar).
My application contains a PLAY/PAUSE button that is set to type Toggle in Interface Builder. I use it - as the name reveals - to play back my assets or to pause them.
Further, I am listening to the SPACE key to enable the same functionality via the keyboard shortcut. Therefore, I use keyDown: from NSResponderin my application. This is done in another subview. The button itself is not visible at this time.
I store the current state of playback in a Singleton.
How would you update the title/alternative title for the toogle button while taking into account that its state could have been altered by the keyboard shortcut? Can I use bindings?
I managed to implement the continuous update of the button title as follows. I added a programmatic binding for the state (in the example buttonTitle). Notice, that the IBAction toggleButtonTitle: does not directly change the button title! Instead the updateButtonTitle method is responsible for this task. Since self.setButtonTitle is called the aforementioned binding gets updated immediately.
The following example shows what I tried to describe.
// BindThisAppDelegate.h
#import <Cocoa/Cocoa.h>
#interface BindThisAppDelegate : NSObject<NSApplicationDelegate> {
NSWindow* m_window;
NSButton* m_button;
NSString* m_buttonTitle;
NSUInteger m_hitCount;
}
#property (readwrite, assign) IBOutlet NSWindow* window;
#property (readwrite, assign) IBOutlet NSButton* button;
#property (readwrite, assign) NSString* buttonTitle;
- (IBAction)toggleButtonTitle:(id)sender;
#end
And the implementation file:
// BindThisAppDelegate.m
#import "BindThisAppDelegate.h"
#interface BindThisAppDelegate()
- (void)updateButtonTitle;
#end
#implementation BindThisAppDelegate
- (id)init {
self = [super init];
if (self) {
m_hitCount = 0;
[self updateButtonTitle];
}
return self;
}
#synthesize window = m_window;
#synthesize button = m_button;
#synthesize buttonTitle = m_buttonTitle;
- (void)applicationDidFinishLaunching:(NSNotification*)notification {
[self.button bind:#"title" toObject:self withKeyPath:#"buttonTitle" options:nil];
}
- (IBAction)toggleButtonTitle:(id)sender {
m_hitCount++;
[self updateButtonTitle];
}
- (void)updateButtonTitle {
self.buttonTitle = (m_hitCount % 2 == 0) ? #"Even" : #"Uneven";
}
#end
If you store your state in an enum or integer a custom NSValueTransformer will help you to translate a state into its button title equivalent. You can add the NSValueTransformer to the binding options.
NSDictionary* options = [NSDictionary dictionaryWithObject:[[CustomValueTransformer alloc] init] forKey:NSValueTransformerBindingOption];
[self.button bind:#"title" toObject:self withKeyPath:#"buttonTitle" options:options];
I have one class that controls one window, and another class that controls a different window in the same xib, however, the second window never displays what it should.
In the first class I alloc and init the second class, then pass some information to it. In the second class it displays that data in the table view.
Yes, in the .xib I have all the connections set up correctly, I've quadruple checked. Also the code is correct, same with the connections, I've quadruple checked.
Edit: and yes, there's data in the arrays, and the classes are NSObjects.
Edit2: I kinda found the problem. For some reason, the array is filled with contents, but it's returning 0 as a count.
Edit 9000:
Here's the code:
Answer.h
#import <Cocoa/Cocoa.h>
#interface MSAnswerView : NSObject {
IBOutlet NSWindow *window;
NSArray *User;
NSArray *Vote;
NSArray *Text;
IBOutlet NSTableView *view;
IBOutlet NSTableColumn *voteCount;
IBOutlet NSTableColumn *saidUser;
IBOutlet NSTextView *body;
}
-(void)setUpWithVoteCount:(NSArray *)array User:(NSArray *)user Text:(NSArray *)text;
#property (nonatomic, retain) NSWindow *window;
#property (nonatomic, retain) NSTableView *view;
#property (nonatomic, retain) NSTableColumn *voteCount;
#property (nonatomic, retain) NSTableColumn *saidUser;
#property (nonatomic, retain) NSTextView *body;
#end
.m
#import "MSAnswerView.h"
#implementation MSAnswerView
#synthesize view;
#synthesize voteCount;
#synthesize saidUser;
#synthesize body;
#synthesize window;
-(void)awakeFromNib
{
[view setTarget:self];
[view setDoubleAction:#selector(bodydata)];
[view reloadData];
}
-(void)setUpWithVoteCount:(NSArray *)array User:(NSArray *)user Text:(NSArray *)text
{
Vote = array;
User = user;
Text = text;
if (window.isVisible = YES) {
[view reloadData];
[view setNeedsDisplay];
}
}
-(int)numberOfRowsInTableView:(NSTableView *)aTable
{
return [User count];;
}
-(id)tableView:(NSTableView *)aTable objectValueForTableColumn:(NSTableColumn *)aCol row:(int)aRow
{
if (aCol == voteCount)
{
return [Vote objectAtIndex:aRow];
}
else if (aCol == saidUser)
{
return [User objectAtIndex:aRow];
}
else
{
return nil;
}
}
-(void)bodydata
{
int index = [view selectedRow];
[body setString:[Text objectAtIndex:index]];
}
#end
The problems in your code are numerous.
For one thing, this comparison in -setUpWithVoteCount:User:Text: is incorrect:
window.isVisible = YES
That should be the comparison operator, == not the assignment operator =.
Secondly, you are naming your ivars and methods incorrectly. Instance variables (in fact, variables of any type) should start with a lower-case letter. This is to distinguish them from class names. Check out the Apple coding guidelines.
I'd also suggest that a name like text is a bad name for a variable that stores a collection like an NSArray. Instead, you should name it something like textItems so it's clear that the variable represents a collection and not a single string.
Also, the class itself is poorly named. You have called it MSAnswerView but it's not a view, it's some type of window controller. At the very least call it MSAnswerWindowController. Better still would be to make it a subclass of NSWindowController and make it File's Owner in its own nib. This is the standard pattern for window controllers.
Your method -setUpWithVoteCount:User:Text: should really be an initializer:
- initWithVoteCount:user:text:
That way it's clear what it's for and that it should be called once at object creation time.
The main problem, however, is that you're not retaining the values that you pass in to your setup method. That means that if no other object retains a reference to them, they will go away at some indeterminate point in the future. If you access them at a later time, you will crash or at the very least receive bad data, which is what's occurring.
Of course, you must also add a -dealloc method in this case to ensure you release the objects when you're finished with them.
Putting all those suggestions together, your class should really look something like this:
MSAnswerWindowController.h:
#import <Cocoa/Cocoa.h>
//subclass of NSWindowController
#interface MSAnswerWindowController : NSWindowController <NSTableViewDataSource>
{
//renamed ivars
NSArray *users;
NSArray *voteCounts;
NSArray *textItems;
IBOutlet NSTableView *view;
IBOutlet NSTableColumn *voteCount;
IBOutlet NSTableColumn *saidUser;
IBOutlet NSTextView *body;
}
//this is now an init method
- (id)initWithVoteCounts:(NSArray *)someVoteCounts users:(NSArray *)someUsers textItems:(NSArray *)items;
//accessors for the ivars
#property (nonatomic, copy) NSArray* users;
#property (nonatomic, copy) NSArray* voteCounts;
#property (nonatomic, copy) NSArray* textItems;
#property (nonatomic, retain) NSWindow *window;
#property (nonatomic, retain) NSTableView *view;
#property (nonatomic, retain) NSTableColumn *voteCount;
#property (nonatomic, retain) NSTableColumn *saidUser;
#property (nonatomic, retain) NSTextView *body;
#end
MSAnswerWindowController.m:
#import "MSAnswerWindowController.h"
#implementation MSAnswerWindowController
//implement the init method
- (id)initWithVoteCounts:(NSArray*)someVoteCounts users:(NSArray*)someUsers textItems:(NSArray*)items
{
//this is an NSWindowController, so tell super to load the nib
self = [super initWithWindowNibName:#"MSAnswerWindow"];
if(self)
{
//copy all the arrays that are passed in
//this means we hold a strong reference to them
users = [someUsers copy];
voteCounts = [someVoteCounts copy];
textItems = [items copy];
}
return self;
}
//make sure we deallocate the object when done
- (void)dealloc
{
self.users = nil;
self.voteCounts = nil;
self.textItems = nil;
[super dealloc];
}
//this is called when the window first loads
//we do initial window setup here
- (void)windowDidLoad
{
[view setTarget:self];
[view setDataSource:self];
[view setDoubleAction:#selector(bodydata)];
}
//this is called when the view controller is asked to show its window
//we load the table here
- (IBAction)showWindow:(id)sender
{
[super showWindow:sender];
[view reloadData];
}
- (NSInteger)numberOfRowsInTableView:(NSTableView*)aTable
{
return [users count];
}
- (id)tableView:(NSTableView*)aTable objectValueForTableColumn:(NSTableColumn*)aCol row:(NSInteger)aRow
{
if (aCol == voteCount)
{
return [voteCounts objectAtIndex:aRow];
}
else if (aCol == saidUser)
{
return [users objectAtIndex:aRow];
}
return nil;
}
- (void)bodydata
{
NSInteger index = [view selectedRow];
[body setString:[textItems objectAtIndex:index]];
}
#synthesize users;
#synthesize voteCounts;
#synthesize textItems;
#synthesize view;
#synthesize voteCount;
#synthesize saidUser;
#synthesize body;
#end