第五章:管理手机

一、使用AddressBook管理联系人

当我们的应用也需要访问甚至修改设备里的联系人信息,就需要借助于ABAddressBook或ABAddressBookUI来管理联系人信息,其中ABAddressBook只提供了一些工具函数来访问、修改联系人信息,而ABAddressBookUI则直接提供了一些试图控制器来访问、修改联系人信息.除此之外,系统还内置了打电话、发短信、发邮件的应用,这些应用为iPhone手机提供了最基本的功能.

iPhone手机通常都有一个自带的Contacts应用,该应用用于管理用户的联系人信息,包括所有联系人的姓(last name)、名(first name)、电话、E-mail地址、住址、生日等个人信息.通常来说Contacts应用管理的联系人数据将保存在自己的应用程序沙盒中,其他应用不能访问他们,但对于某些iOS应用而言,程序需要访问这些数据–比如开发一个短信群发应用,该应用就需要让用户从手机自带的联系人中进行选择.

iOS系统提供了如下两个框架:

  • 1、AddressBook.framework:通过该框架提供的系列函数,开发者可以开发程序界面对手机中的联系人信息进行增、删、改、查.
  • 2、AddressBookUI.framework:该框架以AddressBook.framework为基础,它直接包含4个视图控制器类以及相应的委托协议.这些特殊的视图控制器提供了默认的用户界面对手机中的联系人信息进行操作,开发者只要创建这些视图控制器的实例并显示出来即可.

AddressBook.framework的用法,AddressBook框架主要由于如下4组API组成:

  • (1)、ABAddressBook:ABAddressBook实例代表地址簿对象,该对象提供了一个通用编程接口,允许开发者无需理会联系人信息在底层数据库中的存储方式,直接以透明的方式来访问这些联系人信息.通讯录中包含一个或多个联系人,也包含一个过多个组,一个联系人可以属于多个组,一个组也可以包含多个联系人.实际编程时,总是面向ABAddressBook的指针(ABAddressBookRef)编程。

    ABAddressBook框架并非面向对象的,它是一种面向过程的操作方式,该框架为4组API提供了大量函数。常用函数如下:

(1)、ABAddressBookRef ABAddressBookCreateWithOptions(CFDictionaryRef options, CFErrorRef* error);//该函数创建一个地址簿对象。
(2)、ABAuthorizationStatus ABAddressBookGetAuthorizationStatus(void);//返回该iOS应用访问地址簿的授权状态。

提示:由于手机内的联系人信息属于隐私信息,如果iOS应用尝试访问这些联系人信息,必须得到用户的授权——系统会弹出一个提示框,提醒用户是否允许访问。

(3)、void ABAddressBookRequestAccessWithCompletion(ABAddressBookRef addressBook,  ABAddressBookRequestAccessCompletionHandler completion);//该函数用于向地址簿发送访问请求,如果用户同意或拒接将会执行completion代码块。
(4)、bool ABAddressBookHasUnsavedChanges(ABAddressBookRef addressBook);//该函数用于哦按段addressBook是否包含未保存的修改。
(5)、bool ABAddressBookSave(ABAddressBookRef addressBook, CFErrorRef* error);//该函数用于保存addressBook中未保存的修改。
(6)、void ABAddressBookRevert(ABAddressBookRef addressBook);//该函数用于丢弃addressBook中未保存的修改。
(7)、bool ABAddressBookAddRecord(ABAddressBookRef addressBook, ABRecordRef record, CFErrorRef* error);//想addressBook中添加record记录。
(8)、bool ABAddressBookRemoveRecord(ABAddressBookRef addressBook, ABRecordRef record, CFErrorRef* error);//从addressBook中删除record记录。
(9)、void ABAddressBookRegisterExternalChangeCallback(ABAddressBookRef addressBook, ABExternalChangeCallback callback, void *context);//为addressBook注册一个监听改变的代码块,当地址簿被外部程序改变时将会自动执行callback代码块。
(10)、void ABAddressBookUnregisterExternalChangeCallback(ABAddressBookRef addressBook, ABExternalChangeCallback callback, void *context);//从addressBook上删除已经注册的监听改变的代码块。

作用:从上面的函数可以看出,ABAddressBook的函数只负责对地址簿进行管理,包括创建地址簿、向地址簿中提那家记录、从地址簿中删除记录等。除此之外,还可以判断用户是否允许访问地址簿,以及对地址簿的修改进行保存或丢弃修改。

  • (2)、ABRecord:ABRecord代表一个通用的记录对象,该记录对象包含了大量的通用数据(如联系人的姓(last name)、名(first name)、电话、邮件等)。每条记录在底层数据库中都有一个唯一的ID,开发者可通过ABRecordGetRecordID()函数获取指定记录的ID。不管是联系人信息还是组信息,都被当成ABRecord处理,开发者可通过ABRecordGetRecordType()函数获取该记录类型,如果该记录是联系人,则该函数返回kABPersonType(0)枚举值;如果该记录是组,则该函数返回kABGroupType(1)枚举值,。实际编程时,总是面向ABRecord的指针(ABRecordRef)编程。

    为了向地址簿中添加记录,或从地址簿中删除记录,都需要使用ABRecoredRef,ABRecord所提供的函数主要用于访问ABRecord本身所包含的大量属性。

    提示:每个ABRecord由多个属性组成,联系人的姓、名、电话、邮件等都属于ABRecord的属性。

    ABRecord提供了如下的常用函数:

(1)、bool ABRecordSetValue(ABRecordRef record, ABPropertyID property, CFTypeRef value, CFErrorRef* error);//将record记录中的property属性设为“value”。其中property必须是由ABPerson、ABGroup定义的常量,用于表示预置的各种属性,而且不同属性需要的属性值不同。比如联系人的名字,对应于kABPersonFirstNameProperty常量,这种类型的属性只需要字符串值即可;如果是联系人的电话,则对应于kABPersonPhoneProperty常量,这种类型的属性则需要ABMutableMultiValue值。

