I have a UITextField that contains a trailing-edge button to toggle the text field's isSecureEntry value. The logic for toggling isSecureEntry works as expected, but the frame of the text field expands or contracts in height by a ~2.5 points when toggling isSecureEntry.
The two image assets used for the button measure 72x72 points, so I don't think the change is a result of changing the button's image.
We are using a custom font, so I thought that I may need to reset the font after each toggle of isSecureEntry (the text field's caret's height is also affected). The steps that I took follow:
I have subscribed to UITextField.textDidChangeNotifications in order to set the font after isSecureEntry is toggled, assuming that the change of characters would cause the notification to be posted, but that doesn't seem to happen because the breakpoint placed at the selector is not triggered.
I used the same logic to set the text field's font in the button's selector for the touchUpInside event, but that does't resolve the frame change either.
What may be causing the frame change of the text field when isSecureEntry is toggled?
Related
I have a Cocoa app that shows a "quick search" window similar to Spotlight. The window contains a visual effect view and inside a NSTextField. The text field stretches across the full width of the window.
I would like to be able to move the window by dragging inside the empty area of the text field. When dragging across text in the text field, the normal editing (i.e. selection) behavior should be used instead.
In theory, moving a window by its background is easy:
window.isMovableByWindowBackground = true
However, this behavior does not work with NSTextField, because it intercepts dragging and attempts to select text instead.
Spotlight does it somehow. Here's an example:
A couple of options that I considered without success:
Tried overriding hitTest: returning nil
Tried overriding mouseDown|Up|Dragging: and forwarding to superview
Tried to use autolayout to have text field shrink to tightly wrap around its text (could not figure this one out)
For reference, I finally found a way:
Part 1: get NSTextField to grow/shrink with its content
Override intrinsicContentSize and measure its content:
private func measure(_ string:NSAttributedString) -> NSSize
{
let cell = NSTextFieldCell(textCell: stringValue)
cell.attributedStringValue = string
return cell.cellSize
}
Part 2: view setup
Add a placeholder view right after the text field
Set up auto layout to have the placeholder view to grow and shrink
Part3: all about the details
Set up the placeholder view to use the iBeam cursor to make it appear like a text field
If the user clicks in the placeholder view, make the text field the first responder
That's it.
UITextField shows its content text bang against the left edge of the field, which is ugly and can be hard to read if the field has a visible border. UITextView, on the other hand, automatically has padding (blank space) around the text. I examined and experimented with all the UITextField properties but I don't see any way they can be used to get padding around the text.
I also tried calling drawText(in:CGRect) on the text field but it had no effect. But I'm not sure what CGRect is needed in that call. I tried different border styles including .none. I also tried making a blank UIView and assigning that to the text field's leftView property but that also had no effect. And I tried doing these things in viewDidLoad, viewDidAppear, and at the point where I'm writing the text into the textField. The text shows in the textField as expected but none of these other things seem to affect the textField's appearance in any way. It seems to me lots of people must show text in a UITextField and somehow get it padded on the left so I'm misunderstanding something but what???
The following works for a field called "textField". It inserts padding by shifting the left side of the field's content 8 pixels right relative to the left edge of the textField:
textField.layer.sublayerTransform = CATransform3DMakeTranslation(8, 0, 0);
This is given in an answer to a different question by user Dhiru (reputation 1285) in Nov. 2016. Thank you Dhiru!
I have to add that I am not familiar with either sublayerTransform or CATransform3DMakeTranslation so I can't be sure that this doesn't have some side effect that isn't immediately obvious. I'm a little worried that it's called "3D transform" whereas there is nothing 3D about my application. But at least for now this seems to answer the question.
Look at the simple app screenshot below. There is a text field and a segmented control. There are three bindings in play:
textfield value binding goes to NSUserDefaultsController values.TextValue
textfield alignment binding goes NSUserDefaultsController values.Alignment
segmented control selectedIndex binding goes to NSUserDefaultsController values.Alignment
So the segmented control controls the text field's alignment.
Problem:
When the text field has first responder, if you click on the segmented control to change the alignment, the text field 1.) throws out the value being edited and reverts to the last bound value, 2.) does not update its alignment, 3.) remains first responder.
Expected behavior would be: when you change the segmented control value the text field resigns first responder, commits the value in the UI to user defaults, and updates its alignment. How can this be done?
You’ve hit a bug and should report a RADAR. There have been many bugs with textfields and bindings when you’re editing a textfield, it’s not an area the engineers focused on originally.
You don’t even need to bind the textField’s value to replicate this, you can just bind the alignment of the textField.
I'm writing an OS X app and have a problem with font smoothing in separate window.
I have a text field where you put text and suggestion window which pops up with a list of suggestions according to what you wrote. I'm using View-cell based NSTableView to display those suggestions and SFBPopoverWindowController to display it as a "popup" window (tried other classes with the same effect). When rows are first drawn they look fine but after I select them (keyboard or mouse) the font changes it's weight. It's only visual - like you would change smoothing method on the font, not it's bold setting.
"Music note" is the selected cell here
What's even more strange is that after I hide and show the window 3 times everything works fine from that point on.
Again - "Music note" is the selected cell.
The selection is done by overwriting NSTableRowView class and its drawSelectionInRect: method but I tried with drawing everything inside custom NSTableCellView class and it didn't help. The text is standard NSTextField - nothing's changed there.
The SFBPopoverWindow (and it's controller) are created once and reused with styleMask NSBorderlessWindowMask, backing NSBackingStoreBuffered, defer set to YES. The only change in SFBPopoverWindowController I made was to turn off window becoming key window but it doesn't change anything.
It might be related to the way a table view draws it's selected cells (setSelectionHightLightStyle:). Try to set the style to None/ NSTableViewSelectionHighlightStyleNone in your code or IB / Storyboard-file and draw the selection yourself (in a NSTableRowView subclass).
Background: When you use NSTableViewSelectionHighlightStyleRegular or NSTableViewSelectionHighlightStyleSourceList the table view assumes that you use the standard selection behaviour and appearance and does some magic to support that.
==========
UPDATE
==========
My previous answer is still valid but since it only describes the problem and hints at a workaround, I wanted to add a real solution. If you want to use NSTableViewSelectionHighlightStyleRegular for your table view (with custom font and colors), you need a way to 'disable' the system magic that comes into place once your row is highlighted. One proposed solution is to decline the first responder status. It has a lot of drawbacks and isn't a good solution at all.
So, let's have a closer look at the system 'magic' that kicks in as soon as the row will be highlighted: NSTableRowView has a property interiorBackgroundStyle that is – according to the documentation – 'an indication of how the subviews should draw'. Furthermore 'This value is dynamically computed based on the set of properties set for the NSTableRowView. Subclassers can override this value when they draw differently based on the currently displayed properties. This method can also be called to determine what color a subview should use, or alternatively, NSControls can have the -backgroundStyle set on their cell to this value.'
I assume that this style will be handed down the subview hierarchy and causes your text fields to look odd. The system assumes that a highlighted cell has a dark background and changes the interiorBackgroundStyle to dark. Other controls try to adapt accordingly.
I think there are two solutions to this problem:
1) Override interiorBackgroundStyle in your NSTableRowView subclass and return the style that fits your interface (in my case it's .light because my highlight color is a very bright blue). This worked for me.
2) If changing the whole style is a bit too much because you only want certain elements to not change their style, you may only need to adjust these subclasses. I haven't tried this yet.
I have an owner drawn header of a ListView control. If the header is not owner drawn we have a nice hover effect when mouse is on one of the header's columns. After I made header columns owner drawn I have to take care of the hover effect myself.
In order to catch it I monitor WM_MOUSEMOVE message within header window and when I get this message I redraw the column of a header with changed background first and on top of that I draw text and other necessary graphics. There is just one but... The font of a text becomes bold.
You can see it below. First header is mine, and second header is from Windows explorer with hover effect on Date modified column.
Why does the font suddenly becomes bold?
Is this the right way to implement hover effect? Maybe I missed some Header specific notification?
In owner draw mode you have to take care of everything including fonts. Don't assume you know what the current DC font is. If you want a specific font you need to select one into the DC before drawing text.
Well, I solved this mystery :) Even though I still don't know why the font used in DrawThemeText function suddenly changed in WM_MOUSEMOVE notification. My approach still relies on catching WM_MOUSEMOVE event and redrawing the column underneath the mouse pointer. In order not to change the default header font (like in illustration in my question) I used the following code to get and set it:
HFONT displayFont = (HFONT)SendMessage( header, WM_GETFONT, 0, 0 ); // gets default header font
SelectObject(hdc, displayFont); // sets device context to use newly found font
This solution has at least one drawback: I redraw the Header too frequently (each time mouse moves within header column).