Why does a NSTextField's field editor inherit the text color of it's target field when it's configured otherwise? - swift

My NSTextField's textColor is white and non-editable. When I click on the label, I set the NSTextField to isEditable = true. Performing a second click on the label places it into edit mode. I get a field editor etc.
Prior or editing it looks like this:
I am subclassing my NSTextField to configure that field editor like so:
override func becomeFirstResponder() -> Bool {
let value = super.becomeFirstResponder()
if value == false {
return value
}
if let fieldEditor = currentEditor() as? NSTextView {
fieldEditor.backgroundColor = NSColor.white
fieldEditor.drawsBackground = true
fieldEditor.textColor = NSColor.black
}
return value
}
You can see from the code above I am setting the textColor of the field editor to black. When I enter editing mode I get the expected behavior:
So far so good. Enter the issue: Any new keystrokes are reflected as white text:
In the sample below I have highlighted the contents of the field to show the white text:
So it would seem that even though I am setting the fieldEditor.textColor = NSColor.black and I see that black color, the field editor is inheriting the NSTextField's text color for new input?
If, in my editor configuration I do this, keep in mind this is in a NSTextField subclass:
fieldEditor.backgroundColor = NSColor.white
fieldEditor.drawsBackground = true
fieldEditor.textColor = NSColor.black
textColor = NSColor.black // <-------
Then text input while in editing mode does come though as black. I understand the field editor is a proxy to the NSTextField:
https://developer.apple.com/library/content/documentation/TextFonts/Conceptual/CocoaTextArchitecture/TextEditing/TextEditing.html#//apple_ref/doc/uid/TP40009459-CH3-SW29
So is the flow:
field editor shows
becomes first responder
has some kind of view for the first render
sends keystrokes to the underlying NSTextField
updates itself using the NSTextField settings?

Related

Swiftui: How to change the text and color of textfield keyboard return button

when working with Texfield in swiftui, Is it possible to customize the return button on keyboard that is activated when a user tap on the textfield. I want to change the color and have it say "Search". Here is a textfield code I'm using
TextField("Search", text: $searchText, onCommit: show)
.foregroundColor(.primary)
how can I customize the keyboard return button?
You can only customize the title and color of the return key, if you don't implement your own customized keyboard(which is a lot work).
For the title, you can customize it by:
textField.returnKeyType= .search
For the color, there are two options: blue (enabled) and gray (disabled). And you can customized it in a limited way:
textField.enablesReturnKeyAutomatically
The default value for this property is false. If you set it to true, the keyboard disables the Return key when the text entry area contains no text. As soon as the user enters some text, the Return key is automatically enabled.
In SwiftUI, unfortunately, there isn't any modifiers for these things currently. You might have to do it by wrapping UITextField yourself with UIViewRepresentalbe. The implementation is something like this:
struct SearchTextField: UIViewRepresentable {
#Binding var searchText: String
func makeUIView(context: Context) -> UITextField {
let textField = UITextField()
textField.placeholder = "Search"
textField.returnKeyType = .google // or .search, .yahoo, etc.
textField.enablesReturnKeyAutomatically = true
textField.keyboardType = .webSearch
// ... set other preferrences
return textField
}
func updateUIView(_ textField: UITextField, context: Context) {
//
}
// ...
}
For a more detailed implementation, check out this good SO answer.
1) Set the text "Search" to the Return key:
textField.returnKeyType= .search
As the Apple Documentations states: "Specifies that the visible title of the Return key is “Search”".
2) Change color of the Return key
There is no public API available for it.
You can do it in a hacky way with private API but you get no guarantee that in the future it will work and even then your app will be get rejected in app store review.
🔴 Set the text "done" to the Return key:
Form {
TextField("Username", $viewModel.username)
.submitLabel(.done)
}
For Change the keyboard "Done" button color to see this link :
https://gist.github.com/feighter09/d87dccf5bc2a3d7db0dd
Happy coding :)

How to make a NSTextView, that is added programmatically, active?