(2)、CFTypeRef ABRecordCopyValue(ABRecordRef record, ABPropertyID property);//该函数返回record中的property属性的值。
(3)、bool ABRecordRemoveValue(ABRecordRef record, ABPropertyID property, CFErrorRef* error);//该函数删除record记录中property属性的值,返回是否删除成功。
(4)、CFStringRef ABRecordCopyCompositeName(ABRecordRef record);//该函数返回record记录中复合姓名信息(包括姓、名、组织等信息)。
(5)、ABRecordID ABRecordGetRecordID(ABRecordRef record);//该函数用于获取该记录的ID。
(6)、ABRecordType ABRecordGetRecordType(ABRecordRef record);//该函数用于获取该记录的类型。

作用:ABRecord的函数主要用于操作该记录所包含的属性,如果程序要创建或得到已有的记录,则需要借组与ABPerson、ABGroup提供的函数。

另外:ABMutableMultiValue相当于一个集合,每个ABMutableMultiValue可以管理过个电话号码、多个电子邮件或多个住址等。当程序把过个电话号码、多个电子邮件或多个住址添加到ABMutableMultiValue中之后,再将该ABMutableMultiValue设置为ABRecord的一个属性,这样就可以为该记录对应的联系人设置多个电话号码、多个电子邮件、多个住址。

 ABMutableMultiValue提供了如下常用属性。
(1)、bool ABMutableMultiValueRef ABMultiValueCreateMutable(ABPropertyType type);//创建一个管理ANPropertyType类型的属性值的ABMutableMultiValue
(2)、bool ABMultiValueAddValueAndLabel(ABMutableMultiValueRef multiValue, CFTypeRef value, CFStringRef label, ABMultiValueIdentifier *outIdentifier);//向multiValue中添加一个属性值。value代表属性值,label代表属性值的标签(比如电话号码,可设置“家庭”、“工作”等label);
(3)、bool ABMultiValueReplaceValueAtIndex(ABMutableMultiValueRef multiValue, CFTypeRef value, CFIndex index);//将multiValue中index索引处的属性值替换为新的value。
(4)、bool ABMultiValueReplaceLabelAtIndex(ABMutableMultiValueRef multiValue, CFStringRef label, CFIndex index);//将multiValue中index索引处的属性标签替换为新的label。
(5)、bool ABMultiValueInsertValueAndLabelAtIndex(ABMutableMultiValueRef multiValue, CFTypeRef value, CFStringRef label, CFIndex index, ABMultiValueIdentifier *outIdentifier);//想multiValve的index索引处插入一个属性值。value代表属性值,label代表属性值的标签。
(6)、bool ABMultiValueRemoveValueAndLabelAtIndex(ABMutableMultiValueRef multiValue, CFIndex index);//删除multiValue中index索引处的属性值(同时删除属性值和属性值的标签)。
  • (3)、ABPerson:ABPerson代表联系人信息。实际编程时,通常使用类型“kABPersonType”的ABRecordRef表示联系人信息。联系人信息存储了该联系人的姓、名、地址、电子邮件和电话号码等。联系人信息并非一定要存储在通讯录数据库中,开发者也可使用视图控制器(如ABPersonViewController)显示这些联系人信息。

    作用:ABPerson提供了大量获取类型为“kABPersonType”的ABRecord的函数,这些函数即可创建一个空的ABRecord,用于向地址簿中添加新的记录,也可从地址簿中获取满足特定条件的ABRecord,包含根据ID获取、根据人名获取等,这些从地址簿中获取的ABRecord可以被删除或修改。

  • (4)、ABGroup:ABGroup代表组。实际编程时,通常使用类型为“kABGroupType”的ABRecordRef表示组。一个联系人可以属于多个组,一个组也可以包含多个联系人。

    作用:ABGroup提供了大量获取类型为“kABGroupType”的ABRecord的函数,ABGroup包含的函数与ABPerson包含的函数大致类似,同样即可创建新的ABRecord,也可从数据库加载ABRecord。

1、删除联系人

删除联系人,只需要如下几步

  • (1)、创建ABAddressBookRef,这就得到了对地址簿的引用;
  • (2)、获取将要被删除的ABRecordRef,这就得到了要被删除记录的引用;
  • (3)、调用ABAddressBookRemoveRecord()函数删除指定记录;
  • (4)、调用ABAddressBookSave()函数将删除操作保存到底层地址簿中。
- (IBAction)deletePerson:(id)sender {
    CFErrorRef error = nil;
    //创建ABAddressBook,该函数的第一个参数暂时并未使用,直接传入NULL即可
    ABAddressBookRef ab = ABAddressBookCreateWithOptions(NULL, &error);
    if (!error) {
        //请求访问用户地址簿
        ABAddressBookRequestAccessWithCompletion(ab, ^(bool granted, CFErrorRef error) {
            //如果用户允许访问地址簿
            if (granted) {
                //从地址簿中获取ID为3的记录
                ABRecordRef rec = ABAddressBookGetPersonWithRecordID(ab, 3);
                NSLog(@"%@",rec);
                BOOL result = ABAddressBookRemoveRecord(ab, rec, NULL);
                if (result) {
                    //从程序锁做的修改保存包至底部中,如果保存成功
                    if (ABAddressBookSave(ab, NULL)){
                        [self showAlert:@"成功删除ID为3的联系人"];
                    }else{
                        [self showAlert:@"保存修改时出现错误"];
                    }
                }else{
                    [self showAlert:@"删除失败"];
                }
            }
        });
    }
}

2、添加联系人

添加联系人,需要如下步骤:

  • (1)、创建ABAddressBookRef,这就得到了对地址簿的引用;
  • (2)、调用ABPersonCreate()函数创建一个空的ABRecordRef,这就得到了一条孔的联系人记录;
  • (3)、根据需要为ABRecordRef设置属性值。如果只是设置姓氏、名字等简单信息,则直接调用ABRecordSetValue()函数为ABRecordRef的指定属性设置属性值即可。如果是设置电话号码、电子邮件等可以指定label的属性,则需要先创建一个ABMutableMultiValueRef,然后调用ABRecordSetValueAndLabel()函数向ABMutableMultiValueRef中添加多个值,最后才能调用ABRecordSetValue()函数为ABRecordRef的指定属性设置属性值。
  • (4)、调用ABAddressBookValueAndLabel()函数将制定ABRecordRef记录添加到地址簿中。
  • (5)、调用ABAddressBookSave()函数将删除操作保存到底层地址地址簿中。
