How to adjust vertically single line string in NSTextField? - swift

I am using custom font for non-editable NSTextField, which I created in StoryBoard:
generalDisplay.font = NSFont(name: "DS-Digital Bold", size: 25.0)
Then I am adjusting frame height:
generalDisplay.frame.size.height = 28
The result is not centered vertically:
I've tried to turn off single line mode, but the result is even worse.
If I should subclass it, could you give me an example what methods I have to override?

You can subclass UITextField and override this method:
override func textRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 10))
}

Looking at the Apple documentation for NSTextfield maybe you could initialize your NSTextField with a NSAttributedString to be able to change the baseline
NSTextfield(labelWithAttributedString: NSAttributedString)

Related

How to vertically align text of NSSecureTextField in swift for macOS [duplicate]

I have been reading through the various options on how to set the vertical alignment on an NSTextField. I want the text to be displayed in the center and to do it programatically in Swift. Here are the things I have looked so far:
http://www.cocoabuilder.com/archive/cocoa/174994-repositioning-an-nstextfieldcell.html
https://red-sweater.com/blog/148/what-a-difference-a-cell-makes
Vertically Centre Text in NSSecureTextField with subclassing
Get NSTextField contents to scale
vertically align text in a CATextLayer?
One thing I have tried in Swift is to set the following property:
textField.usesSingleLineMode = true
Any tips on the best way to vertically center text would be much appreciated!
This is very hard to do, as Apple makes this very difficult. I achieved it by subclassing NSTextFieldCell and overriding the drawingRectForBounds: method like so:
override func drawingRectForBounds(theRect: NSRect) -> NSRect {
let newRect = NSRect(x: 0, y: (theRect.size.height - 22) / 2, width: theRect.size.width, height: 22)
return super.drawingRectForBounds(newRect)
}
This is just my way to do it, I'm sure there are better ways, which I don't know (yet). And this only works for the standard font size in TextFields (which gives a text height of 22). That's why I hardcoded that. Haven't figured out yet, how to get the height in the cell if you change the font.
Result:
Try this on a playground, it centers the text perfectly, use it on your projects! Hope it helps!
import Cocoa
let cell = NSTableCellView()
cell.frame = NSRect(x: 0, y: 0, width: 100, height: 100)
let tf = NSTextField()
tf.frame = cell.frame
tf.stringValue = "MyTextfield"
tf.alignment = .Center
let stringHeight: CGFloat = tf.attributedStringValue.size().height
let frame = tf.frame
var titleRect: NSRect = tf.cell!.titleRectForBounds(frame)
titleRect.size.height = stringHeight + ( stringHeight - (tf.font!.ascender + tf.font!.descender ) )
titleRect.origin.y = frame.size.height / 2 - tf.lastBaselineOffsetFromBottom - tf.font!.xHeight / 2
tf.frame = titleRect
cell.addSubview(tf)
I have added the NSTextField inside a NSView and centered it.
Another solution was (in an iOS project) to create a UILabel and allow it adjust its size (sizeToFit()) and again embed it inside a UIView.
I personally don't like the calculations in previous answers and the second solution for iOS works for all texts size and row numbers.
I was also facing vertical alignment issue with NSTextField. My requirement involved, rendering a single-line string inside a NSTextField. Additionally,
textfield needed to be resize implying we had programatically resized the font-point-size of the text inside text-field on resize. In this scenario we faced vertical-alignment issues - the mis-alignment was tough to grasp/understand in a straight forward way.
What finally worked:
So, in my scenario a simple,
turn off the "Single Line Mode" in interface builder
for the text-field solved the issue.
The accepted answer works perfectly and here's the Swift3 version.
class VerticallyAlignedTextFieldCell: NSTextFieldCell {
override func drawingRect(forBounds rect: NSRect) -> NSRect {
let newRect = NSRect(x: 0, y: (rect.size.height - 22) / 2, width: rect.size.width, height: 22)
return super.drawingRect(forBounds: newRect)
}
}

Swift: Update UIViewController insets via extension

I wanted to update the UIEdgeInsets of all my view controllers on certain devices and I wanted to see if there's a way to do it globally as an extension, rather than creating a method and calling it in viewDidLoad for each of them. Is there a way to achieve this? I tried using awakeFromNib but this doesn't work.
extension UIViewController {
open override func awakeFromNib() {
self.additionalSafeAreaInsets = UIEdgeInsets(top: 100, left: 100, bottom: 100, right: 100)
}
}
I also tried calling self.viewLayoutMarginsDidChange() after changing the edge insets, with no results.
Any suggestions would be appreciated. Am I just overriding the wrong method or is this just not possible, or as easy as I'm thinking.
extension UIViewController {
open override func awakeAfter(using: NSCoder) -> Any? {
self.additionalSafeAreaInsets = UIEdgeInsets(top: 100, left: 100, bottom: 100, right: 100)
return super.awakeAfter(using: using)
}
}

How do you enlarge the search icon on an NSSearchField?

