Appending text to NSTextView in Swift 3
I think I got this figured out. I went into the view controller scene and drilled down into the NSScrollView > Clip View > Text View and then created my outlet out of that instead of just manually changing it from NSScrollView to NSTextView.
Once I did that, the following line did me just fine:
myTxtView.textStorage?.append(NSAttributedString(string: "Hello World"))
Append to NSTextView and scroll
After cross-referencing several answers and sources (with some tweaks), here's the answer that does work (given _myTextView
is an NSTextView
outlet) :
- (void)appendToMyTextView:(NSString*)text
{
dispatch_async(dispatch_get_main_queue(), ^{
NSAttributedString* attr = [[NSAttributedString alloc] initWithString:text];
[[_myTextView textStorage] appendAttributedString:attr];
[_myTextView scrollRangeToVisible:NSMakeRange([[_myTextView string] length], 0)];
});
}
Change the text of a NSTextView programmatically
The right method is "setString" [textView setString:@"the string"];
Scroll an NSTextView to bottom after adding text
NSTextView has an exact action: scrollToEndOfDocument(_ sender: Any?)
.
So, I suppose the following code is enough.
self.logTextView.string += "\nnew text"
self.logTextView.scrollToEndOfDocument(nil)
NSMutableAttributedString for adding subscript (superscript) for NSTextView from String
I would suggest using a standard NSAttributedString
and NSBaselineOffsetAttributeName
. Take a look at the example I just put together:
override func viewDidLoad() {
super.viewDidLoad()
let label = NSTextView(frame: CGRect(x: 20, y: 20, width: 100, height: 30))
let str = "A1 = 50 m2"
let aString = NSMutableAttributedString(string: str)
let myFont = NSFont(name: label.font!.fontName, size: 10.0)
let subscriptAttributes: [String : Any] = [ NSBaselineOffsetAttributeName: -5, NSFontAttributeName: myFont! ]
let superscriptAttributes: [String : Any] = [ NSBaselineOffsetAttributeName: 5, NSFontAttributeName: myFont! ]
aString.addAttributes(subscriptAttributes, range: NSRange(location: 1, length: 1))
aString.addAttributes(superscriptAttributes, range: NSRange(location: 9, length: 1))
// Kerning adds a little spacing between all the characters.
aString.addAttribute(NSKernAttributeName, value: 1.5, range: NSRange(location: 0, length: 2))
aString.addAttribute(NSKernAttributeName, value: 1.5, range: NSRange(location: 8, length: 2))
label.textStorage?.append(aString)
view.addSubview(label)
}
Here's the result:
Automatically markup part of NSTextView
Please try the following. My view controller has a single property, textView that points to an NSTextView in my storyboard.
import Cocoa
class ViewController: NSViewController {
@IBOutlet var textView: NSTextView!
override func viewDidLoad() {
super.viewDidLoad()
let text = "This is the string of my #NSTextView. All #hashtags should be displayed in bold."
textView.string = text
let attributes = [NSFontAttributeName : NSFont.systemFont(ofSize: 14.0)]
let range = NSRange(location: 0, length: text.characters.count)
textView.textStorage?.setAttributes(attributes, range: range)
do {
let regexString = "#(\\w*)"
let regex = try NSRegularExpression(pattern: regexString, options: [])
let matches = regex.matches(in: text, options: [], range: NSRange(location: 0, length: text.characters.count))
for match in matches {
textView.textStorage?.setAttributes([NSFontAttributeName : NSFont.boldSystemFont(ofSize: 14.0)], range: match.range)
}
} catch let error {
print(error)
}
}
}
Append text to NSScrollView - Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
Your issue is in clickOption
when you are calling:
let vc = ViewController()
vc.myPrint(string: "This is a test")
When you call this method from code and the ViewController's
UIViews
are set up in a storyboard, the connection from the storyboard is not made. That is why the notesArea
is nil when you call the function myPrint
. In this case you are creating a new copy of ViewController
and it will not be the same one that created the popover.
There are a few ways you can solve the problem that you are trying to accomplish. One of them is known as a delegate
. This is a way for you to to call the ViewController's
methods like your popover
inherited them. You can check out a tutorial here. The idea is that we want to have a reference to the ViewController
in your popover
so that you can call the functions in the protocol
. Then the ViewController
that conforms to the protocol
will be responsible for handling the method call.
So let's create a protocol
called PrintableDelegate
and have your ViewController
class conform to it. Then in your popover
, you will be able to have a reference to the ViewController
as a weak var
called delegate
(you can use what ever name you want but delegate
is standard). Then we can call the methods described in the protocol PrintableDelegate
, by simply writing delegate?.myPrint(string: "Test")
. I have removed some of your irrelevant code from my example.
protocol PrintableDelegate {
func myPrint(string: String)
}
class ViewController : UIViewController, PrintableDelegate {
func myPrint (string: String){
let mystring = string
let myNotes = notesArea.documentView as? NSTextView
let text = myNotes?.textStorage!
let attr = NSAttributedString(string: mystring)
text?.append(attr)
}
@IBAction func oneClicked(_ sender: NSButton) {
let vc = PopoverVC1.loadView()
// Set the delegate of the popover to this ViewController
vc.delegate = self
vc.showPopover(view: sender)
}
}
class PopoverVC1: NSViewController {
// Delegates should be weak to avoid a retain cycle
weak var delegate: PrintableDelegate?
@IBAction func clickOption(_ sender: NSButton) {
// Use the delegate that was set by the ViewController
// Note that it is optional so if it was not set, then this will do nothing
delegate?.myPrint(string: "This is a test")
}
}
Related Topics
Core Data: Rename Attribute Without Having Issues with Users and Their Current Data
How to Simplify Swift Enum Custom Init
Xctestcase Optional Instance Variable
Using State Variables as Inputs to a Func in Swiftui
Is There a Technical Reason to Use Swift's Caseless Enum Instead of Real Cases
Swiftui Widget iOS 14 Gradient Issue
Realitykit - Load Another Scene from the Same Reality Composer Project
Drawing a Gradient Color in an Arc with a Rounded Edge
How to Pass One Swiftui View as a Variable to Another View Struct
How to Use Enumeratedate in Swift 3 to Find All Sundays the Last 50 Years
Swift String Permutations Allowing the Same Strings
Shared Cookies with Wkprocesspool for Wkwebview in Swift
How to Get a Double Value Up to 2 Decimal Places
How to Immediately See Swift Errors in Appcode