- (IBAction)add:(id)sender {
    NSString* firstName = self.firstnameField.text;
    NSString* lastName = self.lastnameField.text;
    NSString* homePhone = self.homePhoneField.text;
    NSString* mobilePhone = self.mobilePhoneField.text;
    NSString* workMail = self.workMailField.text;
    NSString* privateMail = self.privateMailField.text;
    NSString* country = self.countryField.text;
    NSString* state = self.stateField.text;
    CFErrorRef error = nil;
    //创建ABAddressBook,该函数第一个参数暂时并未使用,直接传入NULL即可。
    ABAddressBookRef ab = ABAddressBookCreateWithOptions(NULL, &error);
    if (!error) {
        //请求访问用户地址簿
        ABAddressBookRequestAccessWithCompletion(ab, ^(bool granted, CFErrorRef error) {
            //如果访问用户地址簿
            if (granted) {
                //创建一条新的记录
                ABRecordRef rec = ABPersonCreate();
                //为rec的kABPersonFristNameProperty(名字)属性设置值
                ABRecordSetValue(rec, kABPersonFirstNameProperty, (__bridge CFStringRef)firstName, NULL);
                //为rec的kABPersonFristNameProperty(姓氏)属性设置值
                ABRecordSetValue(rec, kABPersonLastNameProperty, (__bridge CFStringRef)lastName, NULL);
                //创建ANMutableMultiValueRef用来管理过个电话号码
                ABMutableMultiValueRef phoneValue = ABMultiValueCreateMutable(kABPersonPhoneProperty);
                //添加label为家庭的电话号码
                ABMultiValueAddValueAndLabel(phoneValue, (__bridge CFStringRef)homePhone, kABHomeLabel, NULL);
                // 添加label为移动的电话号码
                ABMultiValueAddValueAndLabel(phoneValue, (__bridge CFTypeRef)mobilePhone, kABPersonPhoneMobileLabel , NULL);
                //为rec的kABPersonPhoneProperty(电话)属性设置值
                ABRecordSetValue(rec, kABPersonPhoneProperty, phoneValue, NULL);
                //创建ABMutableMultiValueRef用来管理多个电子邮件
                ABMutableMultiValueRef mailValue = ABMultiValueCreateMutable(kABPersonEmailProperty);
                //添加label为工作的电子邮件
                ABMultiValueAddValueAndLabel(mailValue, (__bridge CFStringRef)workMail, (__bridge CFStringRef)@"工作", NULL);
                //添加label为私人的电子邮件
                ABMultiValueAddValueAndLabel(mailValue, (__bridge CFStringRef)privateMail, (__bridge CFStringRef)@"死人", NULL);
                //为rec的kABPersonEmailProperty(电子邮件)属性设置值
                ABRecordSetValue(rec, kABPersonEmailProperty, mailValue, NULL);
                //创建ANMutableMultiValueRef用来管理多个地址
                ABMutableMultiValueRef addressValue = ABMultiValueCreateMutable(kABPersonAddressProperty);
                NSDictionary* addressDict = [NSDictionary dictionaryWithObjectsAndKeys:country,kABPersonAddressCountryCodeKey,state,kABPersonAddressStateKey, nil];
                //添加label为住址的地址
                ABMultiValueAddValueAndLabel(addressValue, (__bridge CFTypeRef)addressDict, (__bridge CFStringRef)@"住址", NULL);
                //为rec的kABPersonAddressProperty(地址)属性设置值
                ABRecordSetValue(rec, kABPersonAddressProperty, addressValue, NULL);
                BOOL result = ABAddressBookAddRecord(ab, rec, NULL);
                if (result) {
                    //将程序所做的修改保存到地址簿中,如果保存成功
                    if (ABAddressBookSave(ab, NULL)) {
                        [self showAlert:@"成功添加新的联系人"];
                    }else{
                        [self showAlert:@"保存添加操作出现错误"];
                    }
                }else{
                    [self showAlert:@"添加失败"];
                }
            }
        });
    }
}

3、修改联系人

实际上,修改联系人与添加联系人大致相似,只是修改联系人无需创建新的ABRecordRef,而是先从底层地址簿中加载一条ABPercordRef记录,然后对这条ABRecordRef记录的属性值进行修改,修改完成后把这条修改后的ABRecordRef记录存入地址簿即可。

修改联系人步骤如下:

  • (1)、获取底层地址簿中已有的联系人对应的ABRecordRef记录。
  • (2)、根据需要修改的属性,调用ABRecordSetValue()函数修改ABRecordRef记录中制定属性的值。
  • (3)、修改完成后调用ABAddressBookSave()函数将刚刚所做的修改保存到底层地址簿。

例如:

#import "FKUpdatePersonViewController.h"
#import <AddressBook/AddressBook.h>
#define PHONE_PROPERTY_KEY @"phone"
#define MAIL_PERPERTY_KEY @"mail"
@interface FKUpdatePersonViewController ()
{
    ABAddressBookRef ab;
    //定义ABRecordRef类型的变量保存当前正在更新的记录
    ABRecordRef rec;
    //使用该变量定义当前动态添加的行的Y坐标
    NSInteger curLineY;
    //定义一个NSMutableDictionary来保存所有动态添加的文本框
    NSMutableDictionary* textFields;
    //定义ABMutableMultiValueRef变量记录正在修改的电话号码属性值
    ABMutableMultiValueRef phoneValue;
    //定义ABMutableMultiValueRef变量记录正在修改的电子邮件属性值
    ABMutableMultiValueRef mailValue;
}
@end

