我正在尝试模拟iOS Pages和Keynote应用的粘贴板行为。简而言之,允许将基本的NSAttributedString文本格式(即BIU)粘贴到UITextView中,但不粘贴到图像,HTML等中。
行为摘要
这是如何实现的?在Mac OS上,您似乎可以自己ask the pasteboard to return different types,让它同时提供富文本和字符串表示,并使用富文本作为首选。不幸的是,对于iOS,readObjectsForClasses似乎不存在。也就是说,我可以通过日志看到iOS确实有RTF相关类型的粘贴板,thanks to this post。但是,我无法找到一种方法来请求NSAttributedString版本的粘贴板内容,因此我可以优先考虑粘贴。
背景
我有一个应用程序,它允许UITextViews中基本的NSAttributedString用户可编辑格式(即粗体,斜体,下划线)。用户希望从其他应用程序(例如Safari中的网页,Notes应用程序中的文本)复制文本,以粘贴到我的应用程序中的UITextView中。允许粘贴板作为默认操作意味着我可能最终得到我的应用程序不打算处理的背景颜色,图像,字体等。下面的示例显示了粘贴到我的应用程序的UITextView时带有背景颜色的复制文本的外观。
我可以通过继承UITextView
来克服1- (void)paste:(id)sender
{
UIPasteboard *pasteBoard = [UIPasteboard generalPasteboard];
NSString *string = pasteBoard.string;
NSLog(@"Pasteboard string: %@", string);
[self insertText:string];
}
意想不到的后果是,无法保留从我的应用中复制的文本格式。用户可能希望从我的应用程序中的一个UITextView复制文本,并将其粘贴到我的应用程序中的另一个UITextView。他们希望保留格式(即粗体,斜体,下划线)。
洞察力和建议表示赞赏。
答案 0 :(得分:3)
这应该是对伦纳德·保利(Leonard Pauli)答案的评论,但是我没有足够的声誉来发表评论。
代替:
selectedRange.location += attributedString.string.characters.count
(或attributedString.string.count,如在较新版本的Swift中一样)
最好使用:
selectedRange.location += attributedString.length
否则,当您粘贴包含表情符号的文本时,这些表情符号会导致attributedString.length和attributedString.string.count不同,那么选择的结果将放在错误的位置。
答案 1 :(得分:2)
一些复制/粘贴好东西和想法,为您:)
// Setup code in overridden UITextView.copy/paste
let pb = UIPasteboard.generalPasteboard()
let selectedRange = self.selectedRange
let selectedText = self.attributedText.attributedSubstringFromRange(selectedRange)
// UTI List
let utf8StringType = "public.utf8-plain-text"
let rtfdStringType = "com.apple.flat-rtfd"
let myType = "com.my-domain.my-type"
pb.setValue(selectedText.string, forPasteboardType: myType)
允许进行富文本复制(复制:):
// Try custom copy
do {
// Convert attributedString to rtfd data
let fullRange = NSRange(location: 0, length: selectedText.string.characters.count)
let data:NSData? = try selectedText.dataFromRange(fullRange, documentAttributes: [NSDocumentTypeDocumentAttribute: NSRTFDTextDocumentType])
if let data = data {
// Set pasteboard values (rtfd and plain text fallback)
pb.items = [[rtfdStringType: data], [utf8StringType: selectedText.string]]
return
}
} catch { print("Couldn't copy") }
// If custom copy not available;
// Copy as usual
super.copy(sender)
允许富文本粘贴(在粘贴中:):
// Custom handling for rtfd type pasteboard data
if let data = pb.dataForPasteboardType(rtfdStringType) {
do {
// Convert rtfd data to attributedString
let attStr = try NSAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSRTFDTextDocumentType], documentAttributes: nil)
// Bonus: Possibly strip all unwanted attributes here.
// Insert it into textview
replaceSelection(attStr)
return
} catch {print("Couldn't convert pasted rtfd")}
}
// Default handling otherwise (plain-text)
else { super.paste(sender) }
更好的是使用自定义粘贴板类型,白名单所有可能需要的标签,循环并剥离所有其他粘贴。
另外值得注意的是,当粘贴板包含特定类型时,textView可能不希望允许粘贴,以修复它:
// Allow all sort of paste (might want to use a white list to check pb.items agains here)
override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
if action == #selector(UITextView.paste(_:)) {
return true
}
return super.canPerformAction(action, withSender: sender)
}
此外,cut:也可以很好地实现。基本上只是copy:
和replaceSelection(emptyString)
为方便起见:
// Helper to insert attributed text at current selection/cursor position
func replaceSelection(attributedString: NSAttributedString) {
var selectedRange = self.selectedRange
let m = NSMutableAttributedString(attributedString: self.attributedText)
m.replaceCharactersInRange(self.selectedRange, withAttributedString: attributedString)
selectedRange.location += attributedString.string.characters.count
selectedRange.length = 0
self.attributedText = m
self.selectedRange = selectedRange
}