Is there a way to programmatically determine the proper sizes for Apple's built-in controls? - cocoa

When writing Cocoa apps, I do the majority of the user interface layout programmatically. For example:
NSRect popUpFrame = NSMakeRect(10, 10, 100, kDefaultPopUpButtonHeight);
NSPopUpButton * popUp = [[NSPopUpButton alloc] initWithFrame:popUpFrame];
//...
My question is about that kDefaultPopUpButtonHeight constant. I currently maintain a source file full of such constants, and I fill in the proper sizes manually. I am able to determine the correct sizes by dropping a new control into a blank view in Interface Builder and then checking its properties to see what size IB gives it.
There must be a better way. Is it possible to access these values at runtime? Ideally, I would expect every NSControl to have a class method something like: +(NSSize)defaultSize, or, for controls like NSButton that have different default sizes depending on the particular button style used, something like +(NSSize)defaultSizeForButtonStyle:(NSButtonStyle)buttonStyle.
Apple's Human Interface Guidelines has information about control layout and the spacing between controls, but it doesn't say anything about the proper sizes for individual controls.

I agree with Peter, and would recomend that you use Interface Builder. But if that isn't appropriate in your situation, here's one way to find the best size for most controls:
NSSize idealSize = [[control cell] cellSize];
If you need more control over the sizing, you can use the -[NSCell cellSizeForBounds:] method.
Also, cellSize really gives you the minimum size for a control, not necessarily the best size. For example, for a Cocoa aqua style push button with the text "OK", it would return a width that more narrow than the HIG would recommend. For your purposes, it sounds like you're only interested in the fixed hight portion of the size. -[NSCell cellSize] should work great.

Related

iPhone X interface programming without autolayout

I have an old app source code that uses storyboard & xib without auto layout or size classes. Even though I am rewriting it from scratch, it would take some time as number of modules are too many. The immediate need is to fix the code that manually sets frame of UI elements by detecting screen size at runtime. So a code written this way places elements outside the safe area:
self.controlBarBkg568h.frame = CGRectMake(0, CGRectGetHeight(self.view.bounds) - tapBarBackgroundImage.size.height, CGRectGetWidth(self.view.bounds), tapBarBackgroundImage.size.height);
Since there are so many interface elements that are placed this way, I am wondering what is an easy way to fix the code that works on iPhone X?
Use the safeAreaInsets property on UIView. This is a UIEdgeInsets object that describes the safe area for the current view. (Documentation)
In your specific case, you should be able to change your code to something like this:
self.controlBarBkg568h.frame = CGRectMake(0, CGRectGetHeight(self.view.bounds) - tapBarBackgroundImage.size.height - self.view.safeAreaInsets.bottom, CGRectGetWidth(self.view.bounds), tapBarBackgroundImage.size.height);
If you have many views positioned this way, you are going to have to edit them all to respect the safe area. This is one of the downsides of manual layout — it's hard to change in the future (especially if there aren't any helper layout variables where you can edit many variables at once).
More documentation about positioning content relative to the safe area is available here: https://developer.apple.com/documentation/uikit/uiview/positioning_content_relative_to_the_safe_area

Make control horizontally aligned in Xcode

I'm trying to horizontally align my UILable for iPhone screen, by there is no x or left in mylable properties.
How can center-align it on the screen?
The property you are looking for is the frame or center property. In this case, center is probably your best bet. All you need to do is something like this:
CGPoint newCenter = myLabel.center;
newCenter.x = self.view.center.x;
myLabel.center = newCenter;
or
myLabel.center = CGPointMake ( self.view.center.x, myLabel.center.y );
There are more advanced ways of maintaining center alignment using autolayout, or the autoresizingmask. If you are interested in learning about them (autolayout being the most robust and in some ways user friendly) I recommend watching the WWDC 2012 sessions about Auto Layout
The best way to determine what types of properties an object has is to use the class reference. When you first look at UILabel class reference you'll see there are no properties for adjusting the view of the label.
But if you look at the very top you can see where UILabel inherits from. You can see it inherits from UIView, which makes sense to look at for what you need since you're asking how to adjust the labels position on the screen (the view).
Click on UIView and you'll be taken directly to it's reference. Scroll down until you see a list of "Tasks" under which there are several categories, each with a number of properties. You're interested in the "Bounds and Frame Rectangles" category in which there is a property called "frame".
By now we've determined we can set the UILabels frame via myLabel.view.frame
But you may be wondering how to set a frame and so you need to click on 'frame' in the class reference you've been looking at.
You'll see that frame is of type CGRect, so you can use CGRectMake to set the frame. But now you're asking how do I do that, so we click on the CGRect reference.
And you can see that CGRect is a struct made of a CGPoint and a CGSize and if necessary please look those up as well to understand how they are defined.
I took this time to explain the flow of using the docs so that you can do it for yourself in the future. You can survive for awhile on the help of others, but eventually you'll need to just dive into the docs and figure it out for yourself.
In short, you can set the position of your label using the following:
myLabel.frame = CGRectMake(self.view.frame.size.width/2-someWidth/2, self.view.frame.size.height/2-someHeight/2, someWidth, someHeight);
Where someWidth and someHeight are custom variables of float type and self.view is some superview you want to center the label in.
I did not test the code but believe this should work. Good luck to you.
This works:
mylabel.center = CGPointMake(CGRectGetMidX(self.view.bounds), mylabel.center.y);

