在苹果文档之后,我试图NSTextView通过它的两个构造函数方法设置一个简单的.
我将以下代码放在viewDidAppear内容视图的视图控制器的方法中.NSTextViewtextView 是一个实例,frameRect是内容视图的框架.
以下Swift代码可以工作(给我一个可编辑的textView,屏幕上显示文字):
textView = NSTextView(frame: frameRect!)
self.view.addSubview(textView)
textView.textStorage?.appendAttributedString(NSAttributedString(string: "Hello"))
Run Code Online (Sandbox Code Playgroud)
以下不起作用(文本视图不可编辑,屏幕上不显示任何文本):
var textStorage = NSTextStorage()
var layoutManager = NSLayoutManager()
textStorage.addLayoutManager(layoutManager)
var textContainer = NSTextContainer(containerSize: frameRect!.size)
layoutManager.addTextContainer(textContainer)
textView = NSTextView(frame: frameRect!, textContainer: textContainer)
textView.editable = true
textView.selectable = true
self.view.addSubview(textView)
textView.textStorage?.appendAttributedString(NSAttributedString(string: "Hello more complex"))
Run Code Online (Sandbox Code Playgroud)
我在第二个例子中做错了什么?我试图遵循Apple的"可可文本架构指南"中给出的示例,他们NSTextView通过显式实例化其辅助对象Web来讨论设置.
查看文档NSLayoutManager,特别是drawUnderlineForGlyphRange:underlineType:baselineOffset:lineFragmentRect:lineFragmentGlyphRange:containerOrigin:方法,我注意到以下内容(强调我的):
underlineVal
画下划线的风格.此值是从NSUnderlineStyleAttributeName-for(例如)(NSUnderlinePatternDash|NSUnderlineStyleThick)的值派生的掩码.子类可以定义自定义下划线样式.
我的问题是:这究竟是怎么做的?
NSUnderlineStyle是一个枚举,你无法扩展或覆盖.您当然可以Int为属性提供随机原始值,但不包括枚举案例:
self.addAttribute(NSUnderlineStyleAttributeName, value: 100022, range: lastUpdatedWordRange)
哪个会传递"无效"但可underlineType用于布局管理器:
但这并不安全,绝对不优雅.
我无法在线找到任何示例,也无法在Apple文档中找到有关神秘自定义下划线样式类型的更多线索.我很想知道我是否遗漏了一些明显的东西.
默认布局管理器填充没有文本(最后一行除外)的背景颜色(通过 NSAttributedString .backgroundColor 属性指定)。
我已经通过子类化 NSLayoutManager 并覆盖来实现我想要的效果,func drawBackground(forGlyphRange glyphsToShow: NSRange, at origin: CGPoint)如下所示:
override func drawBackground(forGlyphRange glyphsToShow: NSRange, at origin: CGPoint) {
guard let textContainer = textContainers.first, let textStorage = textStorage else { fatalError() }
// This just takes the color of the first character assuming the entire container has the same background color.
// To support ranges of different colours, you'll need to draw each glyph separately, querying the attributed string for the
// background color attribute …Run Code Online (Sandbox Code Playgroud) 我正在尝试创建一个自定义NSTextBlock,就像Apple在WWDC 18上(在23分钟内)所做的那样。
好的,所以当我用带有文本块的段落样式编辑和标记段落时,它的效果很好。
但是,当我剪切并粘贴它(或从磁盘归档/取消归档)时,它将丢失。编辑:它实际上将我的TweetTextBlock子类转换为NSTableViewTextBlock,这也说明了边界。
实作
这是完整的Xcode项目。使用Format顶部菜单项触发markTweet功能。
这是我将属性添加到段落的方法
@IBAction func markTweet(_ sender : Any?){
print("now we are marking")
let location = textView.selectedRange().location
guard let nsRange = textView.string.extractRange(by: .byParagraphs, at: location) else { print("Not in a paragraph"); return }
let substring = (textView.string as NSString).substring(with: nsRange)
let tweetParagraph = NSMutableParagraphStyle()
tweetParagraph.textBlocks = [TweetTextBlock()]
let twitterAttributes : [AttKey : Any] = [
AttKey.paragraphStyle : tweetParagraph,
AttKey.font : NSFont(name: "HelveticaNeue", size: …Run Code Online (Sandbox Code Playgroud) 即使iOS文档说:
只要应用程序保证从单个线程访问,就可以从子线程访问NLayoutManager,NSTextStorage和NSTextContainer.
我偶尔遇到这个例外:
由于未捕获的异常'NSInternalInconsistencyException'终止应用程序,原因:'只在主线程上运行!'
这是回溯:
Exception Type: SIGABRT
Exception Codes: #0 at 0x197bca58c
Crashed Thread: 7
Application Specific Information:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Only run on the main thread!'
Last Exception Backtrace:
0 CoreFoundation 0x000000018afd2f50 __exceptionPreprocess + 132
1 libobjc.A.dylib 0x00000001974dc1fc objc_exception_throw + 56
2 CoreFoundation 0x000000018afd2e10 +[NSException raise:format:arguments:] + 112
3 Foundation 0x000000018bb0ae20 -[NSAssertionHandler handleFailureInFunction:file:lineNumber:description:] + 84
4 UIFoundation 0x00000001940f0654 -[NSLayoutManager(NSPrivate) _resizeTextViewForTextContainer:] + 412
5 UIFoundation 0x00000001940f0318 -[NSLayoutManager(NSPrivate) _recalculateUsageForTextContainerAtIndex:] + 1748
6 UIFoundation 0x000000019411ec2c …Run Code Online (Sandbox Code Playgroud) UITextView在iOS 7中,我一直在使用自动布局以编程方式创建.这样的事情:
_textView = [UITextView new];
_textView.translatesAutoresizingMaskIntoConstraints = NO;
_textView.font = [UIFont systemFontOfSize:18.0];
_textView.delegate = self;
[self.view addSubview:_textView];
Run Code Online (Sandbox Code Playgroud)
除了我创建的其他约束之外,我还有一个用于文本视图的底部:
_textViewBottomConstraint =
[NSLayoutConstraint constraintWithItem:_textView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.bottomLayoutGuide
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0.0];
[self.view addConstraint:_textViewBottomConstraint];
Run Code Online (Sandbox Code Playgroud)
这样我可以在键盘显示时更改约束:
- (void)keyboardDidShow:(NSNotification*)sender
{
CGRect keyboardFrame =
[sender.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect convertedFrame =
[self.view convertRect:keyboardFrame fromView:self.view.window];
_textViewBottomConstraint.constant = -convertedFrame.size.height;
[_textView layoutIfNeeded];
}
Run Code Online (Sandbox Code Playgroud)
这一切都很好......
我刚刚切换到Xcode 6和iOS 8(我知道很晚) - UITextView现在的文本顶部有一个插页.例如(文本视图背景为黄色):

是什么导致了插图?是否NSTextContainer与textContainerInset?相关?
NSTextContainer和textContainerInset的正确使用方式UITextView从iOS的7及以后?我正在尝试构建一个具有UITextView的表视图单元格.为了重用此表视图单元格,我将新文本存储设置到布局管理器.但是将布局管理器添加到新文本存储中不会更改文本视图的文本存储.
布局管理器,文本容器,文本视图的设置如下:
_myLayoutManager = [[NSLayoutManager alloc] init];
_myTextContainer = [[NSTextContainer alloc] init];
[_myLayoutManager addTextContainer:_myTextContainer];
_myTextView = [[UITextView alloc] initWithFrame:self.bounds
textContainer:_myTextContainer];
Run Code Online (Sandbox Code Playgroud)
当我"配置"表格视图单元格时,我将布局管理器添加到新的文本存储中:
MyTextStorage *textStorage = ...
[textStorage addLayoutManager:self.myLayoutManager];
Run Code Online (Sandbox Code Playgroud)
这项工作到目前为止,新文本将正确显示.但是,当我重用单元格(将布局管理器添加到另一个文本存储)并获得文本视图的大小时,通过...
[self.myTextView systemLayoutSizeFittingSize:...]
Run Code Online (Sandbox Code Playgroud)
......得到错误的大小.在查看我发现的文本视图的属性后,它仍然指向错误的文本存储.
self.myLayoutManager.textStorage -> <new text storage>
self.myTextView.textStorage -> <old text storage>
Run Code Online (Sandbox Code Playgroud)
这感觉很奇怪.我是否需要再次设置整个堆栈(布局管理器,文本容器,文本视图)?
如果是,我需要启动一个新的文本视图,并更新布局约束,这将是非常昂贵的.
我正在尝试实现多页文本编辑布局,如 Pages、MS Word 等。在 OS XI 上,可以通过为多个 NSTextView 创建一个 NSLayoutManager 和一个 NSTextStorage 来实现这一点。每个 NSTextView 都有自己的 NSTextContainer。见下面的 OS X 代码。一个简单的例子,文本在两个 NSTextViews 之间传播:
import Cocoa
class ViewController: NSViewController {
let layoutManager = NSLayoutManager()
let textStorage = NSTextStorage(attributedString: NSAttributedString(string: "This is a test"))
let textContainer1 = NSTextContainer()
let textContainer2 = NSTextContainer()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
textStorage.addLayoutManager(layoutManager)
textContainer1.widthTracksTextView = true
textContainer1.heightTracksTextView = true
textContainer2.widthTracksTextView = true
textContainer2.heightTracksTextView = true
let textView1 = NSTextView(frame: CGRectMake(0, …Run Code Online (Sandbox Code Playgroud) 我正在用一个相对较小的文本编辑器来完成一个不错的应用程序。在语法突出显示的实现过程中,我发现自己需要更改已识别标记的前景色属性。我注意到有一个NSTextStorage属性:
var fixesAttributesLazily: Bool { get }
Run Code Online (Sandbox Code Playgroud)
有关的文档是:
一个布尔值,指示文本存储对象是否延迟固定属性。(只读)
讨论区
子类化时,此属性的默认值为NO,这意味着子类在属性更改时会立即修复。系统的具体子类覆盖此属性并将其设置为YES。
我真的不知道该怎么解释...但这就是我所做的:
textStorage(textStorage: NSTextStorage, didProcessEditing editedMask: NSTextStorageEditActions, range editedRange: NSRange, changeInLength delta: Int)(这是NSTextStorage的委托方法)。在这里,我正在检查此属性的值-它为FALSE。fixesAttributesLazily属性的值。也许我对NSTextStorage的实现是不好的,或者至少不是很复杂。欢迎在后台或懒惰地或...应用属性的任何技巧。
另外:我知道有很多方法可以优化突出显示的语法。它可能会部分突出显示,根据更改的范围使用某种逻辑...等等。我正在寻找的是一种在后台处理属性更改的方法。例如,当前,当我将4 MB文件粘贴到文本视图时,它首先突出显示它(需要2-3秒),然后将其可视化。我要寻找的效果是立即显示文字,并在一段时间后显示颜色。
我正在处理的项目是用Swift编写的。
预先感谢大家的帮助。您可以通过gmail dot com上的ivailon与我联系以获取详细信息,因为我不想在这里公开该应用程序...至少现在还没有;-)
使用Swift,我想在UILabel中的draw#rect中获得字形的boundingRect。
UILabel已经具有大小(在示例中为300x300)和诸如文本居中的质量。
class RNDLabel: UILabel {
override func draw(_ rect: CGRect) {
let manager = NSLayoutManager()
let store = NSTextStorage(attributedString: NSAttributedString(
string: text!,
attributes: [NSAttributedString.Key.font: font]))
store.addLayoutManager(manager)
let textContainer = NSTextContainer(size: rect.size)
// note, intrinsicContentSize is identical there, no difference
manager.addTextContainer(textContainer)
let glyphRange = manager.glyphRange(
forCharacterRange: NSRange(location: 0, length: 1),
actualCharacterRange: nil)
let glyphRect = manager.boundingRect(
forGlyphRange: glyphRange, in: textContainer)
print("glyphRect \(glyphRect)")
...context?.addRect(glyphRect), context?.drawPath(using: .stroke)
super.draw(rect)
}
Run Code Online (Sandbox Code Playgroud)
绿色方块不正确-应该更像这些红色方块!
似乎存在许多问题:
当然,我制作的layoutManager应该获得UILabel的质量,例如“居中文本”吗?(不过,我相信您实际上不能直接访问UILabel的layoutManager?)
我们应该使用像CTLineGetOffsetForStringIndex这样的东西吗?在draw#rect中甚至有可能吗
注意,以及没有正确的偏移量,无论如何,绿色框似乎是错误的高度。(它看起来更像是一个普通的旧的internalContentSize,而不是字形边界框。)
如何?
作为记录,我的总体目标是根据实际字形框移动字形(当然,“ y”,“ X”等也不同)。但是总的来说,有很多有用的原因来了解字形框。
nslayoutmanager ×10
swift ×7
ios ×6
textkit ×5
nstextview ×3
uitextview ×3
cocoa ×1
core-text ×1
drawrect ×1
ios7 ×1
ios8 ×1