【问题标题】:Obj-C - Call number on button tap crashes app?Obj-C - 按钮点击上的呼叫号码崩溃应用程序?
【发布时间】:2020-11-29 06:52:23
【问题描述】:

当我的用户点击按钮时,我希望应用提示用户拨打电话。我正在尝试通过以下方式完成此操作,但是每当点击按钮时,应用程序就会崩溃。我已经仔细检查了 NSString 是否也填充在 phoneUrl 中(没有空值)。知道为什么会发生这种情况吗?

*** 由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“-[NSURL 长度]:无法识别 发送到实例 0x280244620' 的选择器以未捕获的方式终止 NSException 类型的异常

ViewController.m

- (IBAction)phoneLocation:(id)sender {
    
    NSString *number = self.locationHours.text;
    
    NSString *noDashes = [number
       stringByReplacingOccurrencesOfString:@"-" withString:@""];

    NSURL *phoneUrl = [NSURL URLWithString:[NSString stringWithFormat:@"tel://%@", noDashes]];

    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:phoneUrl]];

 
}

【问题讨论】:

    标签: ios objective-c


    【解决方案1】:

    这个答案基本上建立在Kamil.S 的原则上,但使用现代API,如果拼写错误,它根本不会打开phoneUrl。它还可以防止在电话号码中重复输入+

    在研究如何正确拨打电话时,发现只有容易出错的电话号码检测还试图在没有第三方依赖的情况下进行更好的检测。 (请注意,以下解决方案并不意味着扫描整个文本,可能有很多数字散布在单词之间) PS:但是是的,当数字的排序有意义时,您甚至可以将电话号码隐藏在更大的字符串中。假设你有一个 NSString *dirty; 包含..

    @"uglyPreString +49(17)-12-+345-678 uglyPostString seePlusInBetween?"@"tel://+49(17)-12-+345-678"

    它会起作用吗? 决定编写一个确保您在发送电话号码以调用其他应用程序(即电话应用程序)之前拥有正确的可拨号号码。

    NSString *dirty = @"uglyPreString +49(017)-12-+345-678 uglyPostString seePlusInBetween?";
    
    // Let's keep all (only) possible phone number parts with a regex.
    dirty = [dirty stringByReplacingOccurrencesOfString:@"[^0-9,^+]" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, dirty.length)];
    
    // We may have doublet '+' sign in there 
    // ending up in a wrong call, prevent that
    if (dirty.length>1) {
        // If the possible string contains '+' twice, kick out all '+' after first.
        // If there was no + sign at all, string stays as is. And
        // do not exceed max character index = NSMakeRange(1, dirty.length-1)
        dirty = [dirty stringByReplacingOccurrencesOfString:@"[+]" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(1, dirty.length-1)];
        NSLog(@"does it look nice here? phone: %@",dirty);
    }
    
    // make sure it will not validate if detector doesnt agree its a phonenumber
    // we don't want phonenumbers to fly as url call between app without users consent
    // only apps that conform to handle @"tel://" protocol should be able to handle your call.
    // so we set phonenumber invalid first.
    NSString *phonenumber = nil;
    
    NSError *error = nil;
    NSDataDetector *detector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypePhoneNumber error:&error];
    NSArray *check = [detector matchesInString:dirty options:0 range:NSMakeRange(0, dirty.length)];
    if (check) {
        for (NSTextCheckingResult *result in check) {
            phonenumber = [result phoneNumber];
            if (phonenumber) break;
        }
        // if this part is reached, there is no valid phonenumber in dirty
    }
        
    if (phonenumber) {
        // construct the phone url
        NSString *dial = [NSString stringWithFormat:@"tel://%@",phonenumber];
        NSURL *phoneUrl = [NSURL URLWithString:dial];
        NSLog(@"dial will be %@",phoneUrl.absoluteString);
        
        if ([UIApplication.sharedApplication canOpenURL:phoneUrl]) {
            [UIApplication.sharedApplication openURL:phoneUrl options:@{} completionHandler:^(BOOL success) {
                // assuming the app that opens is phone app.
                NSLog(@"pressed %@ button", success ? @"green/call" : @"red/cancel");
            }];
        }
    }
    

    【讨论】:

      【解决方案2】:

      我很惊讶甚至编译。很确定你的最后一行可能有一个警告

      [[UIApplication sharedApplication] openURL:[NSURL URLWithString:phoneUrl]];
      

      您的 phoneUrl 在此调用之前被定义为 NSURL *,但您将其作为 NSString * 参数传递。您可以对照 Apple 文档 https://developer.apple.com/documentation/foundation/nsurl/1572047-urlwithstring 验证这一点

      • (实例类型)URLWithString:(NSString *)URLString;

      因此,在(instancetype)URLWithString:(NSString *)URLString; 内部,调用(由于其内部实现细节)尝试发送length Objective-c 运行时消息(即从高级角度调用length 方法),这对于NSString * 但绝对不适合 NSURL *。因此,您会看到您提供的 NSInvalidArgumentException 消息。

      一个可能的解决方法可能是将您的最后一行更改为:

      [[UIApplication sharedApplication] openURL:phoneUrl];
      

      @OlSen 建议使用现代非弃用 API 更好: https://stackoverflow.com/a/48618644/1443038

      【讨论】:

      • @Brittany API_DEPRECATED_WITH_REPLACEMENT("openURL:options:completionHandler:", ios(2.0, 10.0)) NS_EXTENSION_UNAVAILABLE_IOS("") 走这边.. SO answer
      • ...很明显我需要睡觉了,哈哈,太明显了O_O不知道我怎么忽略了这一点。谢谢!!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多