(AppKit) Tab insertion inside of NSTextBlock
Apparently a tab in a text block moves the insertion point to the next text block, which makes sense in a table. Workaround: subclass NSTextView
, override insertTab
and insert a tab.
override func insertTab(_ sender: Any?) {
insertText("\t", replacementRange: selectedRange())
}
How to relayout content of NSTextView so that my tab characters are drawn with width of 4 characters
Douglas Davidson of Apple provided me with the clues to get to the answer via the cocoa-dev@apple.lists.com email list.
I've resolved the problem by updating the updateMyTextViewTextAttributes function like so:
- (void)updateMyTextViewTextAttributes
{
NSMutableParagraphStyle* paragraphStyle = [[myTextView defaultParagraphStyle] mutableCopy];
if (paragraphStyle == nil) {
paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
}
float charWidth = [[myFont screenFontWithRenderingMode:NSFontDefaultRenderingMode] advancementForGlyph:(NSGlyph) ' '].width;
[paragraphStyle setDefaultTabInterval:(charWidth * 4)];
[paragraphStyle setTabStops:[NSArray array]];
[myTextView setDefaultParagraphStyle:paragraphStyle];
NSMutableDictionary* typingAttributes = [[myTextView typingAttributes] mutableCopy];
[typingAttributes setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
[typingAttributes setObject:scriptFont forKey:NSFontAttributeName];
[myTextView setTypingAttributes:typingAttributes];
/** ADDED CODE BELOW **/
NSRange rangeOfChange = NSMakeRange(0, [[myTextView string] length]);
[myTextView shouldChangeTextInRange:rangeOfChange replacementString:nil];
[[myTextView textStorage] setAttributes:typingAttributes range:rangeOfChange];
[myTextView didChangeText];
[paragraphStyle release];
[typingAttributes release];
}
recognizing key event in entire window
Step 1 is to set your View Controller to be a delegate of NSTextField. If you use nibs or storyboard it will look like this:
Basically ctrl drag to view controller and set delegate.
Then you can react on text changed in your view controller:
import Cocoa
class ViewController: NSViewController {
override func controlTextDidChange(obj: NSNotification) {
guard let textField = obj.object as? NSTextField else { return }
if textField.stringValue.characters.last == " " {
print(textField.stringValue.componentsSeparatedByString(" "))
}
}
}
It will print out array of words which were separated by space:
["a", ""]
["a", "boy", ""]
["a", "boy", "came", ""]
["a", "boy", "came", "to", ""]
["a", "boy", "came", "to", "school", ""]
You may need to remove last item from the array:
override func controlTextDidChange(obj: NSNotification) {
guard let textField = obj.object as? NSTextField else { return }
if textField.stringValue.characters.last == " " {
print(textField.stringValue.componentsSeparatedByString(" ").filter { $0 != "" })
}
}
For NSTextView the logic is similar. Ctrl drag from text view to set view controller as delegate:
Then use the following code:
func textDidChange(obj: NSNotification) {
guard let textView = obj.object as? NSTextView else { return }
guard let stringValue = textView.textContainer?.textView?.string else { return }
if stringValue.characters.last == " " {
print(stringValue.componentsSeparatedByString(" ").filter { $0 != "" })
}
}
It will work fine:
Adding margins to TextEditor
so I found a simple solution and hard one.
1. Simple one
import SwiftUI
extension NSTextView {
open override var frame: CGRect {
didSet {
// Top inset
textContainerInset = NSSize(width: 0, height: 72)
// Left fragment padding <<< This is what I was looking for
textContainer?.lineFragmentPadding = 72
}
}
}
struct TextEditingView: View {
@State private var fullText: String = "One \nTwo \nThree"
var body: some View {
TextEditor(text: $fullText)
.frame(width: 720, height: 480)
.font(.system(size: 24, design: .monospaced))
}
}
As result you get this:
A repository of the demo:
https://github.com/yaosamo/Swift-TextView-Demo
2. Second solution
Using NSParagraphStyle, headIndent, firstLineHeadIndent
I believe this is how indents on Pages on Mac implemented. I do not know tho how they persist default indent. If you open ruler you will see that it set to 1 and you can't go below it.
Using code of
(AppKit) Tab insertion inside of NSTextBlock
class ParagraphStyle {
let bgColor: NSColor
let paragraphStyle: NSParagraphStyle
init(bgColor: NSColor) {
self.bgColor = bgColor
//Set paragraph style
self.paragraphStyle = {
let mutableParagraphStyle = NSMutableParagraphStyle()
let specialBlock = CustomTextBlock(bgColor: bgColor)
mutableParagraphStyle.textBlocks.append(specialBlock)
mutableParagraphStyle.headIndent = 50 // Add indent here
let style = mutableParagraphStyle as NSParagraphStyle
return style
}()
}}
you can add headIndent to text style. And it will work for copy that you insert there. Problem like I said if you start typing Indents break and I don't know how to preserve it.
First one works for me exactly how I want it. Next will figure out how to use headIndent/FirstlineheadIndent
Thanks to this community I was able to find a solution! Don't give up you also can make it! :D
Related Topics
Libicuuc.So.55: Cannot Open Shared Object File
Detect When Wkwebview Is Finished Loading
How to Implement Default Associated Values with Swift Enums
Xcode Takes Long Time to Print Debug Results
Swiftui Sheet Not Animating Dismissal on MACos Big Sur
Uirefreshcontrol() in iOS 11 Glitchy Effect
Why Does Using Dynamictype on a Force Unwrapped Nil Optional Value Type Work
Countforfetchrequest in Swift 2.0
How to Cast a Metaclass Object to a Protocol Type in Swift
Building for Arm64E on Apple Silicon
What Might Be Causing This Animation Bug with Swiftui and Navigationview
Syncconfiguration Deprecated, What Is the Proper Use of Syncuser.Configuration()
Protocol Having Generic Function and Associatedtype
How to Save Value in Nsset Core Data (Swift)
How to Add an Admob Gadbannerview to Every View
Compiler Cannot Infer Return Type
Urlsessiondelegate's Didwritedata Not Call When App Is Going to Background in iOS12