toolbarSelectableItemIdentifiers: is not called - objective-c

I'm trying to make selectable NSToolbarItems. I've connected everything correctly in IB, but toolbarSelectableItemIdentifiers: is not working. It doesn't get called. The delegate is the File's Owner (subclass of NSWindowController), and the toolbar is in a sheet. Here's my code:
// TOOLBAR DLGT
- (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar {
NSLog(#"Foo");
NSMutableArray *arr = [[NSMutableArray alloc] init];
for (NSToolbarItem *item in [toolbar items]) {
[arr addObject:[item itemIdentifier]];
}
return [arr autorelease];
}
Screenshot:
Can you help me please?
No, I don't want to use BWToolkit.

Are you positive the toolbar's delegate outlet points to the class (or instance thereof) you think it does? Are any other NSToolbar delegate methods called there (easy enough to test)?

Related

GKScore challengeComposeControllerWithPlayers not showing view

Here's my code:
GKScore *scoreObj = [[[GKScore alloc] initWithCategory:category] autorelease];
scoreObj.value = playerScore;
[scoreObj
challengeComposeControllerWithPlayers:playerIDs
message:msg
completionHandler:^(UIViewController *composeController,
BOOL didIssueChallenge,
NSArray *sentPlayerIDs) {
// I don't really care what you do
}];
When I run the code, I get no overlay at all, and no error messages in the log.
Currently, playerIDs is an NSMutableArray that's empty. I don't know if that's related, but I don't want to have to pre-populate a list - that's what this native UI is suppoed to do for me, right? mgs is an empty string.
First of all in iOS 7 initWithCategory: is deprecated. Use initWithLeaderboardIdentifier: instead. As the documentation says:
Provides a challenge compose view controller with pre-selected player identifiers and a preformatted, player-editable message.
So save a pointer to this view controller, present it from your current view controller and dismiss it from within the completion handler.
Assuming you're within the implementation of your current viewController do the following:
GKScore *scoreObj = [[[GKScore alloc] initWithLeaderboardIdentifier:category] autorelease];
scoreObj.value = playerScore;
UIViewController *vc = [scoreObj challengeComposeControllerWithPlayers:playerIDs
message:msg
completionHandler:^(UIViewController *composeController,
BOOL didIssueChallenge,
NSArray *sentPlayerIDs) {
[composeController.presentingViewController dismissViewControllerAnimated: YES completion: nil];
// I don't really care what you do
}];
[self presentViewController: vc animated: YES completion: nil];
This should displayed the viewController as intended.

How do UIAlertView or UIActionSheet handle retaining/releasing themselves under ARC?

I want to create a similar class to UIAlertView which doesn't require a strong ivar.
For example, with UIAlertView, I can do the following in one of my UIViewController's methods:
UIAlertView *alertView = [[UIActionSheet alloc] initWithTitle:nil
message:#"Foo"
delegate:nil
cancelButtonTitle:#"Cancel"
otherButtonTitles:nil];
[alertView show];
... and the actionSheet will not be dealloced until it is no longer visible.
If I were to try to do the same thing:
MYAlertView *myAlertView = [[MYAlertView alloc] initWithMessage:#"Foo"];
[myAlertView show];
... the myAlertView instance will automatically be dealloced at the end of the current method I am in (e.g. right after the [myAlertView show] line).
What is the proper way to prevent this from happening without having to declare myView as a strong property on my UIViewController? (I.e. I want myView to be a local variable, not an instance variable, and I would like the MYAlertView instance to be in charge of its own lifecycle rather than my UIViewController controlling its lifecycle.)
Update: MYAlertView inherits from NSObject, so it cannot be added to the Views hierarchy.
UIAlertView creates a UIWindow, which it retains. The alert view then adds itself as a subview of the window, so the window retains the alert view. Thus it creates a retain cycle which keeps both it and its window alive. UIActionSheet works the same way.
If you need your object to stay around, and nothing else will retain it, it's fine for it to retain itself. You need to make sure you have a well-defined way to make it release itself when it's no longer needed. For example, if it's managing a window, then it should release itself when it takes the window off the screen.
If you add it as a subview of another view it will be retained. When the user selects and action or dismisses it, then it should call self removeFromSuperview as it's last act.
I've done my own AlertView with a little trick.
Just retain the object himself and release it on action. With this, you can call your custom alert vies as native one.
#import "BubbleAlertView.h"
#import <QuartzCore/QuartzCore.h>
#interface BubbleAlertView ()
...
#property (nonatomic, strong) BubbleAlertView *alertView;
...
#end
#implementation BubbleAlertView
...
- (id)initWithTitle:(NSString*)title message:(NSString*)message delegate:(id)delegate cancelButtonTitle:(NSString*)cancelButtonTitle okButtonTitle:(NSString*) okButtonTitle
{
self = [super init];
if (self)
{
// Custom initialization
self.alertView = self; // retain myself
//More init stuff
}
return self;
}
...
//SHOW METHOD
- (void)show
{
// We need to add it to the window, which we can get from the delegate
id appDelegate = [[UIApplication sharedApplication] delegate];
UIWindow *window = [appDelegate window];
[window addSubview:self.view];
// Make sure the alert covers the whole window
self.view.frame = window.frame;
self.view.center = window.center;
}
- (IBAction)btPressed:(id)sender
{
//Actions done
[UIView animateWithDuration:0.4f animations:^{
self.vContent.alpha = 0.f;
} completion:^(BOOL finished) {
[self.view removeFromSuperview];
self.alertView = nil; // deallocate myself
}];
}
You need to retain it somehow until it is released.
I do not really understand why you cannot implement it as subclass of UIView. Then you could use the view hierarchy as the keeper of a strong reference (retain +1). But you will have good reasons for not doing so.
If you don't have such a thing then I would use an NSMutableArray as class varialbe (meaning statc). Just declare it in the #interface block and initialize it with nil:
#interface
static NSMutableArray _allMyAlerts = nil;
provide an accessor.
-(NSMutableArray *) allMyAlerts {
if (_allMyAlerts == nil) {
_allMyAlerts = [[NSMutableArray alloc] init];
}
return _allMyAlerts
}
Within the init method do the following:
- (id) init {
self = [super init];
if (self) {
[[self allMyAlerts] addObject:self];
}
}
You will invode some method when the alert is dismissed.
- (void) dismissAlert {
// Do your stuff here an then remove it from the array.
[[self allMyAlerts] removeObject:self];
}
You may want to add some stuff to make it mutli threading save, which it is not. I just want to give an example that explains the concept.
allMyAlert could be an NSMutableSet as well. No need for an array as far as I can see. Adding the object to an array or set will add 1 to the retain count and removing it will reduce it by 1.

How do I insert UITextField in to UITableView after button is pressed

In master view application xcode generates ready app with table view and the plus button. I want to change that button to to add a new cell but not with the date as it is by default. I want to add two text fields like label->textfield, label->textfield.
In code I have this:
- (void)viewDidLoad{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.leftBarButtonItem = self.editButtonItem;
UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(insertNewObject:)];
self.navigationItem.rightBarButtonItem = addButton;
self.detailViewController = (GCDetailViewController *) [[self.splitViewController.viewControllers lastObject] topViewController];
}
and the function:
- (void)insertNewObject:(id)sender{
if (!_objects) {
_objects = [[NSMutableArray alloc] init];
}
[_objects insertObject:[UITextField alloc] atIndex:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
Thank You
The way to think about this is model-view-controller (MVC). _objects is your model representing whatever the user thinks is in the table. Say it's a to-do list, then objects could be an array of NSObject subclass you create like TodoItem.
You would insert new TodoItems into _objects, then tell your table (the "View" in MVC) that it's model has changed. You can do that imprecisely using reloadData, or in a more targeted fashion as your code suggests, calling insertRowsAtIndexPaths - but that call must be sandwiched between tableView beginUpdates and endUpdates.
You can add textFields in code in your cellForRowAtIndexPath, or in the cell prototype in storyboard. Your table view datasource should always refer to objects... i.e. numberOfRows answers self.objects.count, cellForRowAtIndexPath gets:
TodoItem *item = [self.objects objectAtIndexPath:indexPath.row];
and uses that item's properties to initialize the textField's text. Also, incidentally, objects should be declared like this:
#property(strong,nonatomic) NSMutableArray *objects;
...and your code should refer to self.objects almost everywhere (not _objects). Initializing it on the first insert is too late, because the table needs it to be valid right-away, as soon as it's visible. Usually, a good practice is a "lazy" init replacing the synthesized getter...
- (NSMutableArray *)objects {
if (!_objects) { // this one of just a few places where you should refer directly to the _objects
_objects = [NSMutableArray array];
}
return _objects;
}
You might find using the free Sensible TableView framework really helpful here. Here is some sample code to illustrate how you'd do this using the framework:
- (void)insertNewObject:(id)sender{
SCTableViewSection *section = [self.tableViewModel sectionAtIndex:0];
[section addCell:[SCTextFieldCell cellWithText:#"Enter Text"]];
}
Comes in really handy for these types of situations.

MKMapView viewForAnnotation: returning nil

I'm trying to remove a pin from a map. I have an observer on the #"selected" property of the MKPinAnnotationView so I know which object to delete. When the user taps the trash can icon and a pin is selected, this method gets called:
- (IBAction)deleteAnnotationView:(id)sender {
MKPinAnnotationView *pinView = (MKPinAnnotationView *)[self.mapView viewForAnnotation:self.currentAddress];
[pinView removeObserver:self forKeyPath:#"selected"];
[self.mapView removeAnnotation:self.currentAddress];
[self.map removeLocationsObject:self.currentAddress];
}
This method works fine if I do not drag the pin anywhere. If I drag the pin, my pinView in the above method returns nil, and the MKPinAnnotationView never gets removed from the MKMapView. I'm not sure why. Here's the didChangeDragState delegate method:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view didChangeDragState:(MKAnnotationViewDragState)newState fromOldState:(MKAnnotationViewDragState)oldState {
if (newState == MKAnnotationViewDragStateEnding) {
CLLocationCoordinate2D draggedCoordinate = view.annotation.coordinate;
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
CLLocation *location = [[CLLocation alloc] initWithLatitude:draggedCoordinate.latitude longitude:draggedCoordinate.longitude];
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
// Check for returned placemarks
if (placemarks && [placemarks count] > 0) {
CLPlacemark *topResult = [placemarks objectAtIndex:0];
AddressAnnotation *anAddress = [AddressAnnotation annotationWithPlacemark:topResult inContext:self.managedObjectContext];
view.annotation = anAddress;
self.currentAddress = anAddress;
}
}];
}
}
In both the didChangeDragState: and deleteAnnotationView: methods, my self.address object has a valid address. For some reason though, when the pin is dragged, the pinView is nil. Any thoughts? Thanks!
Observing an annotation view's selected property via KVO should be unnecessary since there's the didSelectAnnotationView delegate method and (even better in your case) the selectedAnnotations property in MKMapView.
Assuming the user taps the trash can after selecting a pin, the trash can tap method can get the currently selected annotation through the selectedAnnotations property. For example:
if (mapView.selectedAnnotations.count == 0)
{
//No annotation currently selected
}
else
{
//The currently selected annotation is the first object in the array...
id<MKAnnotation> ann = [mapView.selectedAnnotations objectAtIndex:0];
//do something with ann...
}
In the above example, there was no need to access the annotation's view, no observer and no need for your own "currentAddress" property.
If instead you want to do some action immediately when an annotation is selected, you can put the code in the didSelectAnnotationView delegate method. There, the annotation selected is view.annotation.
Regarding the issue on the drag-end, the current code is completely replacing the view's annotation. I think this is only a good idea when the view is being created or re-used in the viewForAnnotation delegate method. In the drag-end method, you should instead try updating the view.annotation's properties instead of replacing with an entirely new object.

Unique ID on NSViews

Is there any kind of ID that can be used and set in the .nib/.xib via Xcode that can be queried at runtime to identify a particular view instance from code?
In particular when having multiple copies of the same NSView subclass in our interface how can we tell which one we're currently looking at?
In Interface Builder, there is a way to set the "identifier" of an NSView. In this case, I'll use the identifier "54321" as the identifier string.
NSView Conforms to the NSUserInterfaceItemIdentification Protocol, which is a unique identifier as an NSString. You could walk through the view hierarchy and find the NSView with that identifier.
So, to build on this post about getting the list of NSViews, Get ALL views and subview of NSWindow, you could then find the NSView with the identifier you want:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSView *viewToFind = [self viewWithIdentifier:#"54321"];
}
- (NSView *)viewWithIdentifier:(NSString *)identifier
{
NSArray *subviews = [self allSubviewsInView:self.window.contentView];
for (NSView *view in subviews) {
if ([view.identifier isEqualToString:identifier]) {
return view;
}
}
return nil;
}
- (NSMutableArray *)allSubviewsInView:(NSView *)parentView {
NSMutableArray *allSubviews = [[NSMutableArray alloc] initWithObjects: nil];
NSMutableArray *currentSubviews = [[NSMutableArray alloc] initWithObjects: parentView, nil];
NSMutableArray *newSubviews = [[NSMutableArray alloc] initWithObjects: parentView, nil];
while (newSubviews.count) {
[newSubviews removeAllObjects];
for (NSView *view in currentSubviews) {
for (NSView *subview in view.subviews) [newSubviews addObject:subview];
}
[currentSubviews removeAllObjects];
[currentSubviews addObjectsFromArray:newSubviews];
[allSubviews addObjectsFromArray:newSubviews];
}
for (NSView *view in allSubviews) {
NSLog(#"View: %#, tag: %ld, identifier: %#", view, view.tag, view.identifier);
}
return allSubviews;
}
Or, since you are using an NSView subclass, you could set the "tag" of each view at runtime. (Or, you could set the identifier at run-time.) The nice thing about tag, is that there is a pre-built function for finding a view with a specific tag.
// set the tag
NSInteger tagValue = 12345;
[self.myButton setTag:tagValue];
// find it
NSButton *myButton = [self.window.contentView viewWithTag:12345];
Generic NSView objects cannot have their tag property set in Interface Builder. The tag method on NSView is a read-only method and can only be implemented in subclasses of NSView. NSView does not implement a setTag: method.
I suspect the other answers are referring to instances of NSControl which defines a -setTag: method and has an Interface Builder field to allow you to set the tag.
What you can do with generic views is use user-defined runtime attributes. This allows you to pre-set the values of properties in your view object. So if your view defined a property like so:
#property (strong) NSNumber* viewID;
Then in the user-defined attributes section of the Identity inspector in Interface Builder, you could add a property with the keypath viewID, the type Number and the value 123.
In your view's -awakeFromNib method, you can then access the value of the property. You will find that in the example above, the viewID property of your view will have been pre-set to 123.
Here's how to simulate "tags" in OSX without subclassing.
In iOS:
{
// iOS:
// 1. You add a tag to a view and add it as a subView, as in:
UIView *masterView = ... // the superview
UIView *aView = ... // a subview
aView.tag = 13;
[masterView addSubview:aView];
// 2. Later, to retrieve the tagged view:
UIView *aView = [masterView viewWithTag:13];
// returns nil if there's no subview with that tag
}
The equivalent in OSX:
#import <objc/runtime.h> // for associated objects
{
// OSX:
// 1. Somewhere early, create an invariant memory address
static void const *tag13 = &tag13; // put at the top of the file
// 2. Attach an object to the view to which you'll be adding the subviews:
NSView *masterView = ... // the superview
NSView *aView = ... // a subview
[masterView addSubview:aView];
objc_setAssociatedObject(masterView, tag13, aView, OBJC_ASSOCIATION_ASSIGN);
// 3. Later, to retrieve the "tagged" view:
NSView *aView = objc_getAssociatedObject(masterView, tag13);
// returns nil if there's no subview with that associated object "tag"
}
Edit: The associated object "key" (declared as const void *key) needs to be invariant. I'm using an idea by Will Pragnell (https://stackoverflow.com/a/18548365/236415).
Search Stack Overflow for other schemes for making the key.
Here's a simple way get NSView tags in OSX without subclassing.
Although NSView's tag property is read-only, some objects
that inherit from NSView have a read/write tag property.
For example, NSControl and NSImageView have a r/w tag properties.
So, simply use NSControl instead of NSView, and disable (or ignore)
NSControl things.
- (void)tagDemo
{
NSView *myView1 = [NSView new];
myView1.tag = 1; // Error: "Assignment to readonly property"
// ---------
NSControl *myView2 = [NSControl new]; // inherits from NSView
myView2.tag = 2; // no error
myView2.enabled = NO; // consider
myView2.action = nil; // consider
// ---------
NSImageView *myView3 = [NSImageView new]; // inherits from NSControl
myView3.tag = 3; // no error
myView3.enabled = NO; // consider
myView3.action = nil; // consider
}
Later, if you use viewWithTag: to fetch the view, be sure to specify NSControl (or NSImageView) as the returned type.