阿拉伯语的标签会使应用程序崩溃
Hashtags in Arabic language crashes the app
我有一个主题标签解析器 UITextView 扩展,可以将前面带有“#”的任何单词变成可点击的蓝色 link。除了点击阿拉伯语主题标签时,它工作正常。
extension UITextView {
func resolveHashAndMentionTags(){
// turn string in to NSString
let nsText:NSString = self.text as NSString
// this needs to be an array of NSString. String does not work.
let words:[NSString] = nsText.components(separatedBy: " ") as [NSString]
// you can't set the font size in the storyboard anymore, since it gets overridden here.
let attrs = [
NSAttributedStringKey.font : UIFont(name: "Avenir-Light", size: 14.0)!//UIFont.systemFont(ofSize: 13.0)
]
// you can staple URLs onto attributed strings
let attrString = NSMutableAttributedString(string: nsText as String, attributes:attrs)
// tag each word if it has a hashtag or mention
for word in words {
// found a word that is prepended by a hashtag!
if word.hasPrefix("#") {
// a range is the character position, followed by how many characters are in the word.
// we need this because we staple the "href" to this range.
let matchRange:NSRange = nsText.range(of: word as String)
// convert the word from NSString to String
// this allows us to call "dropFirst" to remove the hashtag
var stringifiedWord:String = word as String
// drop the hashtag
stringifiedWord = String(stringifiedWord.dropFirst())
print(stringifiedWord)
attrString.addAttribute(NSAttributedStringKey.link, value: "hash://\(stringifiedWord)", range: matchRange)
// }
} else if word.hasPrefix("@") {
// a range is the character position, followed by how many characters are in the word.
// we need this because we staple the "href" to this range.
let matchRange:NSRange = nsText.range(of: word as String)
// convert the word from NSString to String
// this allows us to call "dropFirst" to remove the mention@
var stringifiedWord:String = word as String
// drop the hashtag
stringifiedWord = String(stringifiedWord.dropFirst())
attrString.addAttribute(NSAttributedStringKey.link, value: "mention://\(stringifiedWord)", range: matchRange)
// }
}
}
// we're used to textView.text
// but here we use textView.attributedText
// again, this will also wipe out any fonts and colors from the storyboard,
// so remember to re-add them in the attrs dictionary above
self.attributedText = attrString
}
然后在我的帖子单元格中调用 UITextViewDelegate
函数。
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
let path = URL.absoluteString
switch URL.scheme! {
case "hash" :
let hash = path.removingPercentEncoding?.components(separatedBy: "://").last
print(hash!) // ---> Retriving tapped on hash name correctly
delegate?.hashTableViewCell(self, shouldSelectTag: hash!)
case "mention" :
let mention = path.removingPercentEncoding?.components(separatedBy: "://").last
print(mention!) // ---> Retriving tapped on mention name correctly
delegate?.mentionTableViewCell(self, shouldSelectTag: mention!, userId: nil)
default:
print("Just a regular link \(path.removingPercentEncoding!)")
}
return true
}
正如我上面提到的,该代码对于英文字母工作正常,但是当点击阿拉伯语标签或提及它时,它会在 AppDelegate
.
中崩溃
Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1030fcb94)
我尝试使用断点进行调试以了解错误的确切位置,但实际上我发现它甚至在进入 UITextViewDelegate
函数之前就崩溃了。所以现在我完全不知道错误可能在哪里。
在 UITextView
中将阿拉伯字母用作 link 可能存在某种问题?
这是堆栈跟踪。
- thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=1, subcode=0x1030fcb94)
frame #0: 0x00000001030fcb94 libswiftFoundation.dylib
function signature specialization <Arg[0] = Owned To Guaranteed, Arg[1] = Dead> of static Foundation.URL._unconditionallyBridgeFromObjectiveC(Swift.Optional<__ObjC.NSURL>) -> Foundation.URL + 288
frame #1: 0x0000000103045f24 libswiftFoundation.dylib
static Foundation.URL._unconditionallyBridgeFromObjectiveC(Swift.Optional<__ObjC.NSURL>) -> Foundation.URL + 20
frame #2: 0x0000000101199c68 OOL@objc PostTableViewCell.textView(_:shouldInteractWith:in:) at PostTableViewCell.swift:0
frame #3: 0x000000018e1e397c UIKit
-[_UITextViewInteractableLink allowInteraction:] + 360
frame #4: 0x000000018e1e2698 UIKit-[_UITextViewInteractableItem handleTap] + 40
frame #5: 0x000000018db64548 UIKit
-[UITextGestureClusterLinkInteract smallDelayRecognizer:] + 296
frame #6: 0x000000018db7ccd0 UIKit-[UIGestureRecognizerTarget _sendActionWithGestureRecognizer:] + 64
frame #7: 0x000000018db812c4 UIKit
_UIGestureRecognizerSendTargetActions + 124
frame #8: 0x000000018d659aa8 UIKit_UIGestureRecognizerSendActions + 320
frame #9: 0x000000018d510c38 UIKit
-[UIGestureRecognizer _updateGestureWithEvent:buttonEvent:] + 732
frame #10: 0x000000018db6ab34 UIKit_UIGestureEnvironmentUpdate + 1084
frame #11: 0x000000018db6a6a4 UIKit
-[UIGestureEnvironment _deliverEvent:toGestureRecognizers:usingBlock:] + 404
frame #12: 0x000000018db69800 UIKit-[UIGestureEnvironment _updateGesturesForEvent:window:] + 276
frame #13: 0x000000018d50ef44 UIKit
-[UIWindow sendEvent:] + 3180
frame #14: 0x000000018d4dff64 UIKit-[UIApplication sendEvent:] + 340
frame #15: 0x000000018de3531c UIKit
__dispatchPreprocessedEventFromEventQueue + 2364
frame #16: 0x000000018de378a8 UIKit__handleEventQueueInternal + 4760
frame #17: 0x000000018de307c0 UIKit
__handleHIDEventFetcherDrain + 152
frame #18: 0x0000000183fa697c CoreFoundation__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 24
frame #19: 0x0000000183fa68fc CoreFoundation
__CFRunLoopDoSource0 + 88
frame #20: 0x0000000183fa6184 CoreFoundation__CFRunLoopDoSources0 + 204
frame #21: 0x0000000183fa3d5c CoreFoundation
__CFRunLoopRun + 1048
frame #22: 0x0000000183ec3e58 CoreFoundationCFRunLoopRunSpecific + 436
frame #23: 0x0000000185d70f84 GraphicsServices
GSEventRunModal + 100
frame #24: 0x000000018d54367c UIKit`UIApplicationMain + 236
- frame #25: 0x0000000101063348 OOL
main at AppDelegate.swift:22
frame #26: 0x00000001839e056c libdyld.dylib
start + 4
我猜可能是问题所在(标签或哈希)...
你这样做:
attrString.addAttribute(NSAttributedStringKey.link, value: "hash://\(stringifiedWord)", range: matchRange)
但是如果你使用 URL
对象 (let url = URL.init(string:"hash://\(stringifiedWord)")
) 你会看到它可能是 nil
以防无效字符串 url 并且在你的大小写阿拉伯字符似乎无效。
委托方法 func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool
在参数中给出一个 URL
对象,并且因为当您没有有效的 url 时不会调用您的方法,我猜想内部Apple 代码中的行假定它是有效的 url,不检查它并崩溃。这可以解释为什么你的方法在进入你的方法之前没有被调用并崩溃。
解决方案:使用百分号转义字符。
不要使用 value: "hash://\(stringifiedWord)"
,而是使用 value: "hash://\(stringifiedWord.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed))"
或其他允许的字符集。
在委托方法中,您已经删除了转义百分比:let hash = path.removingPercentEncoding?.components(separatedBy: "://").last
因此您不必在那里添加任何内容。
补充说明:
你正在做 if word.hasPrefix("#")
和 `if word.hasPrefix("@") 几乎相同的事情(只是方案改变),你可以 simplify/factorize 那,示例代码未测试:
let dict = ["#":"mention://", "@": "hash://"]
for (key, value) in dict
{
if word.hasPrefix(key)
{
let matchRange:NSRange = nsText.range(of: word as String)
var stringifiedWord:String = word as String
stringifiedWord = String(stringifiedWord.dropFirst(key.count))
let espcapedWord = stringifiedWord.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed
let stringURL = "\(value)\(escapedWord)"
if let url = URL.init(string:stringURL) //That should you avoid any crash just in case
{
attrString.addAttribute(.link, value: stringURL, range: matchRange)
}
else
{
print("Oooops with \(stringifiedWord) invalid URL: \(stringURL)")
}
}
}
编辑:根据 iOS 版本(在模拟器上测试),它可能会崩溃,或者因为 URL.
无效而根本不调用委托方法
我有一个主题标签解析器 UITextView 扩展,可以将前面带有“#”的任何单词变成可点击的蓝色 link。除了点击阿拉伯语主题标签时,它工作正常。
extension UITextView {
func resolveHashAndMentionTags(){
// turn string in to NSString
let nsText:NSString = self.text as NSString
// this needs to be an array of NSString. String does not work.
let words:[NSString] = nsText.components(separatedBy: " ") as [NSString]
// you can't set the font size in the storyboard anymore, since it gets overridden here.
let attrs = [
NSAttributedStringKey.font : UIFont(name: "Avenir-Light", size: 14.0)!//UIFont.systemFont(ofSize: 13.0)
]
// you can staple URLs onto attributed strings
let attrString = NSMutableAttributedString(string: nsText as String, attributes:attrs)
// tag each word if it has a hashtag or mention
for word in words {
// found a word that is prepended by a hashtag!
if word.hasPrefix("#") {
// a range is the character position, followed by how many characters are in the word.
// we need this because we staple the "href" to this range.
let matchRange:NSRange = nsText.range(of: word as String)
// convert the word from NSString to String
// this allows us to call "dropFirst" to remove the hashtag
var stringifiedWord:String = word as String
// drop the hashtag
stringifiedWord = String(stringifiedWord.dropFirst())
print(stringifiedWord)
attrString.addAttribute(NSAttributedStringKey.link, value: "hash://\(stringifiedWord)", range: matchRange)
// }
} else if word.hasPrefix("@") {
// a range is the character position, followed by how many characters are in the word.
// we need this because we staple the "href" to this range.
let matchRange:NSRange = nsText.range(of: word as String)
// convert the word from NSString to String
// this allows us to call "dropFirst" to remove the mention@
var stringifiedWord:String = word as String
// drop the hashtag
stringifiedWord = String(stringifiedWord.dropFirst())
attrString.addAttribute(NSAttributedStringKey.link, value: "mention://\(stringifiedWord)", range: matchRange)
// }
}
}
// we're used to textView.text
// but here we use textView.attributedText
// again, this will also wipe out any fonts and colors from the storyboard,
// so remember to re-add them in the attrs dictionary above
self.attributedText = attrString
}
然后在我的帖子单元格中调用 UITextViewDelegate
函数。
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
let path = URL.absoluteString
switch URL.scheme! {
case "hash" :
let hash = path.removingPercentEncoding?.components(separatedBy: "://").last
print(hash!) // ---> Retriving tapped on hash name correctly
delegate?.hashTableViewCell(self, shouldSelectTag: hash!)
case "mention" :
let mention = path.removingPercentEncoding?.components(separatedBy: "://").last
print(mention!) // ---> Retriving tapped on mention name correctly
delegate?.mentionTableViewCell(self, shouldSelectTag: mention!, userId: nil)
default:
print("Just a regular link \(path.removingPercentEncoding!)")
}
return true
}
正如我上面提到的,该代码对于英文字母工作正常,但是当点击阿拉伯语标签或提及它时,它会在 AppDelegate
.
Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1030fcb94)
我尝试使用断点进行调试以了解错误的确切位置,但实际上我发现它甚至在进入 UITextViewDelegate
函数之前就崩溃了。所以现在我完全不知道错误可能在哪里。
在 UITextView
中将阿拉伯字母用作 link 可能存在某种问题?
这是堆栈跟踪。
- thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=1, subcode=0x1030fcb94) frame #0: 0x00000001030fcb94 libswiftFoundation.dylib
function signature specialization <Arg[0] = Owned To Guaranteed, Arg[1] = Dead> of static Foundation.URL._unconditionallyBridgeFromObjectiveC(Swift.Optional<__ObjC.NSURL>) -> Foundation.URL + 288 frame #1: 0x0000000103045f24 libswiftFoundation.dylib
static Foundation.URL._unconditionallyBridgeFromObjectiveC(Swift.Optional<__ObjC.NSURL>) -> Foundation.URL + 20 frame #2: 0x0000000101199c68 OOL@objc PostTableViewCell.textView(_:shouldInteractWith:in:) at PostTableViewCell.swift:0 frame #3: 0x000000018e1e397c UIKit
-[_UITextViewInteractableLink allowInteraction:] + 360 frame #4: 0x000000018e1e2698 UIKit-[_UITextViewInteractableItem handleTap] + 40 frame #5: 0x000000018db64548 UIKit
-[UITextGestureClusterLinkInteract smallDelayRecognizer:] + 296 frame #6: 0x000000018db7ccd0 UIKit-[UIGestureRecognizerTarget _sendActionWithGestureRecognizer:] + 64 frame #7: 0x000000018db812c4 UIKit
_UIGestureRecognizerSendTargetActions + 124 frame #8: 0x000000018d659aa8 UIKit_UIGestureRecognizerSendActions + 320 frame #9: 0x000000018d510c38 UIKit
-[UIGestureRecognizer _updateGestureWithEvent:buttonEvent:] + 732 frame #10: 0x000000018db6ab34 UIKit_UIGestureEnvironmentUpdate + 1084 frame #11: 0x000000018db6a6a4 UIKit
-[UIGestureEnvironment _deliverEvent:toGestureRecognizers:usingBlock:] + 404 frame #12: 0x000000018db69800 UIKit-[UIGestureEnvironment _updateGesturesForEvent:window:] + 276 frame #13: 0x000000018d50ef44 UIKit
-[UIWindow sendEvent:] + 3180 frame #14: 0x000000018d4dff64 UIKit-[UIApplication sendEvent:] + 340 frame #15: 0x000000018de3531c UIKit
__dispatchPreprocessedEventFromEventQueue + 2364 frame #16: 0x000000018de378a8 UIKit__handleEventQueueInternal + 4760 frame #17: 0x000000018de307c0 UIKit
__handleHIDEventFetcherDrain + 152 frame #18: 0x0000000183fa697c CoreFoundation__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 24 frame #19: 0x0000000183fa68fc CoreFoundation
__CFRunLoopDoSource0 + 88 frame #20: 0x0000000183fa6184 CoreFoundation__CFRunLoopDoSources0 + 204 frame #21: 0x0000000183fa3d5c CoreFoundation
__CFRunLoopRun + 1048 frame #22: 0x0000000183ec3e58 CoreFoundationCFRunLoopRunSpecific + 436 frame #23: 0x0000000185d70f84 GraphicsServices
GSEventRunModal + 100 frame #24: 0x000000018d54367c UIKit`UIApplicationMain + 236
- frame #25: 0x0000000101063348 OOL
main at AppDelegate.swift:22 frame #26: 0x00000001839e056c libdyld.dylib
start + 4
我猜可能是问题所在(标签或哈希)...
你这样做:
attrString.addAttribute(NSAttributedStringKey.link, value: "hash://\(stringifiedWord)", range: matchRange)
但是如果你使用 URL
对象 (let url = URL.init(string:"hash://\(stringifiedWord)")
) 你会看到它可能是 nil
以防无效字符串 url 并且在你的大小写阿拉伯字符似乎无效。
委托方法 func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool
在参数中给出一个 URL
对象,并且因为当您没有有效的 url 时不会调用您的方法,我猜想内部Apple 代码中的行假定它是有效的 url,不检查它并崩溃。这可以解释为什么你的方法在进入你的方法之前没有被调用并崩溃。
解决方案:使用百分号转义字符。
不要使用 value: "hash://\(stringifiedWord)"
,而是使用 value: "hash://\(stringifiedWord.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed))"
或其他允许的字符集。
在委托方法中,您已经删除了转义百分比:let hash = path.removingPercentEncoding?.components(separatedBy: "://").last
因此您不必在那里添加任何内容。
补充说明:
你正在做 if word.hasPrefix("#")
和 `if word.hasPrefix("@") 几乎相同的事情(只是方案改变),你可以 simplify/factorize 那,示例代码未测试:
let dict = ["#":"mention://", "@": "hash://"]
for (key, value) in dict
{
if word.hasPrefix(key)
{
let matchRange:NSRange = nsText.range(of: word as String)
var stringifiedWord:String = word as String
stringifiedWord = String(stringifiedWord.dropFirst(key.count))
let espcapedWord = stringifiedWord.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed
let stringURL = "\(value)\(escapedWord)"
if let url = URL.init(string:stringURL) //That should you avoid any crash just in case
{
attrString.addAttribute(.link, value: stringURL, range: matchRange)
}
else
{
print("Oooops with \(stringifiedWord) invalid URL: \(stringURL)")
}
}
}
编辑:根据 iOS 版本(在模拟器上测试),它可能会崩溃,或者因为 URL.
无效而根本不调用委托方法