@implementation FKUpdatePersonViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    textFields = [NSMutableDictionary dictionary];
    curLineY = 150;
    CFErrorRef error = nil;
    //创建ABAddressBook,该函数的第一个参数暂时并未使用,直接创入NULL即可
    ab = ABAddressBookCreateWithOptions(NULL, &error);
    if (!error) {
        //请求访问用户地址簿
        ABAddressBookRequestAccessWithCompletion(ab, ^(bool granted, CFErrorRef error) {
            //获取哦ID为1的ABRecordRef记录
            rec = ABAddressBookGetPersonWithRecordID(ab, 1);
            //获取rec记录中kABPersonFirstNameProperty(名字)属性的值
            NSString* firstName = (__bridge NSString*)ABRecordCopyValue(rec, kABPersonFirstNameProperty);
            //获取rec记录中kABPersonLastNameProperty
            NSString* lastName = (__bridge NSString*)ABRecordCopyValue(rec, kABPersonLastNameProperty);
            //控制界面上显示文本框系那是rec记录中姓氏、名字的属性值
            self.firstnameField.text = firstName;
            self.lastnameField.text = lastName;
            //获取rec记录中kABPersonPhoneProperty(电话号码)属性的值
            phoneValue = ABRecordCopyValue(rec, kABPersonPhoneProperty);
            //调用addLabelAndTextField:propertyKey:显示电话号码
            [self addLabelAndTextField:phoneValue propertyKey:PHONE_PROPERTY_KEY];
            mailValue = ABRecordCopyValue(rec, kABPersonEmailProperty);
            //调用addLabelAndTextField:propertyKey:显示电子邮件
            [self addLabelAndTextField:mailValue propertyKey:MAIL_PERPERTY_KEY];
        });
    }
}
- (void) addLabelAndTextField:(ABMutableMultiValueRef) multiValue
  propertyKey:(NSString*)property
{
    // 获取multiValue包含的数据条数
    NSInteger num = ABMultiValueGetCount(multiValue);
    NSMutableArray* textFieldArray = [NSMutableArray array];
    // 依次遍历multiValue所包含的每条数据
    for (int i = 0 ; i < num ; i++){
        curLineY += 38;
        // 创建UILabel,显示本条数据的Label
        UILabel* label = [[UILabel alloc] initWithFrame: CGRectMake(20 , curLineY , 70, 30)];
        NSString* labelStr = (__bridge NSString*)ABAddressBookCopyLocalizedLabel
(ABMultiValueCopyLabelAtIndex(multiValue , i));
        label.text = labelStr;
        // 将该UILabel添加到程序界面上
        [self.view performSelectorOnMainThread:@selector(addSubview:) withObject:label waitUntilDone:YES];
        // 创建UITextField,显示本条数据的Value
        UITextField* textField = [[UITextField alloc] initWithFrame:CGRectMake (98 , curLineY , 202, 30)];
textField.borderStyle = UITextBorderStyleRoundedRect;
        NSString* valueStr = (__bridge NSString*)ABMultiValueCopyValueAtIndex
(multiValue , i);
        textField.text = valueStr;
        [textField addTarget:self action:@selector(finishEdit:) forControlEvents:UIControlEventEditingDidEndOnExit];
        // 使用NSArray集合来保存动态创建的UITextField控件
        [textFieldArray addObject:textField];
        // 将UITextField添加到程序界面上
        [self.view performSelectorOnMainThread:@selector(addSubview:) withObject:textField waitUntilDone:YES];
    }
    // 将程序动态生成的所有textField保存到NSMutableDictionary中
    [textFields setValue:textFieldArray forKey:property];
}
- (IBAction)update:(id)sender {
    NSString* firstName = self.firstnameField.text;
    NSString* lastName = self.lastnameField.text;
    //修改rec记录中kABPersonFirstNameProperty、kABPersonLastNameProperty属性
    ABRecordSetValue(rec, kABPersonFirstNameProperty, (__bridge CFStringRef)firstName, NULL);
    ABRecordSetValue(rec, kABPersonLastNameProperty, (__bridge CFStringRef)lastName, NULL);
    //调用updateMultivalue:properyKey:property:方法修改ABRecordRef的kABPersonPhoneProperty属性

    [self updateMultiValue:phoneValue properyKey:PHONE_PROPERTY_KEY property:kABPersonPhoneProperty];
    //调用updateMultivalue:properyKey:property:方法修改ABRecordRef的kABPersonEmailProperty属性
    [self updateMultiValue:mailValue properyKey:MAIL_PERPERTY_KEY property:kABPersonEmailProperty];
    if (ABAddressBookSave(ab, NULL)) {
        [self showAlert:@"修改成功"];
    }else{
        [self showAlert:@"修改出现错误"];
    }
}
- (void)updateMultiValue:(ABMutableMultiValueRef)multiValue properyKey:(NSString*) properyKey property:(ABPropertyID) property{
    //取出该属性对应的所有UITextView组成的NSArray
    NSArray* textFieldArray = textFields[properyKey];
    NSInteger num = textFieldArray.count;
    //创建一个新的ABMutableMultiValueRef
    ABMutableMultiValueRef newMutli = ABMultiValueCreateMutable(property);
    //遍历UITextView组成的NSArray集合中每个UITextField
    for(int i = 0; i < num;i ++){
        //获取第i哥UITextField控件中的字符串,该字符串作为新的值
        NSString* value = ((UITextField*)textFieldArray[i]).text;
        //获取第i条数据原有的label
CFStringRef label = ABMultiValueCopyLabelAtIndex(multiValue, 0);
        //添加新的值和原有的label(Label不需要修改)
ABMultiValueAddValueAndLabel(newMutli, (__bridge CFStringRef)value, label, NULL);
    }
    ABRecordSetValue(rec, property, newMutli, NULL);
}
- (IBAction)finishEdit:(id)sender {
    [sender resignFirstResponder];
}
- (void) showAlert:(NSString*) msg{
    // 使用UIAlertView显示msg信息
    [[[UIAlertView alloc] initWithTitle:@"提示" message:msg delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil] show];
}
@end

二、使用AddressBookUI管理联系人

iOS SDK为管理地址簿提供的师徒控制器位于AddressBookUI框架内。总结来说,AddressBoolUI框架提供了如下特殊的视图控制器:

  • ABPersonViewController:用于显示指定联系人数据的视图控制器;
  • ABNewPersonViewController:用于新增联系人的视图控制器;
  • ABPeoplePickerViewController:用于让用户选择某个联系人或选择某个联系人信息项的视图控制器;
  • ABUnknownPersonViewController:用于通过一组联系人信息来添加联系人记录。程序可以将视图控制器显示的联系人信息存入手机通讯录中。实际上,该视图控制器可以对联系人数据启动这些标准动作:手机通话、发送短信、新增联系人、添加到已有联系人中。