I'm making a search field with a larger height than the default. Although its text size can be increased to the proper size, the icon stays the same size:
I tried to fix this by overriding NSSearchField's rectForSearchButton(whenCentered:)'s default rectangle. However, not only did it not increase the size of the button, but it also duplicated the button image.
override func rectForSearchButton(whenCentered isCentered: Bool) -> NSRect {
return NSRect(x: 10, y: 10, width: 30, height: 30)
}
I then tried commenting out the above and subclassing NSSearchFieldCell, which had a similar result.
override func searchButtonRect(forBounds rect: NSRect) -> NSRect {
return NSRect(x: 10, y: 10, width: 30, height: 30)
}
How can I properly center and enlarge this icon? I want it to fill up the whole space and be centered, or at least look like a normal search field.
You need to change button image scaling. Assuming your custom class is instantiated from Storyboard/XIB and the height is modified via constraint, it could be like below:
class CustomSearchFiled: NSSearchField {
override func awakeFromNib() {
if let cell = self.cell as? NSSearchFieldCell {
cell.searchButtonCell?.imageScaling = .scaleProportionallyUpOrDown
}
}
...

Vertically aligning text in an NSTextField using Swift

I have been reading through the various options on how to set the vertical alignment on an NSTextField. I want the text to be displayed in the center and to do it programatically in Swift. Here are the things I have looked so far:
http://www.cocoabuilder.com/archive/cocoa/174994-repositioning-an-nstextfieldcell.html
https://red-sweater.com/blog/148/what-a-difference-a-cell-makes
Vertically Centre Text in NSSecureTextField with subclassing
Get NSTextField contents to scale
vertically align text in a CATextLayer?
One thing I have tried in Swift is to set the following property:
textField.usesSingleLineMode = true
Any tips on the best way to vertically center text would be much appreciated!
This is very hard to do, as Apple makes this very difficult. I achieved it by subclassing NSTextFieldCell and overriding the drawingRectForBounds: method like so:
override func drawingRectForBounds(theRect: NSRect) -> NSRect {
let newRect = NSRect(x: 0, y: (theRect.size.height - 22) / 2, width: theRect.size.width, height: 22)
return super.drawingRectForBounds(newRect)
}
This is just my way to do it, I'm sure there are better ways, which I don't know (yet). And this only works for the standard font size in TextFields (which gives a text height of 22). That's why I hardcoded that. Haven't figured out yet, how to get the height in the cell if you change the font.
Result:
Try this on a playground, it centers the text perfectly, use it on your projects! Hope it helps!
import Cocoa
let cell = NSTableCellView()
cell.frame = NSRect(x: 0, y: 0, width: 100, height: 100)
let tf = NSTextField()
tf.frame = cell.frame
tf.stringValue = "MyTextfield"
tf.alignment = .Center
let stringHeight: CGFloat = tf.attributedStringValue.size().height
let frame = tf.frame
var titleRect: NSRect = tf.cell!.titleRectForBounds(frame)
titleRect.size.height = stringHeight + ( stringHeight - (tf.font!.ascender + tf.font!.descender ) )
titleRect.origin.y = frame.size.height / 2 - tf.lastBaselineOffsetFromBottom - tf.font!.xHeight / 2
tf.frame = titleRect
cell.addSubview(tf)
I have added the NSTextField inside a NSView and centered it.
Another solution was (in an iOS project) to create a UILabel and allow it adjust its size (sizeToFit()) and again embed it inside a UIView.
I personally don't like the calculations in previous answers and the second solution for iOS works for all texts size and row numbers.
I was also facing vertical alignment issue with NSTextField. My requirement involved, rendering a single-line string inside a NSTextField. Additionally,
textfield needed to be resize implying we had programatically resized the font-point-size of the text inside text-field on resize. In this scenario we faced vertical-alignment issues - the mis-alignment was tough to grasp/understand in a straight forward way.
What finally worked:
So, in my scenario a simple,
turn off the "Single Line Mode" in interface builder
for the text-field solved the issue.
The accepted answer works perfectly and here's the Swift3 version.
class VerticallyAlignedTextFieldCell: NSTextFieldCell {
override func drawingRect(forBounds rect: NSRect) -> NSRect {
let newRect = NSRect(x: 0, y: (rect.size.height - 22) / 2, width: rect.size.width, height: 22)
return super.drawingRect(forBounds: newRect)
}
}

Change the size of UILabel or the font to fit in Accented Character

I have a UILabel with Text inside it called :
self.textLabel;
Now, I noticed that whenever I added an accented letter like Ä Ö Ü, then I get an effect like :
Notice how the top dots on the accented letters are cut off. I want to get the text to fit the label, however the self.textLabel.numberOfLines = 2 constraint which ensures that I have 2 lines and the text after the sentence goes beyond the width is maintained. Essentially, I want a label like :
With the dots maintained. Now, I have tried :
[self.frame sizeToFit] which does not work because it wraps around the entire text. As I said, cut the text out after 2 lines.
Using .bounds and CGRectMake to create new frames and then assign their new height to the current frame, which doesn't work either. Check out https://stackoverflow.com/questions/21948714/adjust-size-of-uilabel-to-fit-height-of-text for more information. Can someone please help me out with this?
Adding top padding to label fixed the issue for me.
#IBDesignable class PaddingLabel: UILabel {
#IBInspectable var topInset: CGFloat = 5.0
#IBInspectable var bottomInset: CGFloat = 0
#IBInspectable var leftInset: CGFloat = 0
#IBInspectable var rightInset: CGFloat = 0
override func drawText(in rect: CGRect) {
let insets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
super.drawText(in: rect.inset(by: insets))
}
override var intrinsicContentSize: CGSize {
let size = super.intrinsicContentSize
return CGSize(width: size.width + leftInset + rightInset,
height: size.height + topInset + bottomInset)
}
override var bounds: CGRect {
didSet {
// ensures this works within stack views if multi-line
preferredMaxLayoutWidth = bounds.width - (leftInset + rightInset)
}
}
}
The answer was to do
self.textLabel.numberOfLines = 2;
And then doing :
[self.textLabel sizeToFit];