在开发某些应用时可能希望能够调用iOS系统内置的电话、短信、邮件、浏览器应用,此时你可以直接使用UIApplication的OpenURL:方法指定特定的协议来打开不同的系统应用。常用的协议如下:
- 打电话:tel:或者tel://、telprompt:或telprompt://(拨打电话前有提示)
- 发短信:sms:或者sms://
- 发送邮件:mailto:或者mailto://
- 启动浏览器:http:或者http://
下面以一个简单的demo演示如何调用上面几种系统应用:
#import "MainViewController.h"
@interface MainViewController ()
@end
@implementation MainViewController
-(void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIButton *call = [UIButton buttonWithType:UIButtonTypeSystem];
call.frame = CGRectMake(100, 100, 100, 30);
[self.view addSubview:call];
[call setTitle:@"call" forState:UIControlStateNormal];
[call addTarget:self action:@selector(callClick:) forControlEvents:UIControlEventTouchUpInside];
UIButton *message = [UIButton buttonWithType:UIButtonTypeSystem];
message.frame = CGRectMake(100, 150, 100, 30);
[self.view addSubview:message];
[message setTitle:@"message" forState:UIControlStateNormal];
[message addTarget:self action:@selector(sendMessageClick:) forControlEvents:UIControlEventTouchUpInside];
UIButton *email = [UIButton buttonWithType:UIButtonTypeSystem];
email.frame = CGRectMake(100, 200, 100, 30);
[self.view addSubview:email];
[email setTitle:@"email" forState:UIControlStateNormal];
[email addTarget:self action:@selector(sendEmailClick:) forControlEvents:UIControlEventTouchUpInside];
UIButton *browser = [UIButton buttonWithType:UIButtonTypeSystem];
browser.frame = CGRectMake(100, 250, 100, 30);
[self.view addSubview:browser];
[browser setTitle:@"browser" forState:UIControlStateNormal];
[browser addTarget:self action:@selector(browseClick:) forControlEvents:UIControlEventTouchUpInside];
UIButton *thirdParty = [UIButton buttonWithType:UIButtonTypeSystem];
thirdParty.frame = CGRectMake(100, 300, 100, 30);
[self.view addSubview:thirdParty];
[thirdParty setTitle:@"thirdParty" forState:UIControlStateNormal];
[thirdParty addTarget:self action:@selector(thirdPartyApplicationClick:) forControlEvents:UIControlEventTouchUpInside];
}
#pragma mark ** UI事件
//打电话
- (IBAction)callClick:(id)sender
{
NSString *phoneNumber = @"13956856323";
// NSString *url = [NSString stringWithFormat:@"tel://%@",phoneNumber];//这种方式会直接拨打电话
NSString *url = [NSString stringWithFormat:@"telprompt://%@",phoneNumber];//这种方式会提示用户是否拨打电话
[self openUrl:url];
}
//发送短信
- (IBAction)sendMessageClick:(id)sender
{
NSString *phoneNumber = @"13956856323";
NSString *url = [NSString stringWithFormat:@"sms://%@",phoneNumber];
[self openUrl:url];
}
//发送邮件
- (IBAction)sendEmailClick:(id)sender
{
NSString *mailAddress = @"[email protected]";
NSString *url = [NSString stringWithFormat:@"mailto://%@",mailAddress];
[self openUrl:url];
}
//浏览网页
- (IBAction)browseClick:(id)sender
{
NSString *url = @"http://www.baidu.com";
[self openUrl:url];
}
#pragma mark ** 私有方法
- (void)openUrl:(NSString *)urlStr
{
//注意url中包括协议名称,iOS根据协议确定调用哪个应用,例如发送短信是"sms://"其中"//"可以省略写成"sms:"(其他协议也是如此)
NSURL *url = [NSURL URLWithString:urlStr];
UIApplication *application = [UIApplication sharedApplication];
if (![application canOpenURL:url]) {
NSLog(@"无法打开\"%@\",请确保此应用已经正确安装.",url);
return;
}
[application openURL:url];
}
@end
调用系统内置的应用来发送短信、邮件相当简单,但是这么操作也存在着一些弊端:当你点击了发送短信(或邮件)操作之后直接启动了系统的短信(或邮件)应用程序,我们的应用其实此时已经处于一种挂起状态,发送完(短信或邮件)之后无法自动回到应用界面。如果想要在应用程序内部完成这些操作则可以利用iOS中的MessageUI.framework,它提供了关于短信和邮件的UI接口供开发者在应用程序内部调用。从框架名称不难看出这是一套UI接口,提供有现成的短信和邮件的编辑界面,开发人员只需要通过编程的方式给短信和邮件控制器设置对应的参数即可。
控件布局:
实现代码:
#import "ViewController.h"
#import
@interface ViewController () <MFMessageComposeViewControllerDelegate, UITextFieldDelegate>
@property (weak, nonatomic) IBOutlet UITextField *receivers;//收件人
@property (weak, nonatomic) IBOutlet UITextField *body;//内容
@property (weak, nonatomic) IBOutlet UITextField *subject;//主题
@property (weak, nonatomic) IBOutlet UITextField *attachments;//附件
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.subject.delegate = self;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[self.subject resignFirstResponder];
return YES;
}
#pragma mark ** 发送短信
- (IBAction)sendMessageClicked:(id)sender {
//如果能发送文本信息
if([MFMessageComposeViewController canSendText]){
MFMessageComposeViewController *messageController=[[MFMessageComposeViewController alloc]init];
//收件人
messageController.recipients= [self.receivers.text componentsSeparatedByString:@","];
//信息正文
messageController.body=self.body.text;
//设置代理,注意这里不是delegate而是messageComposeDelegate
messageController.messageComposeDelegate= self;
//如果运行商支持主题
if([MFMessageComposeViewController canSendSubject]){
messageController.subject=self.subject.text;
}
//如果运行商支持附件
if ([MFMessageComposeViewController canSendAttachments]) {
NSArray *attachments= [self.attachments.text componentsSeparatedByString:@","];
if (attachments.count>0) {
[attachments enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString *path=[[NSBundle mainBundle] pathForResource:obj ofType:nil];
NSURL *url=[NSURL fileURLWithPath:path];
[messageController addAttachmentURL:url withAlternateFilename:obj];
}];
}
}
[self presentViewController:messageController animated:YES completion:^{
}];
}
else
{
NSLog(@"无法发送短信");
}
}
#pragma mark - MFMessageComposeViewController代理方法
//发送完成,不管成功与否
-(void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result{
NSString *msg;
switch (result) {
case MessageComposeResultCancelled:
msg = @"短信发送取消";
[self alertWithTitle:nil msg:msg];
break;
case MessageComposeResultSent:
msg = @"短信发送成功";
[self alertWithTitle:nil msg:msg];
break;
case MessageComposeResultFailed:
msg = @"短信发送失败";
[self alertWithTitle:nil msg:msg];
break;
default:
break;
}
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark ** 提示框
- (void)alertWithTitle:(NSString *)titile msg:(NSString *)msg
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:titile message:msg delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
[alert show];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
需要注意的是:
1. MFMessageComposeViewController的代理不是通过delegate属性指定的而是通过messageComposeDelegate指定的。
2. 发送的附件请务必指定文件的后缀,否则在发送后无法正确识别文件类别(例如如果发送的是一张jpg图片,在发送后无法正确查看图片)。
3. 无论发送成功与否代理方法-(void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result都会执行,通过代理参数中的result来获得发送状态。
其实只要熟悉了MFMessageComposeViewController之后,那么用于发送邮件的MFMailComposeViewController用法和步骤完全一致,只是功能不同。下面看一下MFMailComposeViewController的使用:
控件布局:
实现代码:
#import "ViewController.h"
#import
@interface ViewController () <MFMailComposeViewControllerDelegate, UITextFieldDelegate>
@property (weak, nonatomic) IBOutlet UITextField *toTecipients;//收件人
@property (weak, nonatomic) IBOutlet UITextField *ccRecipients;//抄送人
@property (weak, nonatomic) IBOutlet UITextField *bccRecipients;//密送人
@property (weak, nonatomic) IBOutlet UITextField *subject;//主题
@property (weak, nonatomic) IBOutlet UITextField *body;//正文
@property (weak, nonatomic) IBOutlet UITextField *attachments;//附件
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.toTecipients.delegate = self;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[self.toTecipients resignFirstResponder];
return YES;
}
- (IBAction)sendEmailClick:(id)sender {
Class mailClass = (NSClassFromString(@"MFMailComposeViewController"));
if (mailClass != nil) {
//判断当前是否能够发送邮件
if ([MFMailComposeViewController canSendMail]) {
MFMailComposeViewController *mailController=[[MFMailComposeViewController alloc]init];
//设置代理,注意这里不是delegate,而是mailComposeDelegate
mailController.mailComposeDelegate=self;
//设置收件人
[mailController setToRecipients:[self.toTecipients.text componentsSeparatedByString:@","]];
//设置抄送人
if (self.ccRecipients.text.length>0) {
[mailController setCcRecipients:[self.ccRecipients.text componentsSeparatedByString:@","]];
}
//设置密送人
if (self.bccRecipients.text.length>0) {
[mailController setBccRecipients:[self.bccRecipients.text componentsSeparatedByString:@","]];
}
//设置主题
[mailController setSubject:self.subject.text];
//设置内容
[mailController setMessageBody:self.body.text isHTML:YES];
//添加附件
if (self.attachments.text.length>0) {
NSArray *attachments=[self.attachments.text componentsSeparatedByString:@","] ;
[attachments enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString *file=[[NSBundle mainBundle] pathForResource:obj ofType:nil];
NSData *data=[NSData dataWithContentsOfFile:file];
[mailController addAttachmentData:data mimeType:@"image/jpeg" fileName:obj];//第二个参数是mimeType类型,jpg图片对应image/jpeg
}];
}
[self presentViewController:mailController animated:YES completion:nil];
}
else
{
NSLog(@"无法发送邮件");
[self launchMailAppOnDevice];
}
}
else
{
[self launchMailAppOnDevice];
}
}
- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error
{
NSString *msg;
switch (result) {
case MFMailComposeResultCancelled:
msg = @"邮件发送取消";
[self alertWithTitle:nil msg:msg];
break;
case MFMailComposeResultSaved:
msg = @"邮件保存成功";
[self alertWithTitle:nil msg:msg];
break;
case MFMailComposeResultSent:
msg = @"邮件发送成功";
[self alertWithTitle:nil msg:msg];
break;
case MFMailComposeResultFailed:
msg = @"邮件发送失败";
[self alertWithTitle:nil msg:msg];
break;
default:
break;
}
[self dismissViewControllerAnimated:YES completion:^{
}];
}
#pragma mark ** 登陆设备上的应用
- (void)launchMailAppOnDevice
{
NSString *recipients = @"mailto:[email protected]&subject=my email";
NSString *body = @"&body=email body";
NSString *email = [NSString stringWithFormat:@"%@%@",recipients,body];
email = [email stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:email]];
}
- (void)alertWithTitle:(NSString *)titile msg:(NSString *)msg
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:titile message:msg delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
[alert show];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
iOS中带有一个Contacts应用程序来管理联系人,但是有些时候我们希望自己的应用能够访问或者修改这些信息,这个时候就要用到AddressBook.framework框架。iOS中的通讯录是存储在数据库中的,由于iOS的权限设计,开发人员是不允许直接访问通讯录数据库的,必须依靠AddressBook提供的标准API来实现通讯录操作。通过AddressBook.framework开发者可以从底层去操作AddressBook.framework的所有信息,但是需要注意的是这个框架是基于C语言编写的,无法使用ARC来管理内存,开发者需要自己管理内存。下面大致介绍一下通讯录操作中常用的类型:
ABAddressBookRef:代表通讯录对象,通过该对象开发人员不用过多的关注通讯录的存储方式,可以直接以透明的方式去访问、保存(在使用AddressBook.framework操作联系人时,所有的增加、删除、修改后都必须执行保存操作,类似于Core Data)等。
ABRecordRef:代表一个通用的记录对象,可以是一条联系人信息,也可以是一个群组,可以通过ABRecordGetRecordType()函数获得具体类型。如果作为联系人(事实上也经常使用它作为联系人),那么这个记录记录了一个完整的联系人信息(姓名、性别、电话、邮件等),每条记录都有一个唯一的ID标示这条记录(可以通过ABRecordGetRecordID()函数获得)。
ABPersonRef:代表联系人信息,很少直接使用,实际开发过程中通常会使用类型为“kABPersonType”的ABRecordRef来表示联系人(由此可见ABPersonRef其实是一种类型为“kABPersonType”的ABRecordRef)
ABGroupRef:代表群组,与ABPersonRef类似,很少直接使用ABGroupRef,而是使用类型为“kABGroupType”的ABRecordRef来表示群组,一个群组可以包含多个联系人,一个联系人也同样可以多个群组。
由于通讯录操作的关键是对ABRecordRef的操作,首先看一下常用的操作通讯录记录的方法:
ABPersonCreate():创建一个类型为“kABPersonType”的ABRecordRef。
ABRecordCopyValue():取得指定属性的值。
ABRecordCopyCompositeName():取得联系人(或群组)的复合信息(对于联系人则包括:姓、名、公司等信息,对于群组则返回组名称)。
ABRecordSetValue():设置ABRecordRef的属性值。注意在设置ABRecordRef的值时又分为单值属性和多值属性:单值属性设置只要通过ABRecordSetValue()方法指定属性名和值即可;多值属性则要先通过创建一个ABMutableMultiValueRef类型的变量,然后通过ABMultiValueAddValueAndLabel()方法依次添加属性值,最后通过ABRecordSetValue()方法将ABMutableMultiValueRef类型的变量设置为记录值。
ABRecordRemoveValue():删除指定的属性值。
代码实现:
创建一个People类,用来存放联系人信息
#import
@interface MFJPeople : NSObject
@property (nonatomic, assign) NSInteger recordID;
@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSString *email;
@property (nonatomic, retain) NSString *tel;
@end
获取联系人信息
#import "MFJAddressBookViewController.h"
#import
#import "MFJPeople.h"
@interface MFJAddressBookViewController () <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, retain) NSMutableArray *addressBook;
@property (nonatomic, retain) UITableView *addressBookTableView;
@end
@implementation MFJAddressBookViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
[self.view addSubview:button];
button.frame = CGRectMake(20, 30, 50, 30);
[button setTitle:@"返回" forState:UIControlStateNormal];
[button addTarget:self action:@selector(backClicked:) forControlEvents:UIControlEventTouchUpInside];
self.addressBook = [NSMutableArray array];
ABAddressBookRef address = nil;
address = ABAddressBookCreateWithOptions(nil, nil);
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
ABAddressBookRequestAccessWithCompletion(address, ^(bool granted, CFErrorRef error) {
dispatch_semaphore_signal(sema);
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(address);
CFIndex numPeople = ABAddressBookGetPersonCount(address);
for (int i = 0; i < numPeople; i++) {
MFJPeople *people = [[MFJPeople alloc] init];
ABRecordRef person = CFArrayGetValueAtIndex(allPeople, i);
//获取名字
CFTypeRef name = ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSString *nameString = (__bridge NSString *)name;
//获取电话号码
ABMultiValueRef telePhone = ABRecordCopyValue(person, kABPersonPhoneProperty);
NSString *telePhoneString = (__bridge NSString *)ABMultiValueCopyValueAtIndex(telePhone, 0);
people.tel = telePhoneString;
people.name = nameString;
//获取唯一标识
people.recordID = (int)ABRecordGetRecordID(person);
[self.addressBook addObject:people];
}
[self tableViewSubview];
}
#pragma mark ** tableView布局
- (void)tableViewSubview
{
self.addressBookTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) style:UITableViewStyleGrouped];
[self.view addSubview:self.addressBookTableView];
self.addressBookTableView.delegate = self;
self.addressBookTableView.dataSource = self;
[self.addressBookTableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"reuse"];
}
#pragma mark ** tableView协议方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.addressBook.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"reuse"];
MFJPeople *people = [self.addressBook objectAtIndex:indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:@"联系人:%@, %@",people.name,people.tel];
return cell;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
不难发现当openURL:方法只要指定一个URL Schame并且已经安装了对应的应用程序就可以打开此应用。当然,如果是自己开发的应用也可以调用openURL方法来打开。假设你现在开发了一个应用A,如果用户机器上已经安装了此应用,并且在应用B中希望能够直接打开A。那么首先需要确保应用A已经配置了Url Types,具体方法就是在plist文件中添加URL types节点并配置URL Schemas作为具体协议,配置URL identifier作为这个URL的唯一标识,如下图
//打开第三方应用
- (IBAction)thirdPartyApplicationClick:(id)sender
{
NSString *url = @"LoveTour://myparams";
[self openUrl:url];
}
//就像调用系统应用一样,协议后面可以传递一些参数(例如上面传递的myparams),这样一来在应用中可以在AppDelegate的代理方法中接收参数并解析。
-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{
NSString *str=[NSString stringWithFormat:@"url:%@,source application:%@,params:%@",url,sourceApplication,[url host]];
NSLog(@"%@",str);
return YES;//是否打开
}