I am making an app where a user can click anywhere on the window and a NSTextView is added at the mouse location. I have got it working with the below code but I am not able to make it active (in focus) after adding it to the view (parent view). I have to click on the NSTextView to make it active but this is not what I want. I want it to automatically become active when its added to the parent view.
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)
}
MyTextView class looks like below:
class MyTextView: NSTextView {
override var shouldDrawInsertionPoint: Bool {
true
}
override var canBecomeKeyView: Bool {
true
}
override func viewWillDraw() {
isHorizontallyResizable = true
isVerticallyResizable = true
insertionPointColor = .red
drawsBackground = false
isRichText = false
allowsUndo = true
font = NSFont.systemFont(ofSize: 40.0)
}
}
Also, I want it to lose focus (become inactive) when some other elements (view) are clicked. Right now, once a NSTextView becomes active, it stays active no matter what other elements I click except when I click on an empty space to create yet another NSTextView.
I have gone through the Apple docs multiple times but I think I am missing something. Any help would be much appreciated.
Get the NSWindow instance of the NSViewController's view and call makeFirstResponder passing the text view as parameter.
To lose focus call makeFirstResponder passing nil.

How can I set title color for a disabled NSButton?(OS X)

I know that we can set a colored title for a button using attributed strings like this:
let style = NSMutableParagraphStyle()
style.alignment = .Center
button.attributedTitle = NSAttributedString(string: "title",
attributes: [ NSForegroundColorAttributeName : NSColor.redColor(),
NSParagraphStyleAttributeName : style])
In fact that's how I'm getting the red title to begin with, but it has no effect on the title color in the disabled mode (it is always gray); I guess in order to set that, I have to create an instance of the NSButton class and override some of its methods related to title color or override some of its properties to disable user interactions(when necessary) instead of disabling the button, but I don't know how.
There are some possible objective-C answers to this question here, here and here.
Create a NSButtonCell Subclass and assign it to the CELL (not the button) in IB.
Then implement
func drawTitle(_ title: NSAttributedString,
withFrame frame: NSRect,
in controlView: NSView) -> NSRect
and return different attributedString for isEnabled or !isEnabled.
You might also set the initial attributedTitle in
init (id)
EDIT: Only works if setWantsLayers is false
Do you need it to be disabled by the definition of it not doing anything when touched?
If so, just have a flag in the button clicked function. So then, the button color could be red (example) when disabled but you can click it but nothing happens. Then if it it's green (example) then it'll allow it to do something.
Your flag could be the color of the button checked through an if statement or set an actual variable.

Changing editability of NSTextfield and multi line text

I have an NSTextField that is used as either a label or an editable text field depending on whether the application is in edit mode or in view mode.
The code to change the editability of the text field is:
var editable : Bool = false {
didSet {
if editable {
titleTextField?.editable = true
titleTextField?.bezeled = true
titleTextField?.drawsBackground = true
titleTextField?.cell?.usesSingleLineMode = false
titleTextField?.cell?.wraps = true
editButton?.state = NSOnState
} else {
titleTextField?.editable = false
titleTextField?.bezeled = false
titleTextField?.drawsBackground = false
titleTextField?.cell?.usesSingleLineMode = false
titleTextField?.cell?.wraps = true
editButton?.state = NSOffState
}
}
}
#IBAction func editButtonPushed(sender : NSButton) {
if sender.state == NSOnState {
self.editable = true
}else{
self.editable = false
}
}
I've defined the NSTextField in Interface Builder as editable and 'Uses Single Line Mode' is not selected. The text field is in a custom view which is the right view in a NSSplitView. The custom view also contains a button with which the user can toggle the edit mode (it is connected to the action in the above code).
When the title in the text field is long (i.e. more than one line) the title is displayed correctly in the editable text field. When I click the Edit button (turning of Edit mode) the text field is made non-editable (the else statement above).
The problem is that in non-edit mode the NSTextField (and the parent custom view) resizes so that the full title fits into the NSTextField (now a label) on ONE LINE. I want the text field to remain the same size and use two lines to display the title.
How can I prevent the resizing of the text field? I've tried using titleTextField?.cell?.usesSingleLineMode = false and titleTextField?.cell?.wraps = true, but neither solution works.

