背景說明
軟回車和硬回車:在字處理軟件中,由Enter鍵按下去導致一行文字換行的叫硬回車,程序自動換行的叫做軟回車。
當NSTextView的寬度被限制時,會自動換行,這裏就是添加了軟回車,當我們手動鍵入Enter,則是在字符串中插入了\n
換行符,數據內容已經改變。軟回車不會改變數據內容,即不會插入\n
需求爲當NSTextView進行軟回車時,記錄下來,在某個時機發出其內容,並將軟回車轉換爲硬回車,即在對應位置插入\n
TextKit
NSTextView的基礎使用參考 https://blog.csdn.net/lovechris00/article/details/78014081
TextKit 框架主要的成員對象(典型的MVC):
- NSTextStorage 是 NSMutableAttributedString 的子類,負責存儲需要處理的文本及其屬性。
- NSLayoutManager 負責將 NSTextStorage 中的文本數據渲染到顯示區域上,負責字符的編碼和佈局。
- NSTextContainer 描述了一個顯示區域,默認是矩形,其子類可以定義任意的形狀。它不僅定義了可填充的區域,而且內部還定義了一個不可填充區域(Bezier Path 數組)。
參考這篇文章 https://www.jianshu.com/p/a12ecae89d6b 和 官方文檔 中搜索。
即NSTextStorage負責文本內容,NSLayoutManager負責佈局換行,字符處理,NSTextContainer負責顯示的區域。
在我們使用NSTextView時,默認都會關聯上述3個。監聽軟回車則需要監聽到換行,或者每行所佔字符數,即着重點在NSLayoutManager中。
解決方案
最初
layoutManager:shouldBreakLineByWordBeforeCharacterAtIndex
方法是NSLayoutManager的代理方法,在文本改變時,會拋出軟回車位置。
起初我想用這個回調,但發現layoutManager:shouldBreakLineByWordBeforeCharacterAtIndex
在文本內容全是英文時,返回的軟回車位置不對。
例如:
本應該軟回車位置爲 11 22 33 時
當中文時,返回爲 11 22 33
當英文時,返回爲 0 11 22 會缺少最後一位
這個回調當文本改變時就會回調。
最終方案
我又找到了一個可以計算出每行的字符長度的方法
即NSLayoutManager中的 enumerateLineFragmentsForGlyphRange
// Enumerates line fragments intersecting with glyphRange.
- (void)enumerateLineFragmentsForGlyphRange:(NSRange)glyphRange usingBlock:(void (^)(NSRect rect, NSRect usedRect, NSTextContainer *textContainer, NSRange glyphRange, BOOL *stop))block NS_AVAILABLE(10_11, 7_0);
這個方法需要主動調用,會在block塊中返回每行的字符串範圍。
參數說明如下
rect 預留給該行使用的大小
usedRect 實際該行使用的大小
textContainer 自己的textContainer實例
glyphRange 文本Range,由Postion和length組成。表示該行開始位置,以及長度
那我們就很好處理了,分別記錄,然後從後向前插入\n
即可
__weak typeof(self) wself = self;
[_layoutManagerB enumerateLineFragmentsForGlyphRange:NSMakeRange(0, testString.length) usingBlock:^(NSRect rect, NSRect usedRect, NSTextContainer * _Nonnull textContainer, NSRange glyphRange, BOOL * _Nonnull stop) {
__strong typeof(self) sself = wself;
NSInteger index = glyphRange.location + glyphRange.length;
[sself.breaklineIndexs addObject:@(index)];
if (sself.layoutManagerB.numberOfGlyphs == index) { //因爲最後一個字符後一定會插入\n,當字符數量==index時,即stop
NSString *result = [self getBreaklineString];
NSLog(@"添加硬回車後的字符串爲 %@",result);
*stop = YES;
}
}];
附上最終的demo,供大家參考
鏈接:https://pan.baidu.com/s/1-jgSF9IucvzzCRo0yun1HA 密碼:3nc2
學習不斷,有錯誤請指出,如果對你有幫助,請點個贊👍