Mac開發-NSTextView軟回車轉換爲硬回車

背景說明

軟回車和硬回車:在字處理軟件中,由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

學習不斷,有錯誤請指出,如果對你有幫助,請點個贊👍

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章