How do you enlarge the search icon on an NSSearchField? - swift

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
}
}
...

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)
}
}

How to resize a NSTextView automatically as per its content?

I am making an app where a user can click anywhere on the window and a NSTextView is added programmatically at the mouse location. I have got it working with the below code but I want this NSTextView to horizontally expand until it reaches the edge of the screen and then grow vertically. It currently has a fixed width and when I add more characters, the text view grows vertically (as expected) but I also want it to grow horizontally. How can I achieve this?
I have tried setting isHorizontallyResizable and isVerticallyResizable to true but this doesn't work. After researching for a while, I came across this https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/TextStorageLayer/Tasks/TrackingSize.html but this didn't work for me either.
Code in my ViewController to add the NSTextView to its view:
private func addText(at point: NSPoint) {
let textView = MyTextView(frame: NSRect(origin: point, size: CGSize(width: 150.0, height: 40.0)))
view.addSubview(textView)
}
And, MyTextView class looks like below:
class MyTextView: NSTextView {
override func viewWillDraw() {
isHorizontallyResizable = true
isVerticallyResizable = true
isRichText = false
}
}
I have also seen this answer https://stackoverflow.com/a/54228147/1385441 but I am not fully sure how to implement it. I have added this code snippet in MyTextView and used it like:
override func didChangeText() {
frame.size = contentSize
}
However, I think I am using it incorrectly. Ergo, any help would be much appreciated.
I'm a bit puzzled, because you're adding NSTextView to a NSView which is part of the NSViewController and then you're talking about the screen width. Is this part of your Presentify - Screen Annotation application? If yes, you have a full screen overlay window and you can get the size from it (or from the view controller's view).
view.bounds.size // view controller's view size
view.window?.frame.size // window size
If not and you really need to know the screen size, check the NSWindow & NSScreen.
view.window?.screen?.frame.size // screen size
Growing NSTextView
There's no any window/view controller's view resizing behavior specified.
import Cocoa
class BorderedTextView: NSTextView {
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
let path = NSBezierPath(rect: bounds)
NSColor.red.setStroke()
path.stroke()
}
}
class ViewController: NSViewController {
override func mouseUp(with event: NSEvent) {
// Convert point to the view coordinates
let point = view.convert(event.locationInWindow, from: nil)
// Initial size
let size = CGSize(width: 100, height: 25)
// Maximum text view width
let maxWidth = view.bounds.size.width - point.x // <----
let textView = BorderedTextView(frame: NSRect(origin: point, size: size))
textView.insertionPointColor = .orange
textView.drawsBackground = false
textView.textColor = .white
textView.isRichText = false
textView.allowsUndo = false
textView.font = NSFont.systemFont(ofSize: 20.0)
textView.isVerticallyResizable = true
textView.isHorizontallyResizable = true
textView.textContainer?.widthTracksTextView = false
textView.textContainer?.heightTracksTextView = false
textView.textContainer?.size.width = maxWidth // <----
textView.maxSize = NSSize(width: maxWidth, height: 10000) // <----
view.addSubview(textView)
view.window?.makeFirstResponder(textView)
}
}
I finally got it to work (except for one minor thing). The link from Apple was the key here but they haven't described the code completely, unfortunately.
The below code work for me:
class MyTextView: NSTextView {
override func viewWillDraw() {
// for making the text view expand horizontally
textContainer?.heightTracksTextView = false
textContainer?.widthTracksTextView = false
textContainer?.size.width = 10000.0
maxSize = NSSize(width: 10000.0, height: 10000.0)
isHorizontallyResizable = true
isVerticallyResizable = true
isRichText = false
}
}
That one minor thing which I haven't been able to figure out yet is to limit expanding horizontally until the edge of the screen is reached. Right now it keeps on expanding even beyond the screen width and, in turn, the text is hidden after the screen width.
I think if I can somehow get the screen window width then I can replace 10000.0 with the screen width (minus the distance of text view from left edge) and I can limit the horizontal expansion until the edge of the screen. Having said that, keeping it 10000.0 won't impact performance as described in the Apple docs.

How to adjust vertically single line string in NSTextField?

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)

Why doesn't overriding `intrinsicContentSize` in `NSView` work for `NSScrollView`?

I have placed an instance of custom class BigView that is a subclass of NSView inside a NSScrollView in IB. The content size of my BigView will be computed at runtime. What is the best practices for setting the content size?
Overriding intrinsicContentSize, as suggested in the The Big Nerd Ranch guide, does not seem to work -- the frame remains at its original size:
class BigView: NSView {
...
override var intrinsicContentSize: NSSize { // doesn't work?!
return NSSize(width: 10000, height: 10000)
}
...
}
Setting the frame programmatically (or in IB) does work:
class BigView: NSView {
...
override func awakeFromNib() {
...
self.frame = NSRect(x: 0, y: 0, width: 10000, height: 10000)
}
...
}I
or from the controller:
class ViewController: NSViewController {
#IBOutlet weak var bigView: BigView!
...
override func viewDidLoad() {
super.viewDidLoad()
bigView.frame = NSRect(x: 0, y: 0, width: 1000, height: 1000)
}
...
}
This can also be done throughout the scrollview's documentView property:
class ViewController: NSViewController {
#IBOutlet weak var scrollView: NSScrollView!
...
override func viewDidLoad() {
super.viewDidLoad()
scrollView.documentView?.frame = NSRect(x: 0, y: 0,
width: 1000, height: 1000)
}
...
}
I suppose one could also use AutoLayout constraints as well.
What doesn't overriding intrinsicContentSize work?
The problem is that when translatesAutoresizingMaskIntoConstraints is enabled in newer versions of AppKit default constraints are set without considering the intrinsic size.
To fix this you need to add the constraints manually. First pin the position of your BigView to the top left of the superview (Should be the NSClipView). Then go to the Size Inspector, and in the Content Compression Resistance Priority section set Intrinsic Size to Placeholder. That gives your view the constraints it needs and everything should work at runtime.

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)
}
}