Custom drawing selection in NSTextView

I've got a NSTextView where I'd like to control drawing of the highlight/selection. Anyone know which method I have to overwrite so I can control what to draw in the selection rect?
You do not specify what you want to draw, so it's not easy to give you a straight answer.
If it's just an attributed string (Font, Style, Color, Background), you do not need anything fancy, just look for NSAttributedString. There are methods like -setSelectedTextAttributes: and the delegate method -textViewDidChangeSelection:
Generally, you should not subclass NSTextView if you don't have a very good reason to do so. You can do almost anything by just utilizing the usual delegation mechanisms.
If it's something very customized, there is a variety of possibilities depending on if you want to customize the selection (-setSelectedRange:) draw an overlay view (get the frame of the selectedRange) or mess around with the low level layout engine (-layoutManager). It really depends.
If you don't want to use the standard attributes to highlight text, eg by using an CALayer, you can get the rects containing the selection from the layoutManager of the NSTextView.

make NSRect selectable

Is there a simple way to create a selectable NSRect in Cocoa? In need a rectangle that can be selected and stays selected after a mouse click.
Thanks.
NSRect is just a struct with a position and size. It's not an object that can actually do anything or have any properties other than a width and height. It sounds like what you want is to create an NSView that can be selected. (Here's Apple's Guide on the subject.)
Though not as immediate as you would like, you may be interested in the management of tracking rectangles and tracking areas performed by NSView class.
This mechanism allows you to define specific areas of your custom view. Then, an event is generated whenever the cursor enters or leaves the area, or a mouse button is pressed in this area (-mouseEntered:, -mouseExited:, -mouseDown:, -mouseUp:, -mouseDragged:, ... of NSResponder class). This up to you to define what you want your application do in response to these events (set the rectangle as selected and display it accordingly).
For an example implementation of this, take a look at the Sketch example included with the Apple developer tools (look in /Developer/Examples/AppKit). Sketch allows the user to create new graphics (including rectangles, but also ovals, lines, and text), select them, move them around in the document, etc. In particular, you'll probably want to look at the SKTGraphic class, which represents a single graphic object in the document, and the SKTGraphicView class, which is an NSView subclass that perform the actual layout and drawing, handling mouse events for dragging views around, etc.

How to change the height of an NSProgressIndicator?

By default, cocoa progress bars are slightly fat and I want something a little slimmer, like the progress bars seen in the Finder copy dialog. However, Interface Builder locks the NSProgressIndicator control height to 20 pixels and my programmatic attempts to slim down aren't working, as calls to
[progressBar setControlSize:NSMiniControlSize];
and
[progressBar setControlSize:NSSmallControlSize];
in awakeFromNib don't do anything, and the suggestive looking NSProgressIndicatorThickness seen in the header file doesn't seem to plug into any methods that I can see.
What's the trick?
Those calls should have worked. In Interface Builder, in the Geometry pane (the one whose icon is a ruler), there is an equivalent control size selector that offers "Regular" and "Small" sizes.
in IB
select your NSProgressIndicator control
in the utilities view select the View Effects inspector
press + in Content Filters
select Lanczos Scale Transform filter
set the appropriate scale value in the Scale row
set the Aspect Ratio too if you need to change the height only

Resources