NSString sizeWithAttributes: Inaccuracy - objective-c

I want to know the width of an NSString displayed on the screen in pixels. So I can fit an NSTextField its bounds to be exactly the length of the string itself. So I used IB's "Label" NSTextField and for those who don't know what I mean, I got a label with title "Label", font "Lucida Grande 13px", not selectable, not editable, regular size, no background and according to IB its width is 38px wide.
If I want to get its width programatically I use
[#"Label" sizeWithAttributes: [NSDictionary dictionaryWithObject: [NSFont fontWithName: #"Lucida Grande" size: 13] forKey: NSFontAttributeName]].width
Which will give me 33.293457 . So that's about 5 px of the real width..

I believe what you are noticing is the difference between the frame of the control and it's layout frame. See Frame vs Layout Frame for a good explanation.
You are doing the right thing in computing the width. For a Label, there is no extra padding at the top or bottom of the control, which is why you saw no problems with the height of your control. However, on the left and right of the control, there are an additional three pixels. This can be verified by looking at the Frame and Layout of a Label control in IB.
So, the ~5 pixels you noticed is actually exactly 6. Once you take into account this padding, you should have no further trouble.
Unfortunately, there is no API to determine what the padding is for various controls (Push Buttons have an additional 6 pixels on each side). I would suggest filing a bug report at http://bugreport.apple.com - Apple does base what APIs they provide in part on the number of requests for them. While IB will tell you, you will need to code those values yourself. If they change in the next OS release, you will need to update your application.

That actually sounds reasonable. You are comparing two different widths; the width of a raw string vs. the width of a string contained in an NSTextField. The contained string likely has just a tad bit of padding on either end, among other minor differences.

Related

How to autolayout in Xcode buttons filled with different sized images?

I am working on a project with multiple buttons on a viewcontroller and I used autolayout. I want all the buttons to have equal widths and heights and I have horizontal/vertical spacing like this:
The result on multiple devices is like this:
For the example I gave the buttons a background color, but in fact they will be filled with images of all kinds of sizes. I have image assets (#1x/#2x/#3x) for the buttons. For example, the #2x image assets will never be bigger then 100width and 75height (points).
Some of the button images can be changed by the user. So I want the button size to be ‘independent’ of the images inside. The buttons should NOT resize based on the image ‘inside’.
So I want to first(auto)layout the buttons, without the images ‘filling’ the buttons, so that the buttons will have the optimum size for the biggest sized images in my project (for the #2x image assets as mentioned:100width&75height points).
When I autolayout the buttons, and afterwards fill them with the images, xcode ‘wants’ to let me update the frames because of misplaced views. I do NOT want the images affecting the autolayout.
If I just run the app on the simulator or on a real iphone, it runs fine. But I have all these misplaced views warnings. What should I do? How will I 'tell' Xcode(7.1) that the images should NOT affect the width and heights of the buttons?
I have the feeling I have a slightly wrong approach to this (auto)layout problem, but I can not yet put my finger on it. I think I make some kind of logical error.
Is it good practice to first (auto)layout the buttons and then ‘fill’ with the images? Or should I fill the buttons with the images and then (auto)layout?
Here an example with images which are of equal widths and heights:
Regarding the earlier question about conflicting constraint:
So I want these buttons to have equal width and heights: ideally 100width and 75height in points. The width will be alright, but the heights is somewhat difficult when I am using autolayout for different sized viewcontrollers (with a scrollview on it). I want to use additional constraints that 'says'; the buttons should NOT have a height value lower then 75 points. If I use a fixed height constraint, with 'equal or higher then' 75 points I get a conflict with the equal heights constraints (off course). I guess I should work with priorities, but I tried, and I did NOT succeed yet with it.
How should I proceed?
Help is much appreciated!
Question 1:
Of course you're getting a conflict when setting two height constraints of which one computes to 1/3 of the screen width (≈ 107px excluding padding on the iPhone 4) and the other enforces a minimum height that is bigger than this value (e.g. 120px).
Fortunately there's a way out of this using priorities:
Set the fixed height constraint's priority to a value below the priority of your 'greater or equal' constraint. This way autolayout will choose the fixed height constraint only if its constant is greater or equal to the constant of your 'greater or equal' constraint. Otherwise the constant of your 'greater or equal' constraint will be used as the view's height. Makes sense, right? :)
Question 2:
You can achieve the desired behavior as described in your second question by enforcing a fixed height and width for your image view and setting its content mode to "Aspect Fit". In Interface Builder there's a drop down menu for that:
In your case the best way to give the image view fixed dimensions is to pin all four sides to its superview (because the superview has a well-defined size):
So when you apply these leading, trailing, top and bottom constraints to all your image views the images will automatically resized according to the constraints and the result will look like this:

Having troubles fitting the UIImageView and Label into Scroll View

I want to fit the Image + Label into Scrollable View.
It looks like so My setup
So trouble here is that width of the picture and the label did not match the screen width.
I tried to change the UIImageView width from 400 to 300 or so, BUT those changes either wont take the effect(if above 300), OR those changes make label disappear (with width setted up on 300 or below). Observe.
Funny thing is - label actually appears when you see view hierarchy, even though it looks kinda distorted. But no label on the iOS screen for you, sir.
I also tried to set equal width and height from the superview, but this option just disabling scroll, making view strictly fit the screen (label appears to be cropped)
I want the label to be nice and scrollable, but I also want to see it on the screen. Too much to ask?
EDIT: I just tried removing Auto-layout and got the same as with auto-layout. (click "Observe" link to take a look again, exact same situation) Could somebody explain why?
This is quite a simple fix. For the image, you should use equal width & center horizontally; however, make sure you use a fixed width. For the image to look nice, play with the scale modes - I find Aspect Fit/Fill work best.
The label should also be equal width & centered horizontally. You may need to calculate the height of the label programmatically if the text isn't always going to be the same size.
So the answer to my particular question was - shortening the actual text that is passed to the label.
Apparently UIScrollView can't handle stuff with crazy abnormal heights (I was passing a huuuge text to it) So in my particular situation solution was - shorten the text. Now it works like a charm (even though I still need to play a little with constraints to get rid of the warnings and stuff)

NSTextField weird left margins

I've been looking for a solution for this one all day.
I have 4 NSTextFields (actually subclassed for a few custom operations), which all share the same X position.
The problem is, some have different styles (light, regular, bold) and might have different sizes.
What happens is that, even though the X origin is the same, the 1st letter always has a bit of (consistently different) left margins.
Please see pic: https://dl.dropbox.com/u/1977230/Screen%20Shot%202012-12-11%20at%2017.55.58.png
I want to make sure that all lines start exactly at the same point, say 100px from the left.
Any idea how to override that weird padding?
Cheers
The margin you're talking about I'm pretty sure is the lineFragmentPadding on the NSTextContainer that is used by the NSTextField.
See the NSTextContainer reference:
http://developer.apple.com/library/Mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSTextContainer_Class/Reference/Reference.html
And here's a page from the tutorial on Text Layout:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/TextLayout/Concepts/CalcTextLayout.html
It states in that article:
The typesetter makes one final adjustment when it actually fits text
into the rectangle. This adjustment is a small amount fixed by the
NSTextContainer object, called the line fragment padding, which
defines the portion on each end of the line fragment rectangle left
blank. Text is inset within the line fragment rectangle by this amount
(the rectangle itself is unaffected). Padding allows for small-scale
adjustment of the text container’s region at the edges and around any
holes and keeps text from directly abutting any other graphics
displayed near the region. You can change the padding from its default
value with the setLineFragmentPadding: method. Note that line fragment
padding isn’t a suitable means for expressing margins; you should set
the NSTextView object’s position and size for document margins or the
paragraph margin attributes for text margins.
Unfortunately, it looks like NSTextField's NSTextContainer and NSLayoutManager are private and inaccessible, but it appears they are accessible in an NSTextView:
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSTextView_Class/Reference/Reference.html#//apple_ref/occ/cl/NSTextView
So that may be the class you need to subclass if you want to have minute control over this kind of functionality.
Have you looked into CoreText? I think it may provide the facilities to do what you're looking for. From the docs...
The Core Text layout engine is designed specifically to make simple text layout operations easy to do and to avoid side effects.
You are able to access "font metrics," which enable you to (from the docs)...
For every font, glyph designers provide a set of measurements, called metrics, which describe the spacing around each glyph in the font. The typesetter uses these metrics to determine glyph placement. Font metrics are parameters such as ascent, descent, leading, cap height, x-height, and so on.
EDIT:
It just may be that NSTextField was not designed for what you are trying to do. NSTextField does custom layout apart from a NSLayoutManager.
You may need to upgrade to a NSTextView, which always has a dedicated NSLayoutManager attached. Apple has some example projects you could search for using NSLayoutManager and NSTextView.
If you're using NSTextField to draw simple static text, take a look at AppKit additions to NSString. Use sizeWithAttributes: to get size of the "text" image. Then use the size to calculate rects for drawing. Finally use one of draw methods to actually draw text. Don't forget to "round" result of sizeWithAttributes! It's not pixel aligned.
But if you need to draw something more complex than simple label, use Core Text. You can find very good example of how to use it in twui source code.

How to Know the maximum font size allowed for a given font name?

Is there any way to know the maximum size a font allowed.
I am doing
textInputTextView.font = [UIFont fontWithName:currentFontName size:doubleFontSize];
frame.size = textInputTextView.contentSize;
For different fonts I am getting wrong contentSize when doubleFontSize is more than 70. For some fonts I get wrong contentSize when doubleFontSize variable is only 40.
I am guessing textView.font = doubleSizeFont is too big for those particular fonts. All are ok when font size is small between 10 to 30.
Is there any way to know the maximum size allowed by a particular font?
Details:
I need an image from UITextView. I am using UIGraphicsGetImageFromCurrentImageContext image is producing ok. But I realized image quality is low. So I tried to make the TextView frame and font double.
When I Write in big font "I am a good boy" if good is the right most word. I get "I am a boy" the right most word good disappears I get some blank space on right side of the image. How tragic :)
You Can Calculate height/Width of the content by using the following code.Then resize your frame. Here my Width is fixed (290) and Calculating the height.
CGSize strSize= [DataToBeDisplayed sizeWithFont:[UIFont systemFontOfSize:17.0] constrainedToSize:CGSizeMake(290.0,99999.0)];
frame.size.height = strSize.height
I believe the issue that you are having is because you are using an UITextView. Text views inherits from the UIScrollView so it would not decrease the size of your font, it would only adapt its contents size and make your text scrollable.
Use an UITextField instead which also inherits from the UIView (thus can be used as an image) but allows for greater control. Fix its size to the maximum you would like it to be on screen and then working with both the [mylabel adjustsFontSizeToFitWidth:Y/N] and the [mylabel setminimumfontsize:somefontsize] methods, constrain it if too much text is entered. This will yield you a crisp big font and dynamically fit it upon input
~/End of Line

UILabel with no whitespace above or below text

I have this UILabel in an app where the bottom of the text needs to (apppear to) rest on the edge of another uiview. The label also gets scaled by arbitrary amounts. The problem is that applying a scale to the label also scales the whitespace below the text in the label. So for instance scaling by 2.0f makes the whitespace twice as big, pushing the text farther away from the edge.
Is there a simple way of making a label perfectly fit the text's size so that the bottom pixel of the text is at the very bottom of the label view?
Let me present you, the most useful method for these situations:
[myString sizeWithFont: ....];
This method (and its multiple variations) return the size that a NSString instance will use, therefore, you can scale the UILabel to your needs. iOS lacks a "Vertical Alignment" option.
Here is a similar stackoverflow question, if you're still in doubt.