这四个视图控制器的使用方式基本相同,都是先穿件视图控制器的实例,并设置相关属性——关键是指定一个xxxDelegate属性,该属性是一个实现特定协议的对象,该协议中定义的方法会负责用户对视图控制器执行的操作。

4个视图控制器对应的Delegate协议如下:

(1)、ABPersonViewControllerDelegate:该协议包含一个必须实现的 - (BOOL)personViewController:(ABPersonViewController *)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier;方法,当用户选中某个联系人的某个属性时激发该方法。如果希望用户选择该属性时自动执行它的默认动作,该方法应该返回YES;否则返回NO;

(2)、ABNewPersonViewControllerDelegate:该协议包含一个必须实现的 - (void)newPersonViewController:(ABNewPersonViewController *)newPersonView didCompleteWithNewPerson:(ABRecordRef)person;方法,当用户单击该视图控制器的Save或Cancel按钮时激发该方法。如果用户点击该视图控制器的Save按钮,那么用户输入的联系人信息将会保存到地址簿的数据库中;

(3)、 ABPeoplePickerNavigationControllerDelegate:该协议包含如下3个必须实现的方法。

①、

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person NS_DEPRECATED_IOS(2_0, 8_0);

当用户通过该协议所对应的视图控制器选中某个联系人后激发该方法。如果希望用户选择该联系人后自动执行默认的动作,该方法应该返回YES;否则返回NO;

②、

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier NS_DEPRECATED_IOS(2_0, 8_0);

当用户选中某个联系人的某个属性后激发该方法。如果希望用户选择该联系人的某个属性后自动执行默认的动作,该方法应该返回YES;否则返回NO;

③、

- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker;

当用户取消选择时激发该方法;

(4)、ABUnknownPersonViewControllerDelegate:该协议包含一个必须实现的 - (void)unknownPersonViewController:(ABUnknownPersonViewController *)unknownCardViewController didResolveToPerson:(ABRecordRef)person;方法,当用户将位置联系人数据添加为新的联系人或保存到已有联系人中时激发该方法;

除此之外,这4个视图控制器还可设置如下的常用属性(有些属性不是每个控制器都支持的,具体以API文档为准)。

①、

@property(nonatomic,readwrite) ABRecordRef displayedPerson;

该属性设置或分那会该视图控制器显示的联系人记录。该属性值是一个ANRecordRef变量;

②、

@property(nonatomic,copy) NSArray  *displayedProperties;

该属性设置或返回视图控制器可以显示的所有属性。该属性值是一个NSArray集合,包含程序希望显示的属性。

③、

@property(nonatomic,readwrite) ABAddressBookRef addressBook;

该属性设置会返回该视图控制器关联的地址簿。该属性是一个ABAddressBookRef变量;

④、

@property(nonatomic) BOOL allowsEditing;

该属性设置或返回是否允许编辑联系人信息;

⑤、

@property(nonatomic) BOOL allowsAddingToAddressBook;

该属性设置或返回是否允许将联系人信息添加到地址簿中。

1、使用ABNewPersonViewController添加联系人

添加头文件: #import < AddressBookUI/AddressBookUI.h >

遵循代理: ABNewPersonViewControllerDelegate

- (IBAction)addContact:(id)sender {
    ABNewPersonViewController* controller =[[ABNewPersonViewController alloc]init];
controller.newPersonViewDelegate = self;
    [self.navigationController pushViewController:controller animated:YES];
}
#pragma mark -- ABNewPersonViewControllerDelegate
//ABNewPersonViewControllerDelegate协议中的方法,的那个用户单击“保存”或@“取消”按钮时激发该方法;
- (void)newPersonViewController:(ABNewPersonViewController *)newPersonView didCompleteWithNewPerson:(ABRecordRef)person{
    //如果用户单击“保存”按钮,person代表刚保存的记录;
    //如果用户单击“取消”按钮,person为NULL
    if (person) {
        //取出person记录中kABPersonFirstNameProperty属性的值
        NSString* firstName = (__bridge NSString*)ABRecordCopyValue(person, kABPersonFirstNameProperty);
        [self showAlert:[NSString stringWithFormat:@"[%@]联系人被保存",firstName]];
    }
    //隐藏包装newPersonViewController
    [newPersonView.navigationController popViewControllerAnimated:YES];
}

2、使用ABUnknownPersonViewController显示未知联系人

添加头文件: #import < AddressBookUI/AddressBookUI.h >

遵循代理: ABUnknownPersonViewControllerDelegate

- (IBAction)unknown:(id)sender {
    ABUnknownPersonViewController* controller = [[ABUnknownPersonViewController alloc]init];
    //设置unknownPersonViewDelegate属性为当前视图控制器自身
controller.unknownPersonViewDelegate = self;
    //设置系那是标准动作
    controller.allowsActions = YES;
    //设置允许将该位置的联系人添加到地址簿
    controller.allowsAddingToAddressBook = YES;
    //创建一条新的记录
    ABRecordRef record = ABPersonCreate();
    //为ABRecordRef记录设置kABPersonFirstNameProperty属性
    ABRecordSetValue(record, kABPersonFirstNameProperty, (__bridge CFTypeRef)@"疯狂软件", NULL);
    ABRecordSetValue(record, kABPersonLastNameProperty, (__bridge CFTypeRef)@"fkit.org", NULL);
    //添加联系人电话号码以及该号码对应的标签
    ABMutableMultiValueRef multi = ABMultiValueCreateMutable(kABPersonPhoneProperty);
ABMultiValueAddValueAndLabel(multi, (__bridge CFTypeRef)@"02028309358", (__bridge  CFTypeRef)@"工作", NULL);
    ABMultiValueAddValueAndLabel(multi, (__bridge CFTypeRef)@"13500008888", (__bridge CFTypeRef)@"手机", NULL);
    //为ABRecordRef记录设置kABPersonPhoneProperty属性
ABRecordSetValue(record, kABPersonPhoneProperty, multi, NULL);
    //设置controller显示record记录
    controller.displayedPerson = record;
    [self.navigationController pushViewController:controller animated:nil];
}
#pragma mark -- ABUnknownPersonViewControllerDelegate
//当用户把这条未知联系人信息添加到某个联系人上,或添加到地址簿中时将会激发该方法
- (void)unknownPersonViewController:(ABUnknownPersonViewController *)unknownCardViewController didResolveToPerson:(ABRecordRef)person{
    //person代表用户刚刚保存的记录
    if (person) {
        //取出person记录中kABPersonFirstNameProperty属性的值
        NSString* firstName = (__bridge NSString*)ABRecordCopyValue(person, kABPersonFirstNameProperty);
        [self showAlert:[NSString stringWithFormat:@"[%@]联系人被保存",firstName]];
    }
    //隐藏包装unknownPersonView的导航控制器
    [unknownCardViewController.navigationController popViewControllerAnimated:YES];
}