UINavigationBar titleTextAttributes does not update fully after reloading views

This is a tricky one.
Here is my storyboard for this demo:
The Settings screen segues to the My Color screen where users can choose either a dark or light color scheme for the app. When a change is made, I remove all views from the window and then re-add them to force the current view to immediately apply the changes via the UIAppearance proxy. So the color of the navigation bar and the nav bar's text color both change immediately.
Next, the user unwinds the segue to return to the Settings screen. On the Settings screen, the new color of the navigation bar is already applied. The new color of the nav bar's text is also already applied. However, for a brief instant while the segue is in transition, the nav bar still shows the old text color. The new text color is not shown until after the transition is complete. This results in a minor, but noticable, visual glitch as the nav bar's text suddenly changes from the old color to the new color.
To update the color of the nav bar text when the user flips the switch, I run the following code in the My Color screen's view controller. (The full project code up on Github at https://github.com/prinomen/social_demo2).
func switchValueDidChange(sender:UISwitch!) {
if (sender.on == true) {
colorIndex = 1 // nav bar is now black
UINavigationBar.appearance().barTintColor = black // set appearance proxy to the new color
// Run this switch to set the textColor global var to match the preferred color scheme, based on the value of colorIndex.
switch colorIndex {
case 0: // white
textColor = green
statusBarTextIsBlack = true
case 1: // black
textColor = red
statusBarTextIsBlack = false
default:
break;
}
// Update these appearance proxy items (they need the window to reload before they will manifest their changes).
UINavigationBar.appearance().titleTextAttributes = [NSFontAttributeName: UIFont(name: "Avenir-Medium", size: 22)!, NSForegroundColorAttributeName: textColor]
UINavigationBar.appearance().tintColor = textColor
// Remove all views from the window and then re-add them in order to force the current view to immediately apply changes to UIAppearance.
let windows : NSArray = UIApplication.sharedApplication().windows
for window in windows as! [UIWindow] {
for view in window.subviews {
view.removeFromSuperview()
window.addSubview(view)
}
}
} else {
colorIndex = 0 // nav bar is now white
UINavigationBar.appearance().barTintColor = white // set appearance proxy to the preferred color
// Run this switch to set the textColor global var to match the preferred color scheme, based on the value of colorIndex.
switch colorIndex {
case 0: // white
textColor = green
statusBarTextIsBlack = true
case 1: // black
textColor = red
statusBarTextIsBlack = false
default:
break;
}
// Update these appearance proxy items (they need the window to reload before they will manifest their changes).
UINavigationBar.appearance().titleTextAttributes = [NSFontAttributeName: UIFont(name: "Avenir-Medium", size: 22)!, NSForegroundColorAttributeName: textColor]
UINavigationBar.appearance().tintColor = textColor
// Remove all views from the window and then re-add them in order to force the current view to immediately apply changes to UIAppearance.
let windows : NSArray = UIApplication.sharedApplication().windows
for window in windows as! [UIWindow] {
for view in window.subviews {
view.removeFromSuperview()
window.addSubview(view)
}
}
}
}
Aside from changing the color via the appearance proxy, I've also tried setting the color explicitly within the viewWillAppear and viewWillLayoutSubviews methods of the Settings screen view controller by running this line:
self.navigationController?.navigationBar.titleTextAttributes = [NSFontAttributeName: UIFont(name: "Avenir-Medium", size: 22)!, NSForegroundColorAttributeName: textColor]
But this results in the same issue. What I find confusing is that the other changes made via the appearance proxy are updated without encountering this issue. Only the titleTextAttributes property is troubled by this issue.
I thought that maybe iOS makes some kind of "snapshot" of the Settings screen when segueing to the My Color screen. Then when the segue is reversed, the "snapshot" with the old nav bar text color is used and the new color is not updated until the segue is finished. But if that were true, then why doesn't the navigation bar's barTintColor also experience the same problem? There must be a different way the reverse segue is handled, but I can't seem to figure it out.
Is there a way to apply the color change to the title text before the transition happens, in a way that affects the transition itself?
Thanks for any insight!