Post

iOS检测文本里的链接、数字等的内容

常见的iOS应用里,一行文字都可以标记出电话号码、链接等内容,方便用户在一段文本中点击拨打电话或点击链接

系统级实现方式,使用系统的
NSDataDetector + NSTextCheckingTypeLink +NSTextCheckingTypePhoneNumber

- (void)enumerateMatchesInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range usingBlock:(void (NS_NOESCAPE ^)(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL *stop))block

具体获取和实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
NSString *plainText = @let text = "联系👏🏻 订单1234567 👋 我👨‍👩‍👧‍👦:123-456-7890 或访问 https://example.com 获取更多 google.com信息。";
// 获取检测器
NSDataDetector *detector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink | NSTextCheckingTypePhoneNumber
                                                   error:nil];
// 使用检测器检测文本内容
[detector enumerateMatchesInString:plainText
                           options:0
                             range:NSMakeRange(0, [plainText length])
                        usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) {
    NSRange range = result.range;
    NSString *subString = [plainText substringWithRange:range];
    let resultType = result.resultType;
    NSLog(@"range: %@ type:%llu text: %@ url: %@", NSStringFromRange(range), result.resultType, subString, result.URL);
}];

swift 版本的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
let text = "联系👏🏻 订单123456 👋 我👨‍👩‍👧‍👦:123-456-7890 或1234567 和 1234568 访问 https://example.com 获取更多 google.com信息。"
print(text)
print("NSString(text).length: \(NSString(string: text).length)")
print("text.count: \(text.count)")
print("text.utf8.count: \(text.utf8.count)")
print("text.utf16.count: \(text.utf16.count)")
// String的utf16.count 与 NSString的length是一样的
do {
    // 配置查找规则
    let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue | NSTextCheckingResult.CheckingType.phoneNumber.rawValue)
    // 在指定字符串里查找匹配
    let results = detector?.matches(in: text, range: NSRange(location: 0, length: text.utf16.count)) ?? []
    // 处理查找结果
    for result in results {
        // 将range反向查找字符串
        var subString: String? = nil
        if let textRange = Range(result.range, in: text) {
            subString = String(text[textRange])
        }
        print("range: \(result.range) type:\(result.resultType) text: \(subString ?? "") url:\(result.url?.absoluteString ?? "")")
    }
}
/** 
联系👏🏻 订单12345 👋 我👨‍👩‍👧‍👦:123-456-7890 或1234567 和 1234568 访问 https://example.com 获取更多 google.com信息。
NSString(text).length: 104
text.count: 90
text.utf8.count: 158
text.utf16.count: 104
range: {31, 12} type:NSTextCheckingType(rawValue: 2048) text: 123-456-7890 url:
range: {45, 7} type:NSTextCheckingType(rawValue: 2048) text: 1234567 url:
range: {55, 7} type:NSTextCheckingType(rawValue: 2048) text: 1234568 url:
range: {66, 19} type:NSTextCheckingType(rawValue: 32) text: https://example.com url:https://example.com
range: {91, 10} type:NSTextCheckingType(rawValue: 32) text: google.com url:http://google.com
*/

支持的其他类型如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
ypedef NS_OPTIONS(uint64_t, NSTextCheckingType) {    // a single type
    NSTextCheckingTypeOrthography           = 1ULL << 0,            // language identification
    NSTextCheckingTypeSpelling              = 1ULL << 1,            // spell checking
    NSTextCheckingTypeGrammar               = 1ULL << 2,            // grammar checking
    NSTextCheckingTypeDate                  = 1ULL << 3,            // date/time detection
    NSTextCheckingTypeAddress               = 1ULL << 4,            // address detection
    NSTextCheckingTypeLink                  = 1ULL << 5,            // link detection
    NSTextCheckingTypeQuote                 = 1ULL << 6,            // smart quotes
    NSTextCheckingTypeDash                  = 1ULL << 7,            // smart dashes
    NSTextCheckingTypeReplacement           = 1ULL << 8,            // fixed replacements, such as copyright symbol for (c)
    NSTextCheckingTypeCorrection            = 1ULL << 9,            // autocorrection
    NSTextCheckingTypeRegularExpression API_AVAILABLE(macos(10.7), ios(4.0), watchos(2.0), tvos(9.0))  = 1ULL << 10,           // regular expression matches
    NSTextCheckingTypePhoneNumber API_AVAILABLE(macos(10.7), ios(4.0), watchos(2.0), tvos(9.0))        = 1ULL << 11,           // phone number detection
    NSTextCheckingTypeTransitInformation API_AVAILABLE(macos(10.7), ios(4.0), watchos(2.0), tvos(9.0)) = 1ULL << 12            // transit (e.g. flight) info detection
};

extension NSTextCheckingResult {

    public struct CheckingType : OptionSet, @unchecked Sendable {

        public init(rawValue: UInt64)

        public static var orthography: NSTextCheckingResult.CheckingType { get }

        public static var spelling: NSTextCheckingResult.CheckingType { get }

        public static var grammar: NSTextCheckingResult.CheckingType { get }

        public static var date: NSTextCheckingResult.CheckingType { get }

        public static var address: NSTextCheckingResult.CheckingType { get }

        public static var link: NSTextCheckingResult.CheckingType { get }

        public static var quote: NSTextCheckingResult.CheckingType { get }

        public static var dash: NSTextCheckingResult.CheckingType { get }

        public static var replacement: NSTextCheckingResult.CheckingType { get }

        public static var correction: NSTextCheckingResult.CheckingType { get }

        @available(iOS 4.0, *)
        public static var regularExpression: NSTextCheckingResult.CheckingType { get }

        @available(iOS 4.0, *)
        public static var phoneNumber: NSTextCheckingResult.CheckingType { get }

        @available(iOS 4.0, *)
        public static var transitInformation: NSTextCheckingResult.CheckingType { get }
    }
}
This post is licensed under CC BY 4.0 by the author.