3、使用ABPeoplePickerNavigationController选择联系人

添加头文件: #import < AddressBookUI/AddressBookUI.h >

遵循代理: ABPeoplePickerNavigationControllerDelegate

注:该代理在iOS8中略有改动,请自行验证

- (IBAction)picker:(id)sender {
    ABPeoplePickerNavigationController* controller = [[ABPeoplePickerNavigationController alloc]init];
    //设置peoplePickerDelegate属性为当前视图控制器自身
    controller.peoplePickerDelegate = self;
    [self presentViewController:controller animated:YES completion:^{
    }];
}
#pragma mark -- ABPeoplePickerNavigationControllerDelegate
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person{
    //取消显示peoplePicker视图控制器
    //的那个peoplePicker视图控制器隐藏时执行后面的代码块
    [peoplePicker dismissViewControllerAnimated:YES completion:^{
        //创建ABPersonViewController视图控制器
        //取出person记录中kABPersonFirstNameProperty属性的值
        NSString* firstName = (__bridge NSString*)ABRecordCopyValue(person, kABPersonFirstNameProperty);
        [self showAlert:[NSString stringWithFormat:@"您选中了[%@]联系人",firstName]];
    }];
    return YES;
}
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{
    return NO;
}
- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker{
    NSLog(@"用户取消了选择");
}
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier NS_AVAILABLE_IOS(8_0){
}
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker didSelectPerson:(ABRecordRef)person NS_AVAILABLE_IOS(8_0){
    //创建ABPersonViewController视图控制器
    //取出person记录中kABPersonFirstNameProperty属性的值
    NSString* firstName = (__bridge NSString*)ABRecordCopyValue(person, kABPersonFirstNameProperty);
    [self showAlert:[NSString stringWithFormat:@"您选中了[%@]联系人",firstName]];
}

4、使用ABPersonViewController显示指定联系人

添加头文件: #import < AddressBookUI/AddressBookUI.h >

遵循代理: ABPersonViewControllerDelegate

- (IBAction)view:(id)sender {
    ABAddressBookRef ab = ABAddressBookCreateWithOptions(NULL, NULL);
    ABRecordRef rec = ABAddressBookGetPersonWithRecordID(ab, 1);
    //创建ABPersonViewControler视图控制器
    ABPersonViewController* controller = [[ABPersonViewController alloc]init];
    controller.allowsActions = YES;
    controller.allowsEditing = YES;
    controller.personViewDelegate = self;
    controller.displayedPerson = rec;
    [self.navigationController pushViewController:controller animated:YES];
}
#pragma mark -- ABPersonViewControllerDelegate
- (BOOL)personViewController:(ABPersonViewController *)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{
    [self showAlert:[NSString stringWithFormat:@"名字为:%@",(__bridge NSString*)ABRecordCopyCompositeName(person)]];
    [personViewController.navigationController popViewControllerAnimated:YES];
    return YES;
}

三、使用UIApplication打电话、发短信

如果应用希望直接启用iOS系统内置的打电话、发短信、发邮件、浏览网页应用程序,则可调用UIApplication的openURL:方法打开特定的NSURL,这个方法将会根据NSURL的前缀不同而启动对应的应用程序。

(1)、sms:或sms://:发送短信;

- (IBAction)sendSMS:(id)sender {
    //打开sms:开头的URL代表发送短信,使用sms:或sms://前缀都行
    [app openURL:[NSURL URLWithString:@"sms:10010"]];
}

(2)、tel:或tel://:拨打电话;

- (IBAction)callPhone:(id)sender {
    //打开tel:开头URL代表拨打电话,使用tel:或tel://前缀都行
    [app openURL:[NSURL URLWithString:@"tel:10010"]];
}
- (IBAction)webViewCallPhone:(id)sender {
    UIWebView* callWebView = [[UIWebView alloc]init];
    //使用UIWebView加载tel:开头的URL代表拨打电话,而且电话结束后返回本应用
    //使用tel:或tel://前缀都行
    NSURL* telURL = [NSURL URLWithString:@"tel:10010"];
    [callWebView loadRequest:[NSURLRequest requestWithURL:telURL]];
    //将UIWebView添加到视图控制器管理的view种
    [self.view addSubview:callWebView];
}

(3)、telprompt:或telprompt://:拨打电话;

- (IBAction)promptCallPhone:(id)sender {
    //打开telprompt:开头的URL代表拨打电话,使用telprompt:或telprompt://前缀都行
    [app openURL:[NSURL URLWithString:@"telprompt:10010"]];
}

(4)、mailto::发送邮件

- (IBAction)sendMail:(id)sender {
    //打开mailto:开头URL代表发送短信,此处使用mailto://或mailto:前缀都行
    [app openURL:[NSURL URLWithString:@"mailto:[email protected]"]];
}

(5)、http:或http://:浏览网址;

- (IBAction)browserSite:(id)sender {
    //打开http:开头的URL代表使用默认的浏览器浏览指定网站,使用http:或http://前缀都行
    [app openURL:[NSURL URLWithString:@"http:www.crazyit.org"]];
}

四、使用MFMessageComposeViewController发短信

如果应用想自己提供界面让用户输入短信收件人地址、短信内容、主体、附件等短信内容,则可使用

