I’m attempting to update a Cocoa app for the first time in perhaps 8 years. It seems to build OK, and mostly runs fine too. I can edit text, but the insertion point doesn’t blink.
I use an NSTextView subclass to display text. I’m rusty at Cocoa, so I am guessing something changed with app napping or the like. Is there anything I need to do to make sure insertion points blink? More likely, what did I break to prevent periodic updating in the modern battery-friendly way?
I have the same problem with NSTextView and after multiples tests, it seems that bug happens with every basic textView on macOS 10.14 for me.
I've searched for multiple fixes on the web with no result. The best workaround I've found is to set the textView delegate to itself. Then, on didProcessEditing, call updateInsertionPointStateAndRestartTimer on the main thread as the following :
class CustomTextView: NSTextView {
override func awakeFromNib() {
super.awakeFromNib()
// 1. Set the textStorage delegate
textStorage?.delegate = self
}
}
extension CustomTextView: NSTextStorageDelegate {
func textStorage(_ textStorage: NSTextStorage, didProcessEditing editedMask: NSTextStorageEditActions, range editedRange: NSRange, changeInLength delta: Int) {
// 2. On the main thread, update the insertion point
DispatchQueue.main.async {
self.updateInsertionPointStateAndRestartTimer(true)
}
}
OK, just in case this bites someone else: I had switched to dark mode. Apparently the default insertion point is white, so I couldn’t see it flash against the white background. So I need to properly support dark mode.
Related
I'm working from a storyboard connected to a UIViewController as such:
UPDATE:
The Problem Seems to be that my constraints do not pin to margins. Here are my constraints pinning to the bottom.
Here is how that translates to different devices.
I have messed with the various size settings shown in the second to last picture. I will try a more systematic approach to changing these if someone isn’t able to very easily detect why this is occurring. It seems unrelated to the Page Control and more related to page sizing
Project Github HERE
Note again, this is the exact same problem I get even when creating the view controller in the method used at:
HERE
I chose the code used as opposed to the code at the link only because I had already coded this and they were identical in issue, I have become aware sense then that this is a more complicated way to accomplish this though.
The issue is recreated perfectly with a simple constraint to the bottom.
There are many issues with your code / storyboard, but I think the problem you're complaining about is here:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
for view in self.view.subviews {
if view is UIScrollView {
view.frame = UIScreen.main.bounds // *
} else if view is UIPageControl {
view.backgroundColor = UIColor.clear
}
}
}
Remove the line that I've commented.
(If you also change the color in the last line to .red, just as an experiment, you will be able to see the page control, and you'll see that the text view is behaving consistently on different sizes of device.)
My goal is to make text selection behaviour similar to Apple Pages, MS Word and any other text processor, when NSTextView selects ONLY text glyphs and not any indents or the whole NSTextContainer size like by default NSTextView's behavior.
With that goal in mind, I subclassed NSLayoutManager and overrided func rectArray(forCharacterRange charRange: NSRange, withinSelectedCharacterRange selCharRange: NSRange, in container: NSTextContainer, rectCount: UnsafeMutablePointer<Int>) -> NSRectArray?
In my override I simply calculated new rects that bound just glyphs and rejects any indents, blank spaces etc.
Everything worked fine. But suddenly in macOS 10.11 Apple did deprecate the method I'm using. And suggested to use func enumerateEnclosingRectsForGlyphRange: withinSelectedGlyphRange: inTextContainer: usingBlock: instead.
But it seems that this new method doesn't work in a way the previous deprecated method did. It's impossible to customize enclosing rects any more. All I can do is to enumerate through rects TextKit did calculate. And now it's impossible to achieve desirable text selection behaviour because the rects are read-only. And, by the way, the new method doesn't get called at all by the TextKit. So there's no point for me to think about it.
What should I do?
While the surprise with nonequivalent method replacement remains, I was able to find a solution.
It turned out that I don't need rectArrayForCharacterRange method to do the job. I just overrided func fillBackgroundRectArray(_ rectArray: UnsafePointer<NSRect>, count rectCount: Int, forCharacterRange charRange: NSRange, color: NSColor) and put my rect calculations in there.
Everything works like a charm now!
I'm having trouble with the animation of some subclassed indeterminate NSProgressIndicators. They start and stop animating without any issues. However, if I minimise the window while animating, stopAnimation: / StopAnimation(NSObject sender) is called, which makes sense to save resources if the window is not visible. I assume this is invoked from the cocoa framework itself looking at the stacktrace.
The problem then arises when the window is restored, the animation is not resumed.
I've seen you can use the NSCoding Protocol and can override encodeWithEncoder: / EncodeTo(NSCoder encoder) to save some state, and then use that saved state in initWithCoder: / AppProgressIndicatorBar(NSCoder coder) to resume. But the problem here was that my encodeWithEncoder: / EncodeTo(NSCoder encoder) was never called.
Looking at this SO question and answer, it should be handled automatically if the object needs to be serialized. So I'm not sure why it's not being called.
That same answer says you can do it explicitly with NSKeyedArchiver, but then I would need to listen with NSWindowDelegate to know when the window is minimizing / restoring. In which case, I could just use this, and not use the NSCoding Protocol...
This just feels dirty, and I would imagine this is a very common scenario. So how should / do you resume animation? I'm new to cocoa, coming from a mostly .NET background, and I think this problem is a symptom of my limited cocoa knowledge.
I'm using Xamarin Mac, and have tried to give the Objective-C and C# method signatures. I'll be happy for a solution in either, I'll be able to (hopefully!) convert it to the C# equivalent.
For completeness, here is my current Xamarin Mac subclass using the NSCoder Protocol where EncodeTo is not being called. I'm running OS X 10.11.3 and Xamarin Studio 5.10.2.
[Register("AppProgressIndicatorBar")]
public class AppProgressIndicatorBar : NSProgressIndicator, INSCoding
{
...
public AppProgressIndicatorBar(NSCoder coder) : base(coder)
{
...
}
...
public override void EncodeTo(NSCoder encoder)
{
base.EncodeTo(encoder);
...
}
...
}
You should be able to use the NSWindowWillMiniaturizeNotification, NSWindowDidMiniaturizeNotification and NSWindowDidDeminiaturizeNotification notifications or the windowWillMiniaturize:, windowDidMiniaturize: and windowDidDeminiaturize: Window delegate methods to track the state of your window and restore the state of your progress bar when the window deminiaturises (is that really a word?).
HTH
In OS X 10.10 source lists seem to use the light vibrancy appearance. In the Finder (and in some other third party applications, Things.app for example) the selected item in the source list is indicated by a dark vibrancy appearance. For example, see the Desktop row in the image below.
How can I replicate this behaviour? Do I need to use the delegate methods to specify the table row view,
-outlineView:rowViewForItem:
and attempt custom drawing myself or is there a more straight forward approach? If you make a standard source list UI in Xcode the default highlighting is remain the standard blue rectangle that we have seen in previous version of OS X.
After playing around for a while I found a way to accomplish this.
It turned out that I would get the "Finder highlight" style when using NSTableViewSelectionHighlightStyleSourceList and clicking outside my NSOutlineView. So I figured it would stay that way if you refuse making it first responder.
Simply make your NSOutlineView a subclass and override this method:
-(BOOL)acceptsFirstResponder{
return NO;
}
It works, but with some downsides. For example, using arrow keys in the NSOutlineView will no longer work. I downloaded the Things app, and it does not allow use of arrow keys either, so it's very likely that this is how they are doing it. If anyone finds a better way, please post it.
Here is the Swift equivalent :
func outlineView(_ outlineView: NSOutlineView, rowViewForItem item: Any) -> NSTableRowView? {
return CustomTableRowView(frame: NSZeroRect);
}
And the subclass of NSTableRowView
import Cocoa
class CustomTableRowView: NSTableRowView {
override var isEmphasized: Bool {
set {}
get {
return false;
}
}
}
If you would like to keep arrow keys working, you can subclass NSTableRowView and override the following method:
- (BOOL)isEmphasized
{
return NO;
}
I am not sure, this is "Dark Vibrancy".
I would rather try setting the background color to something like "Alternate Selected Control Text Color"
Have a look at an NSTextField in InterfaceBuilder. there are many "Control Text" colors, which have a special appearance on visual effect views.
and for setting the selection color see this answer (untested):
NSTableview Change the highlight colour
I am making an app that will add sound to keypresses as the user types in an NSTextField. I need to capture keystrokes and know what each individual keypress is (like "d" or "space" or "6"). The app depends on this. There is no other way around it.
Each window is an NSDocument File Owner, and it has a single NSTextField in it, which is where the document data is parsed, and the user will type.
After hours of parsing the Internet for answers and hacking away at code, the four most commonly repeated answers are:
"that is not how things work, here is (irrelevant answer)"
"you are new to Cocoa, that is a bad idea, use control:textView:doCommandSelector:" that doesn't give me individual keys, and some keys need their own unique sound trigger.
"use controlTextDidChange: or textView:shouldChangeTextInRange:replaceString:" controlTextDidChange doesn't give me individual keys, and the second one only works for textViews or UIKit.
People get confused and answer with recommendations for UIKit instead of AppKit, which is iOS-only.
The weird thing is that if I subclass NSTextField, it receives -keyUp. I don't know where -keyDown is going.
So my ultimate question is: can you tell me some kind of step-by-step way to actually capture the keyDown that is sent to NSTextField? Even if it's a hack. Even if it's a terrible idea.
I would love to solve this problem! I am very grateful for your reading.
controlTextDidChange is quite a good solution, but don't forget this 2 important things:
Set the delegate binding of the textField to the object where you define the controlTextDidChange method. Commonly, in document based apps it is the window controller, otherwise your app delegate.
Set the textField's control to "continous" in the attribute inspector section
If you miss those points, you will have no result.
This is a pretty old question, but as I was trying to implement a NSTextField that could react to keyDown so that I could create a hotkey preferences control I found I wanted the answer to this question.
Unfortunately this is a pretty non-standard use and I didn't find any places that had a direct answer, but I've come up with something that works after digging through the documentation (albeit in Swift 4) and I wanted to post it here in case it helps someone else with a non-standard use case.
This is largely based off of the information gleaned from the Cocoa Text Architecture Guide
There are three components to my solution:
Creating your NSWindowController and setting a NSWindowDelegate on your NSWindow:
guard let windowController = storyboard.instanciateController(withIdentifier:NSStoryboard.SceneIdentifier("SomeSceneIdentifier")) as? NSWindowController else {
fatalError("Error creating window controller");
}
if let viewController = windowController.contentViewController as? MyViewController {
windowController.window?.delegate=viewController;
}
Your NSWindowDelegate
class MyViewController: NSViewController, NSWindowDelegate {
// The TextField you want to capture keyDown on
var hotKeyTextField:NSTextField!;
// Your custom TextView which will handle keyDown
var hotKeySelectionFieldEditor:HotKeySelectionTextView = HotKeySelectionTextView();
func windowWillReturnFieldEditor(_ sender: NSWindow, to client: Any?) -> Any? {
// If the client (NSTextField) requesting the field editor is the one you want to capture key events on, return the custom field editor. Otherwise, return nil and get the default field editor.
if let textField = client as? NSTextField, textField.identifier == hotKeyTextField.identifier {
return hotKeySelectionFieldEditor;
}
return nil;
}
}
Your custom TextView where you handle keyDown
class HotKeySelectionTextView: NSTextView {
public override func keyDown(with event: NSEvent) {
// Here you can capture the key presses and perhaps save state or communicate back to the ViewController with a delegate pattern if you prefer.
}
}
I fully admit that this feels like a workaround somewhat, but as I am experimenting with Swift at the moment and not quite up to speed with all of the best practices yet I can't make an authoritative claim as to the "Swift-i-ness" of this solution, only that it does allow a NSTextField to capture keyDown events indirectly while maintaining the rest of the NSTextField functionality.
Try like this if you print nslog you will get individual character record for example you pressd "A" you will get the same in console:-
-(void)controlTextDidChange:(NSNotification*)obj
{
NSLog(#"%#",[yourTextfield stringValue]);
}
Also, not sure this is only your requirement.
Text editing for an NSTextField is handled by an NSTextView provided by the window, called the field editor. See the NSWindow method fieldEditor:forObject: and the NSWindowDelegate method windowWillReturnFieldEditor:toObject:. I suppose you could use one of these to provide your own subclassed NSTextView as the field editor. Or, could you simply use NSTextView instead of NSTextField?