How to set the number of displayed items of NSMenu? - objective-c

As the title mentioned. I implemented an combo-box-like NSMenu object. But I wonder how to set the number of displayed items. Like the method of NSComboBox: -setNumberOfVisibleItems:
Could any one tell me?

There is no method built in.
You could subclass NSMenu easily and override the addItem... and insertItem... methods to first check numberOfItems and remove or cancel as needed.
Of course, if you are sure you will control that menu you can just do this checking before any coded that adds items.
Of course you could also create a new delegate protocol that inherits from the NSMenuDelegate Protocol while you are at it and then easily have a delegate manage via methods like a shouldAddMenuItem or willAddMenuItem

Related

Global Access to Class instance - Optimal design approach?

OK, here is my situation and I'm really not sure on which design approach to use. So, I'd be glad to read some input on my particular case...
The scenario :
I've got a tab-based application
Initially we create an instance of NSWindowController (e.g. MyDocumentManager) which takes care of the tab creation/manipulation
Each tab contains (= is attached to) an instance of MyDocument
Now, I'm about to implement the menu actions.
For each menu there is a separate Menu Controller (actually a subclass of NSObject grouping all relevant functions), e.g. MyFileMenuController
File Menu's actions are linked to actions in a MyFileMenuController object, via Interface Builder
The question :
How is it possible that MyFileMenuController "knows" about MyDocumentManager (created in my AppDelegate.m), so that we can access current document details and perform all relevant actions? Any ideas? Which approach is preferable?
My ideas :
Passing object from class to class (not sounding that great)
Singletons (although I've honestly never used them, and do not know whether/how it could be my particular case)
Notifications & Notification Listeners
(Looking at it from the opposite side, though not sure) Delegate methods
OK (not sure if that's the best way to do it), but this is what I decided to do :
[[[NSApplication sharedApplication] delegate] MyDocumentManager]
So, as long as an object is part of my AppDelegate, this way I can access it from anywhere.
I would make an initial UIViewController linked to appDelegate.rootViewController.
In this new view controller (just call it "mainViewController") I would put my tabBar and the menuController.
I understand your interface is similar to facebook with a leftBarbutton which makes horizontal scroll and discover the menu. So in the selector for this leftBarButton I would call a method like:
- (void)discoverMenuForDocument:(MyDocument*)document {
// Set menu configuration for specific document
// Make animation to discover menu
}
Where document could be something like:
self.selectedViewController (<-- You cand make this in several ways depending on your code...)
Realize that (MyDocument*) is not an object but just a reference, so in my opinion there isn't any problem doing this.
Good luck!

What's the perferred event to handle the end of user interaction with a UIControl?

I have a view with multiple dynamically created UITextfields and UISegmented controls on it (but for purposes of this question, there could also be UIButtons, UISwitches, UISliders, or anything else that inherits from UIControl). I want to preform an action whenever the user finished interacting with any of the controls, regardless of what subclass of control it belongs to. From looking at other questions, I think I want to use addTarget:action:forControlEvents: to add observers to each of my controls after they are created, but I don't know which event I'm looking for. I've tried all the ones that are listed in the Apple Docs here that seemed relevant but none of them seem to be triggered everytime. I'm looking for something like .LostFocus in VBA, but I can't seem to find out what that is - I know there is a becomeFirstResponder method to make a control active, but I can't find anything like a "lostFirstResponder" event.
I suppose I could use isKindOfClass to tell what kind of control it is, and set up my event accordingly, but that seems a little sloppy and I feel like there should be a more direct way to do it. I could also probably set up a UITapGestureRecognizer and build up something that way, but that still feels like a workaround and not really the way it's supposed to be done.
If you're willing to subclass, you can override -resignFirstResponder to detect lost "focus", and act accordingly. This is probably only useful for things like textfields which can hold first responder status, and would not work for UISwitch for instance.
Since all UIControl objects are just UIViews, you can also override touchesEnded to detect the end of interaction with these elements.. although the more accepted way is to add your dismissal handler method as an action for all the UIControlEvents that indicate end of interaction, or just UIControlEventValueChanged.
More info on UIResponder here from Apple's Documentation:
https://developer.apple.com/library/ios/documentation/uikit/reference/UIResponder_Class/Reference/Reference.html#//apple_ref/occ/instm/UIResponder/resignFirstResponder
Many UIKit classes have delegate methods that indicate when interactions have ended, for instance UITextField has a textFieldDidEndEditing method. UITextView has similar methods.

Right way to create a customizable uiview

this question is about "style", because i think this is a very common problem and i'm looking for an elegant solution.
I have created some "advanced" UIView and i try to make them very customizable.
Usually i create the UIView structure inside a custom init method, but i need to know the value of all customizable parameter inside init method so sometimes i need a very long init method like:
initWithFrame:color:font:verticalspace:verylonglist:
I tried to use delegate design pattern but i need also to pass delegate inside init method.
My actual best solution is to leave empty the init method and move everything about layout inside a "configure" method. everytime i chance a property like background color or font i will call this method and i will rebuild the view.
I think there is a best way to solve this problem...
I'd be curious to see the code of UITableView Class, because with that class you can pass a delegate outside init method.
Check out something like a UIButton or UILabel. They both have tons of configurable aspects, however to simply create an instance of one of those objects, they need very little information.
In general, provide init methods that allow the consumer of your class to specify the least amount of information for the class to work.
If you do want to give the consumer a way to initialize the class with a bunch of values, consider using some sort of initWithDictionary: method that takes an NSDictionary of parameters. This keeps your method names short and allows the user to customize an arbitrary number of settings for your class.
You could also consider providing a way for the consumer to request an instance with some standard set of values. UITableViewCell, for example, has an initWithStyle:reuseIdentifier: method. The important part is the style - UITableViewCell provides several default styles like UITableViewCellStyleDefault and UITableViewCellStyleSubtitle.
I don't know if it is the standard/best practices way but I use a dictionary in cases like this and pass that to an initWithDictionaryinitializer. Would be possible too to create a class method that returns a 'default settings' type dictionary which can then be customized (and delegate set), so that not every param needs to be specified whenever the class is used.

Objective-C: Date picker and level indicator

I'm using an NSDatePicker and NSLevelIndicator to try and set/display certain values of an object. I don't want to use bindings. My first thought would be to try and set a delegate of the date picker/level indicator to be my controller class so that I can be notified when either of those is changed. However, NSDatePicker and NSLevelIndicator don't have a delegate (at least, none that I can see in interface builder). How then do I keep track of when these things are changed?
NSControl and its subclasses use the target / action mechanism to alert you when their value changes. Some delegate protocols work in a similar manner, but in general delegates are used to modify the behavior of an object, while target / action alerts your controller of a change in a UI control.

NSComboBox - Obtaining selected information and NSComboBoxDataSource

For the life of me, I am being continually stumped with NSComboBox.
I created an object that conforms the NSComboBoxDataSource protocol, and implemented:
- (NSInteger)numberOfItemsInComboBox:(NSComboBox *)aComboBox;
- (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(NSInteger)index;
I set the instance of my NSComboBox to use a Data Source, and set this object as the source. That works great, my implementation returns the number of items, and returns an NSString value for an item at specific indices.
Then I decide that I want to do something when something is selected, this is where my problems begin. There is no obvious method to override in the NSComboBoxDataSource protocol to handle the selection of items in the combo box.
So, I also have my object conform to NSComboBoxDelegate and implement:
- (void)comboBoxSelectionDidChange:(NSNotification *)notification;
Unfortunately, unlike NSTableView on selection, the notification's object is the NSComboBox not the object of the item selected. "Fine" I tell myself, I will call the NSComboBox method:
- (id)objectValueOfSelectedItem;
This should return the item that is selected and I can go from there. However, that method is to be called ONLY when usesDataSource is set to NO, which is not my case. Warnings start flying when I use this.
So, my question is, what is the proper way to handle NSComboBox selections when you are using a data source?
I think you want indexOfSelectedItem instead of objectValueOfSelectedItem. Then since you're the data source you should be able to call your own comboBox:objectValueForItemAtIndex: method.