MFMessageComposeViewController来发送短信,他也是一个驶入控制器,继承UINavigationController。

MFMessageComposeViewController提供了如下类方法判断iOS设备是否支持发送短信:

+ (BOOL)canSendText  __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_4_0);//该iOS设备是否支持发送文本段兴;

+ (BOOL)canSendAttachments __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_7_0);//该iOS设备是否支持发送带副本的短信;

+ (BOOL)canSendSubject __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_7_0);//该iOS设备是否支持发送带标题的短信。

程序使用MFMessageComposeViewController的类方法进行判断之后,接下来就按如下步骤发送短信:

  • ①、创建MFMessageComposeViewController对象;
  • ②、为MFMessageComposeViewController设置recipients(接受NSArray作为属性值,用于设置多个收件人号码)、subject(设置短信主题)、body(设置短信内容)/attachments(接受NSArray作为属性值,用于设置多个附件)等属性;
  • ③、为MFMessageComposeViewController设置messageComposeDelegate,该属性值必须是一个实现MFMessageComposeViewControllerDelegate协议的对象。该协议中定义了一个必须实现的messageComposeViewController:didFinishWithResult:方法,该方法负责处理短信发送结果。

添加库:MessageUI.framework

添加头文件: #import

- (IBAction)send:(id)sender {
    NSString* destStr = self.destField.text;
    NSString* contentStr = self.contentStr.text;
    if (destStr != nil&&destStr.length>0) {
        //如果能发送文本消息
        if ([MFMessageComposeViewController canSendText]) {
            //创建MFMessageComposeViewController对象
            MFMessageComposeViewController* picker = [[MFMessageComposeViewController alloc]init];
            //为MFMessageComposeViewController对象指定messageComposeDelegate
            picker.messageComposeDelegate = self;
            picker.navigationBar.tintColor = [UIColor blackColor];
            //设置收件人,此处可通过NSArray集合指定多个收件人
            picker.recipients = [NSArray arrayWithObject:destStr];
            //设置短信内容
            picker.body = contentStr;
            /*如果运行商支持,picker还支持指定subject(主题)和attachments(附件),也可用addAttachmentURL:withAlternateFilename:或addAttachmentData:typeIdentifier:filename:方法添加附件*/
            //显示MFMessageComposeViewController控制器
            [self.navigationController pushViewController:picker animated:YES];
        }
    }
}
#pragma mark -- MFMessageComposeViewControllerDelegate
//MFMessageComposeViewControllerDelegate协议中的方法,负责处理短信的发送结果
- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result{
    switch (result) {
        case MessageComposeResultCancelled:
            NSLog(@"结果:短信被取消发送");
            break;
        case MessageComposeResultSent:
            NSLog(@"结果:发送成功");
            break;
        case MessageComposeResultFailed:
            NSLog(@"结果:发送失败");
            break;
        default:
            break;
    }
    [self.navigationController popViewControllerAnimated:YES];
}

五、使用MFMailComposeViewController发送邮件

MFMailComposeViewController提供了如下类方法判断iOS设备是否支持发送邮件:

+ (BOOL)canSendMail __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);//该iOS设备是否支持发送邮件;

程序使用MFMailComposeViewController的类方法进入判断之后,接下来就可按如下步骤发送邮件:

  • ①、创建MFMailComposeViewController对象;
  • ②、为MFMailComposeViewController设置toRecipients:(接受NSArray作为属性值,用于设置多个收件人地址)、ccRecipients:(接受NSArray作为属性值,用于设置多个抄送人地址)、bccRecipients:(接受NSArray作为属性值,用于设置多个密送人地址)、subject(设置邮件主题),还可通过setMessageBody:isHTML:方法设置邮件正文,通过addAttachmentData:mimeType:fileName:方法附加附件。
  • ③、为MFMailComposeViewController设置mailComposeDelegate,该属性值必须是一个实现MFMailComposeViewControllerDelegate协议的对象。该协议中定义了一个必须实现的mailComposeController:didFinishWithResult:error:方法,该方法负责处理邮件的发送结果。

添加库:MessageUI.framework

添加头文件: #import

- (IBAction)sendMail:(id)sender {
    //获取界面上用户输入的内容
    NSString* toStr = self.toField.text;
    NSString* ccStr = self.ccField.text;
    NSString* bccStr = self.bccField.text;
    NSString* subjectStr = self.subjectField.text;
    NSString* contentStr = self.contentField.text;
    if (toStr != nil && subjectStr.length > 0 && subjectStr != nil && subjectStr.length > 0 && contentStr != nil && contentStr.length > 0) {
        //如果能发送邮件
        if ([MFMailComposeViewController canSendMail]) {
            MFMailComposeViewController* picker = [[MFMailComposeViewController alloc]init];
            //创建MFMailComposeViewController对象指定mailComposeDelegate
            picker.mailComposeDelegate = self;
            picker.navigationBar.tintColor = [UIColor blackColor];
            //设置收件人,此处可通过NSArray集合指定多个收件人
            [picker setToRecipients:[NSArray arrayWithObject:toStr]];
            if (ccStr != nil &&ccStr.length > 0) {
                //设置抄送人,此处可通过NSArray结合指定多个抄送人
                [picker setCcRecipients:[NSArray arrayWithObject:ccStr]];
            }
            if (bccStr != nil && bccStr.length > 0) {
                //设置密送人,此处可通过NSArray集合指定多个密送人
                [picker setBccRecipients:[NSArray arrayWithObject:ccStr]];
            }
            //设置邮件主题
            [picker setSubject:subjectStr];
            //设置邮件正文
            [picker setMessageBody:contentStr isHTML:NO];
            //显示MFMailComposeViewController控制器
            [self.navigationController presentViewController:picker animated:YES completion:nil];
        }
    }
}
#pragma mark -- MFMailComposeViewControllerDelegate
- (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error{
    switch (result) {
        case MFMailComposeResultCancelled:
            NSLog(@"结果:邮件被取消发送");
            break;
        case MFMailComposeResultSent:
            NSLog(@"结果:发送成功");
            break;
        case MFMailComposeResultFailed:
            NSLog(@"结果:发送失败");
            break;
        case MFMailComposeResultSaved:
            NSLog(@"结果:邮件被保存了");
            break;
        default:
            break;
    }
}
时间: 2024-10-28 16:17:32

第五章:管理手机的相关文章

2017.2.28 activiti实战--第五章--用户与组及部署管理(二)部署流程资源

学习资料:<Activiti实战> 第五章 用户与组及部署管理(二)部署流程资源 内容概览:讲解流程资源的读取与部署. 5.2 部署流程资源 5.2.1 流程资源 流程资源常用的有以下几种: 1 流程定义文件:拓展名为bpmn20.xml和bpmn 2 流程定义的图片:拓展名为PNG 3 表单文件:拓展名为form 4 规则文件:拓展名为drl 部署流程资源的时候,要注意一点: 引擎会根据不同的拓展名进行不同的处理.bpmn或bpmn20.xml类型的文件,会在ACT_RU_PROCDEF(流

2017.2.20 《activiti实战第五章--用户与组及部署管理》(一)用户与组

学习资料:<Activiti实战> 第五章 用户与组及部署管理(一)用户与组 内容概览:讲解activiti中内置的一套用户.组的关系,以及如何通过API添加.删除.查询. 5.1 用户与组 5.1.1 用户 1 public class IdentityServiceTest{ 2 @Rule 3 public ActivitiRule ar = new ActivitiRule();//使用默认的acitiviti.cfg.xml作为参数 4 5 @Test 6 public void t

第十五章 系统虚拟机管理

第十五章  系统虚拟机管理 下载虚拟机  lftp 172.25.254.250 ls cd  pub/iso/ get rhel-server-7.1-x86_64-dvd.iso 安装 ************************virt-install********************* #!/bin/bash virt-install \                        /*建立虚拟机 -name $1 \                          /*虚

Testlink1.9.17使用方法(第五章 测试用例管理)

第五章 测试用例管理 QQ交流群:585499566 TestLink支持的测试用例的管理包含二层:分别为新建测试用例集(Test Suites).创建测试用例(Test Cases).可以把测试用例集对应到项目的功能模块,测试用例则对应着具体的功能. 我们可以使用测试用例搜索功能从不同的项目.成百上千的测试用例中查到我们需要的测试用例,并且还提供移动和复制测试用例的功能,可以将一个测试用例移动或复制到别的项目里,勾上自动更新树选项,添加.删除或编辑测试用例后更新树会被自动更新. 一. 创建测试

Gradle 1.12用户指南翻译——第二十五章. Scala 插件

其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://github.com/msdx/gradledoc/tree/1.12. 直接浏览双语版的文档请访问: http://gradledoc.qiniudn.com/1.12/userguide/userguide.html. 另外,Android 手机用户可通过我写的一个程序浏览文档,带缓存功能的,目前

第五章心得

第五章主要学习了搭建S3C6410开发板的测试环境.首先要了解到S3C6410是一款低功耗.高性价比的RISC处理器它是基于ARMI1内核,广泛应用于移动电话和通用处理等领域. 开发板从技术上说与我们经常用的手机类似, 也包含显示屏. 键盘. Wi-Fi. 蓝牙等模块(可 能有的是选配〉.但与手机不同的是在开发板上安装嵌入式系统要比手机容易得多.而且一般开发 扳都有很多扩展的端口,可以很容易开发定制的硬件, 并与开发板链接.因此,开发板相对于孚机 来说,更适合对程序进行测试,尤其适合对底层的 L

第五章 搭建 S3C6.410 开发板的 测试环境

一.前言 开发板从技术上说与我们经常用的手机类似, 也包含显示屏. 键盘. Wi-Fi. 蓝牙等模块(可 能有的是选配〉.但与手机不同的是在开发板上安装嵌入式系统要比手机容易得多.而且一般开发 扳都有很多扩展的端口,可以很容易开发定制的硬件, 并与开发板链接.因此,开发板相对于孚机 来说,更适合对程序进行测试,尤其适合对底层的 Linux 程序(如 Linux 驱动〉进行测试. 目前市面上的开发板型号和种类很多,但目前最流行的是基于三星S3C6410 ARM11架构的开发板.国内很多厂商在S3C

大道至简第五章读后感

第五章 失败的过程也是过程 今天照样老师带领着我们阅读了大道至简第五章,阅读了<大道至简>的第五章,这章在前面的基础上又进了一步,有了技术和团队,加上有效的沟通,接下来就要接项目做工程. “虚有其表耳”,本章以<明皇实录>中的一句话来告诉我们一个深刻的道理:不要只求外表,只做形象工程,而是要透过表象,力求实质. 失败了不要紧,没有失败也就找不到自己的不足,也就不会发现自己的问题,更不用谈改进了.我们的前辈们就是在不断的失败中才总结出了“瀑布模型”“螺旋模型”等模型,方便了我们.但是

第五章搭建S3C6410开发板的测试环境

第五章本章主要介绍开发板的调试环境的搭建,以及如何将Android系统安装开发板上. 开发板是开发和学习嵌入式技术的主要硬件设备. 尽管可以在PC上开发Linux驱动,然后重新编译成ARM构架的Linux驱动模块,但最终都是要在开发板上进行测试的. 开发板从技术上说与手机类似,包含显示器.键盘.Wi-Fi.蓝牙等模块.开发板可扩展的端口多,容易开发定制的硬件. 第一节S3V6410开发板简介 S3C6410是一款低功耗.高性价比的RISC处理器,它给予ARM11内核,可以广泛应用于移动电话和通用

C和指针 (pointers on C)——第十五章:输入输出函数

第十五章 输入输出函数 这一章读完的第一感觉就是"呵呵". 如果说上过C语言课,基本上scanf()/printf()算是用的比较熟练了.尤其是那些抽象的格式说明.还有scanf()为什么要加括号. 读过本书前面的内容的话,getchar(),putchar(),gets(),puts()这些应该也问题不大. 再如果的话,你学过计算机图形学,你玩过OpenGL,听说过双缓存机制,那么fflush()也肯定弄明白了. 再加上FILE的操作,输入输出定位刷新删除改名,流的概念. 这一章就会