C_obj

Objective-C,面向对象的C语言,iOS系统及移动应用开发语言,集成开发环境xcode。
 
New Project新建应用,xcode会生成main函数、主代理类(加载MainWindow.xib,控制AppViewController加载AppViewController.xib进入应用)、App-info.plist。
Classes:类声明(.h)及实现(.m),通常xib界面->IBOutlet、IBAction - .h(@property) - .m(@synthesize)
Resources:资源png、数据plist(数据可加载到NSArray)、界面xib(Nib文件源于NeXTStep收购,与NSString相关)
Frameworks框架:Cocoa Touch提供Interface Builder所需的界面元素
 
Nib界面AppViewController.xib,使用Interface Builder设计界面,将界面元素、交互动作与File's Owner里的IBOutlet成员和IBAction方法绑定。
 
头文件AppViewController.h,引入库,声明协议支持,声明成员、属性、方法。
#import <UIKit/UIKit.h> //引入库UIKit.framework,#import避免重复引入
@interface AppViewController : UIViewController //单继承
<UIPickerViewDataSource,UIPickerViewDelegate> { //多协议(声明并实现协议方法即可,这里是数据源和控制代理)
IBOutlet UIPickerView *pickerView; //Interface Builder可以将界面上的选取器UIPickerView与此成员绑定
NSArray *activities, *feelings; //内部数据成员,为数据源和代理提供支持;还支持plist文件、sqlite数据库
}
//方法默认是原子的,noatomic配置非原子特性;retain引用copy复制assign赋值readonly只读readwrite读写
@property (noatomic,retain) UIPickerView *pickerView;//属性声明与实现文件里的@synthesize一起辅助编译器生成属性读写方法
//减号表示实例方法,加号表示类方法
-(IBAction) sendButtonTapped: (id) sender;//Interface Builder可以将界面上的按钮的Touch Up Inside事件与此方法绑定
 
实现文件AppViewController.m,引入头文件,声明成员,实现方法,内存释放
#import "AppViewController.h"
@implementation AppViewController
@synthesize pickerView;//结合头文件中的属性声明,IBOutlet与Nib的关联,此变量可以引用界面元素UIPickerView
//实现协议UIPickerViewDataSource需要的方法
-(NSInteger)numberOfComponentsInPickerView: (UIPickerView *)pickerView { return 2; }
-(NSInteger)pickerView: (UIPickerView *)pickerView //UIPickerView需要知道选择器有多少部件(列),每个部件有多少行
numberOfRowsInComponent: (NSInteger)component { return component == 0 ? [activities count] : [feelings count]; }
//实现协议UIPickerViewDelegate需要的方法
-(NSString *)pickerView: (UIPickerView *)pickerView //获取某个部件,某行需要的数据
titleForRow: (NSInteger)row forComponent: (NSInteger)component
{ return component == 0 ? [activities objectAtIndex:row] : [feelings objectAtIndex:row]; }
//实现初始化方法及清理内存
-(void)viewDidLoad { [super viewDidLoad]; //初始化字符串数组,@"hehe"是NSString的简写,与普通的char*区别
activities = [[NSArray alloc] initWithObjects:@"haha", @"hehe", nil]; feelings = ~~~; }
-(void)dealloc { [activities release]; [feelings release]; [super dealloc]; } //释放数组内存
 
  • InstaTwit:即推
    1. 模板:View-based Application视图,主视图InstaTwitViewController
    2. 界面:标题Label,提示Label,选取器PickerView(IBOutlet、UIPickerViewDataSource、UIPickerViewDeletate),按钮Button(IBAction);在.h中添加IBOutlet、@property、IBAction,在.m中添加@synthesize、-(void)dealloc{[var release];}
    3. 主视图实现选取器数据源和代理InstaTwit : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate>,单继承多协议(接口实现);数据源、代理。
      数据源根据内部数组NSArray提供:部件(列)数、各部件行数
      -(NSInteger)numberOfComponentsInPickerView:(UIPickerView*)pickerView
      -(NSInteger)pickerView:(UIPickerView*)pickerView numberOfRowsInComponent:(NSInteger)component
      代理提供:各部件各行的内容
      -(NSString*)pickerView:(UIPickerView*)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
    4. 按钮事件:根据选择行[pickView selectedRowInComponent:0]从内部数组获取字符串,按模式[NSString stringWithFormat:@"我在%@,我觉得%@啊!", activity, feeling]拼出消息,调Twitter接口发布即推NSData=[NSURLConnection sendSynchronouseRequest:NSMutableURLRequest returningResponse:NSURLResponse error:NSError]。
    5. 扩展:自定义输入开头UITextField,键盘会自动激活,其Return按钮可自定义文字(如:完成),为让键盘消失需要绑定事件UITextField.Did End on Exit到IBAction,并调用[sender resignFirstResponder],sender就是输入框UITextField。
  • Drink Mixer:调酒师
    1. 模板:Navigation-based Application导航,主视图RootViewController:UITableViewController
    2. 表格视图UITableViewController相关协议:段落数、各段落行数、各段落各行单元格(IndexPath封装段落和行编号)
      -(NSInteger)numberOfSectionsInTableView:(UITableView*)tableView
      -(NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
      -(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
      表格单元格通常可以按键值重用:
      UITableViewCell* cell=[tableView dequeueReusableCellWithIndentifier:@"cell"];
      if(cell == null) cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"] autorelease];
    3. 从plist加载饮料数据:plist通常是数组,而数组元素可以是NSString饮料名或NSDictionary封装名称、配方等
      NSString *path=[[NSBundle mainBundle] pathForResource:@"DrinkArray" ofType:"plist"];
      [[NSMutableArray alloc] initWithContentsOfFile:path];
    4. 细节视图:DrinkDetailViewController,饮料名称UITextField(不可更改)、成分和方法UITextView
      表格单元格点击事件:新建细节视图并压栈,导航模板内部有UINavigationController控制多个视图
      -(void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath{
      [[DrinkDetailViewController alloc] initWithNibName:@"DrinkDetailViewController" bundle:nil];
      [self.navigationController pushViewController:drinkDetailViewController animated:YES];}
      细节视图可以仅设置模型NSDictionary *drink,而在将要显示时提取drink中的名称、配方等信息
      -(void)viewWillAppear:(BOOL)animated{//给各UI元素赋值,维护数据NSDictionary* drink;}
    5. HIG(Human Interface Guide)建议表格视图必须有披露指示器(有多项操作)或披露指示元素(有下级详情)
      cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;//表格每行右侧显示普通箭头符号
    6. 扩展:添加新饮料AddDrinkViewController,继承细节视图xib,代码中添加“保存”、“取消”按钮。
      “保存”时输入不合法将提示,表明需要“模式视图”,在“添加”按钮的IBAction里压入视图
      [[AddDrinkViewController alloc]initWithNibName:@"DrinkDetailViewController" bundle:nil];
      [self presentModalViewController:addDrinkVC animated:YES];
      模式视图没有顶部导航栏,所以需要手动添加导航栏,同时“保存”和“取消”按钮也有了布局空间。
      [[UINavigationController alloc] initWithRootViewController:addDrinkVC];
      导航控制器自带左右按钮,为“保存”和“取消”按钮提供支持,声明IBAction save|cancel方法。
      self.navigationItem.leftBarButtonItem=[[[UIBarButtonItem alloc] initWithBarButtonSystetmItemCancel target:self action:@selector(cancel:)] autorelease];
      self.navigationItem.rightBarButtonItem=[[[UIBarButtonItem alloc] initWithBarButtonSystemItemSave target:self action:@selector(save:)] autorelease];
      保存或取消后需要退出模式视图
      [self dismissModalViewControllerAnimated:YES]
      实际输入时,键盘挡住下半屏幕,需要滚动视图UIScrollView将所有元素嵌入Embed Objects In,并设置滚动视图大小
      -(void)viewDidLoad{scrollView.contentSize=self.view.frame.size;}//滚动视图大小就是内部view的大小
      键盘的出现和消失,需要系统通知的支持,可以在添加视图的出现viewWillAppear或消失viewWillDisappear时向默认通知中心订阅或取消事件
      [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:null];//方法keyboardDidShow响应UIKeyboardDidShowNotification通知
      [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:null];//方法keyboardDidHide响应UIKeyboardDidHideNotification通知
      [[NSNotificationCenter defaultCenter] removeObserver:self];//添加视图消失时取消响应通知
      键盘事件时需要改变滚动视图可是空间大小,这样滚动视图就可以显示出滚动条,方便输入配方等信息
      -(void)keyboardDidShow:(NSNotification*)notif{
      if(keyboardVisible)return;//需要记住键盘状态,因为切换输入框时键盘都在
      NSDictionary* info=[notif userInfo];//系统通知带有字典,封装事件细节
      CGSize keyboardSize=[[info objectForKey:UIKeyboardBoundsUserInfoKey] CGRectValue].size;//键盘大小
      scrollView.frame.height -= keyboardSize.height;//可视空间高度变小
      keyboardVisible=true;}//标记键盘出现状态
      实际添加数据,并通知视图更新显示
      NSMutableDictionary* newDrink=[[NSMutableDictionary alloc] init];
      [newDrink setValue:nameTextField.text forKey:NAME_KEY];//键值用常量constants.h值#define NAME_KEY "name";
      [self.drinks addObject:newDrink];
      -(void)viewWillAppear:(BOOL)animated{[self.tableView reloadData];}//表格视图RootViewController重新载入数据
    7. 排序显示数据:每次添加新饮料都需要排序
      NSSortDescriptor *nameSorter=[[NSSordDescriptor alloc] initWithKey:NAME_KEY ascending:YES selector:@selector(caseInsensitiveCompare:)];//大小写不敏感排序方法caseInsensitiveCompare
      [self.drinks sortUsingDescriptors:[NSArray arrayWithObject:nameSorter]];
    8. 持久保存数据:应用退出时系统会通知UIApplicationWillTerminateNotification,需要在频繁保存和应用崩溃间权衡
      RootViewController持有drinks数据,与AddDrinkViewController在显示或消失时注册通知不同,表格视图需要在viewDidLoad和viewDidUnload注册通知:表格视图会频繁地出现和消失,比如细节视图或添加视图出现。
      [self.drinks writeToFile:path atomically:YES];//path为之前载入plist的路径NSString,因为文件权限的问题会失败
    9. 删除与编辑已有数据:编辑模式,表格属性里选中Allow Selection While Editing支持选择某项进行编辑
      根视图右侧有添加按钮,左侧再添加编辑按钮即可:viewDidLoad里设置导航按钮
      self.navigationItem.leftBarButtonItem=self.editButtonItem;//表格视图内建支持,取消注释即可
      -(void)tableView:(UITableView*)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath*)indexPath{
      if(editingStyle==UITableViewCellEditingStyleDelete){[self.drinks removeObjectAtIndex:indexPath.row];
      [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath] withRowAnimation:UITableViewRowAnimationFade];}}//数据和视图都清除一行
      选择行时判断是否编辑模式
      -(void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath{
      if(!self.editing){//显示细节视图}else{
      editingDrinkVC.drink=[self.drinks objectAtIndex:indexPath.row];}//使用drink对象标识是编辑或添加模式视图
      显示和保存时判断是否编辑模式
      -(void)viewWillAppear:(BOOL)aniamted{if(self.drink != nil){//设置界面控件显示值}}
      -(IBAction)save:(id)sender{if(self.drink != nil){[drinkArray removeObject:drink]; self.drink=nil;}}//先删后存
  • iBountyHunter:赏金猎人
    1. 模板:Window-based Application,主视图UITabBarController标签包含逃犯和被捕表格UITableView。
    2. 视图:标签UITabBarController,逃犯FugitiveListViewController(带导航)
    3. 将标签加入主窗口:
      -(void)applicationDidFinishingLaunching:(UIApplication*)application{
      [window addSubView:tabcontroller.view];[window makeKeyAndVisible];}
    4. 使用Core Data存取数据:
      受控实体类:Fugitive:NSManagedObject,通常从模型生成实体类,字段以@property形式出现,完全由Core Data自动管理
      受控对象模板:iBountyHunter.xcdatamodel,添加对应字段即可(Interface Builder)
      受控对象上下文:构建NSFetchRequest交给受控对象上下文存取实体,新建实体时不能再手动初始化Fugitive,而应交给它
      iBountyHunterAppDelegate* appDelegate=(iBountyHunterAppDelegate*)[[UIApplication sharedApplication] delegate];
      NSManagedObjectContext* managedObjectContext=appDelegate.managedObjectContext;
      持久存储协调器:配置上下文从SQLite存取数据
    5. 取出数据:从受控对象上下文查询数据,不关心是来自sqlite、memory、binary file,在viewWillAppear时读取
      NSFetchRequest* request=[[NSFetchRequest alloc] ini];
      NSEntityDescription* entity=[NSEntityDescription entityForName:@"Fugitive" inManagedObjectContext:managedObjectContext];//从受控对象上下文读取实体模型
      NSSordDescriptor* sortDescriptor=[[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];//按name字段排序
      [request setEntity:entity];//指定实体
      [request setSortDescriptor:[NSArray arrayWithObject:sortDescriptor]];//指定排序
      NSError* error;//保存错误信息
      NSMutableArray* mutableFetchResults=[[managedObjectContext exucuteFetchRequest:request error:&error] mutableCopy];//取出逃犯数据,存入items变量
      if(mutableFetchResults==nil){NSLog(@"Can't Load Fugitive data!");}
    6. 持久存储协调器:
      -(NSPersistentStoreCoordinator*)persistentStoreCoordinator{
      if(persistentStoreCoordinator==nil){
      NSURL* storeURL=[NSURL fileURLWithPath:[[self applicationDocumentDirectory] stringByAppendPathComponent:@"iBountyHunter.sqlite"]];//从文档目录读取数据库文件
      persistentStoreCoordinator=[[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];//为受控对象上下文配置持久存储协调器,由协调器负责实际读写数据库
      if(![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]){NSLog(@"Unresolved error %@",error);abort();}
      return persistentStoreCoordinator;}
      应用安装后是只读的,目录结构为:iBountyHunter.app程序、Documents+Library(Preferences+Caches)文档目录、tmp临时,应用启动时需要检查Documents目录是否有一份数据拷贝,需要时从资源目录拷贝过来(在applicationDidFinishLaunching调用)
      -(void)createEditableCopyOfDatabaseIfNeeded(
      NSFileManager* fileManager=[NSFileManager defaultManager];
      NSString* documentsDirectory=[self applicationDocumentsDirectory];//可读写数据库文件在文档目录
      NSString* writableDBPath=[documentsDirectory stringByAppendingPathComponent:@"iBountyHunter.sqlite"];
      BOOL dbexists=[fileManager fileExistsAtPath:writableDBPath];//判断数据库文件是否存在
      if(!dbexists){NSString* defaultDBPath=[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"iBountyHunter.sqlite"];//应用资源数据库路径
      if(![fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error]){
      NSLog(@"Failed to create writable database file with message %@",[error localizedDescription]);}}
    7. 细节视图:FugitiveDetailViewController,姓名Label、编号Label、描述TextView、赏金Label;数据Fugitive
      -(void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath{
      FugitiveDetailViewController* fugitiveDetailViewController=[[FugitiveDetailViewController alloc] initWithNibName:@"FugitiveDetailViewControoler" bundle:nil];//加载逃犯视图
      fugitiveDetailViewController.fugitive=[self.items objectAtIndex:indexPath.row];//细节视图需要逃犯实体
      [self.navigationController pushViewController:fugitiveDetailViewController animated:YES];}//压入视图堆栈
      然后再FugitiveDetailViewController的viewWillAppear中向UI填充Fugitive数据
    8. 增加被捕信息:添加到逃犯细节视图方便编辑已被捕,NSNumber* captured;NSDate* captdate;
      数据迁移:给数据模型添加版本控制DataModel->AddModelVersion|SetCurrentVersion,Core Data需要知道所有模型版本已方便从各种旧版数据迁移到最新版数据。
      -(NSPersistentStoreCoordinator*)persistentStoreCoordinator{
      NSDictionary* options=[NSDictionary dictionaryWithObjectAndKeys:[NSNumber numberWithBool:YES],NSMigratePersistentStoresAutomaticallyOption,[NSNumber numberWithBool:YES],NSInferMappingModelAutomaticallyOption,nil];
      [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error];//之前options为nil,这里启用轻量级数据迁移,通常添加属性、改名可行,高级有映射模型
      “已抓获?”使用分段控件UISegmentedControll(可同时显示YES|NO,开关控件仅交替显示ON|OFF不合意图、复选框不是标准控件需自己定制),点击是否已抓获时的动作
      -(IBAction)capturedToggleChanged:(id)sender{
      if(capturedToggle.selectedSegmentIndex==0){//分段控件有属性selectedSegmentIndex标识所选项
      fugitive.captdate=[NSDate date];//now此时已抓获
      fugitive.captured=[NSNumber numberWithBool:YES];//标记已抓获
      }else{fugitive.captdate=nil;}//标记未抓获
      fugitive是受控对象,上下文会在应用退出时自动检查并保存数据,但通常我们需要即时保存预防应用崩溃导致数据丢失
      [managedObjectContex save:&error];
      新建受控实体:新的实体被managedObjectContext知道所以会自动保存,校验[fugitive validateForInsert],回滚rollback
      [NSEntiryDescription insertNewObjectForEntityForName:@"Fugitive" inManagedObjectContext:managedObjectContext];
    9. 被捕视图:CapturedListViewController,获取逃犯列表时过滤数据,NSFetchRequest(Entity实体,Predicate谓词,sort排序]
      NSPredicate* captured=[NSPredicate predicateWithFormat:@"captured=YES"];
      [request setPredicate:captured];//过滤已抓获的逃犯
      列表与逃犯视图FugitiveListViewController类似,重用细节视图FugitiveDetailViewController
      谓词Predicate还可以从数据模型获取(xcode可视化编辑不易出错):Add Fetch Request -> Edit Predicate
      NSFetchRequest* request=[managedObjectModel fetchRequetFromTemplateWithName:@"capturedFugitives" substitutionVariables:[NSDictionary dictionaryWithObject:capturedFlag forKey:@"captured"]];
    10. 性能调优:逃犯视图和被捕视图有两个数组,如果移至viewDidLoad会有两个问题(仅加载一次细节视图修改后不会反应出变化、viewDidLoad在applicationDidFinishLaunching复制数据之前运行读取会有问题),而应该使用NSFetchedResultsController(专门设计与表格视图配合高效工作,可以仅保留需要显示的数据15个左右在内存中)
      CapturedListViewController支持协议NSFetchedResultsControllerDelegate并添加成员属性NSFetchedResultsController*
      在viewWillAppear中检查resultsController不为空时加载数据,
      resultsController=[[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:@"captured_list.cache"];
      resultsController.delegate=self;//被捕视图是它的代理并支持NSFetchedResultsControllerDelegate协议,数据变化时通知
      [self.tableView reloadData];加载数据
      更新表格视图数据协议与代理
      -(NSInteger)numberOfSectionsInTableView:(UITableView*)tableView{
      return [[self.resultsController sections] count];}//数据控制器知道有多少段落,sectionNameKeyPath按属性名分组
      -(NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section{
      return [[[self.resultsController sections] objectAtIndex:section] numberOfObjects];//各段落行数
      -(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath{
      Fugitive* fugitive=[self.resultsController objectAtIndexPath:indexPath];}//重用标识单元格,取出数据(选择行类似)
      数据改变时需要视图刷新数据,否则界面不会更新。数据变化时控制器resultsController会通知代理capturedListView
      -(void)controllerDidChangeContent:(NSFetchedResultsController*)controller{[self.tableView reloadData];}
      这里完全载入数据还有优化的余地:仅更新显示单元格
    11. 扩展:逃犯照片,CapturedPhotoViewController,@property (nonatomic,retain) UIImageView* fugitiveImage;
      在FugitiveDetailViewController视图添加showInfoButton
      -(IBAction)showInfoButtonPressed:(id)sender{
      CapturedPhotoViewController* controller=[[CapturedPhotoViewController alloc] initWithNubName:@"CapturedPhotoViewController" bundle:nil];
      controller.fugitive=self.fugitive;//从细节视图传入逃犯信息Fugitive
      controller.modalTransitionStyle=UIModalTransitionStyleFlipHorizontal;//翻转动画效果切换
      [self presentModalViewController:controller animated:YES];
      数据模型添加image属性,再次迁移,
      -(void)viewWillAppear:(BOOL)animated{
      self.fugitiveImage.image=[[[UIImage alloc] initWithData:fugitive.image] autorelease];}
      照片视图CapturedPhotoViewController,照片UIImageView
      -(void)takePictureButton:(id)sender{
      if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]){//iTouch不支持相机
      UIActionSheet* photoSourceSheet=[[UIActionSheet alloc] initWithTitle:@"Select Fugitive Picture" delegate:self cancelButton:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Take New Photo",@"Choose Existing Photo",nil,nil];//选择相片源表单的通知协议UIActionSheetDelegate
      [photoSourceSheet showInView:self.view];//选择相片源:相机、图片库、取消
      }else{UIImagePickerController* picker=[[UIImagePickerController alloc] init];
      picker.sourceType=UIImagePickerControllerSourceTypePhotoLibrary;
      picker.delegate=self;//选择图片后自己会被通知,协议UIImagePickerControllerDelegate
      picker.allowsEditing=YES;//允许照相后编辑图片
      [self presentModalViewController:picker animated:YES];}
      选择相片源表单协议UIActionSheetDelegate
      -(void)actionSheet:(UIActionSheet*)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex{
      UIImagePickerController* picker=[[UIImagePickerController alloc] init];
      picker.delegate=self;picker.allowEditing=YES;
      switch(buttonIndex){case 0:picker.sourceType=UIImagePickerControllerSourceTypeCamera;break;//相机
      case 1:picker.sourceType=UIImagePickerControllerSourceTypePhotoLibrary;break;//图片库
      defalt:[picker release];return;//用户选择了取消
      [self presentModalViewController:picker animated:YES];}//拍照或选择相片
      选择图片后的协议UIImagePickerControllerDelegate
      -(void)imagePickerController:(UIImagePickerController*)picker didFinishPickingImage:(Image*)image editingInfo:(NSDictionary*)editingInfo{self.fugitive.image=UIImagePNGRepresentation(image);//保存照片,Core Data会存入库
      [self dismissModalViewControllerAnimated:YES];[picker release];}//关掉窗口、释放照片选取器
      -(void)imagePickerControllerDidCancel:(UIImagePickerController*)picker{
      [self dismissModalViewControllerAnimated:YES];[picker release];}//用户直接返回了
    12. 扩展:抓捕地点
      引入新框架Add->ExistingFramework->CoreLocation.framework,#include <CoreLocation/CoreLocation.h>
      数据模型添加double capturedLat,capturedLon经纬度,迁移数据库
      详情视图添加UILabel* capturedLocation位置,[NSString stringWithFormat:@"%.3f,%.3f",capturedLat,capturedLon]
      详情视图添加属性CLLocationManager* locationManager;
      -(void)viewWillAppear{
      self.locationManager=[[CLLocationManager alloc] init];
      self.locationManager.desiredAccuracy=kCLLocationAccuracyNearestTenMeters;//十米精度
      self.locationManager.delegate=self;//地址更新时通知代理
      [self.locationManager startUpdatingLocation];}//定位功能非常耗电,在viewWillDisapplear释放
      -(void)viewWillDisappear:(BOOL)animated{
      [self.locationManager stopUpdatingLocation];[self.locationManager=nil];}
      定位地址通知协议CLLocationManagerDelegate
      -(void)locationManager:(CLLocationManager*)manager didUpdateToLocation:(CLLocation*)newLocation fromLocation:(CLLocation*)oldLocation{capturedToggle.enabled=YES;}//可切换被捕
      -(void)locationManager:(CLLocationManager*)manager didFailWithError:(NSError*)error{
      capturedToggle.enabled=NO;}//不能定位地址时禁用切换按钮
      切换被捕状态时保存位置信息
      -(IBAction)capturedToggleChanged:(id)sender{
      if(capturedToggle.selectedSegmentIndex==0){
      CLLocation* curPos=self.locationManager.location;//获得上次通知的地址
      fugitive.capturedLat=[NSNumber numberWithDouble:curPos.coordinate.latitude];
      fugitive.capturedLon=[NSNumber numberWithDouble:curPos.coordinate.longitude];
      }else{fugitive.capturedLat=nil;fugitive.capturedLon=nil;}//更新位置标签capturedLocation
    13. 扩展:地图显示抓捕地点CapturedPhotoViewController下部
      添加地图开发包#iport <MapKit/MapKit.h>,@property (nonatomic,retain) IBOutlet MKMapView* fugitiveMapView;
      -(void)viewWillAppear:(BOOL)animated{//在照片视图显示时加载地图
      if([fugitive.captured boolValue]==YES){//判断是否已被捕
      CLLocationCoordinate2D mapCenter;
      mapCenter.latitude=[fugitive.capturedLat doubleValue];
      mapCenter.longitude=[fugitive.capturedLon doubleValue];//提取被捕位置经纬度
      MKCoordinateSpan mapSpan;
      mapSpan.latitudeDelta=0.005;mapSpan.longitudeDelta=0.005;//地图尺寸比例千分之五
      MKCoordinateRegion mapRegion;
      mapRegion.center=mapCenter;mapRegion.span=mapSpan;//位置和比例组合成范围
      self.fugitiveMapView.region=mapRegion;
      self.fugitiveMapView.mapType=MKMapTypeHybrid;}}//混合卫星和道路信息的地图
      [self.fugitiveMapView addAnnotaion:fugitive];//Fugitive实现了地图注释协议MKAnnotation
      大头针注释地图
      @interface Fugitive:NSManagedObject<MKAnnotation>
      @property (nonatomic,readonly) CLLocationCoordinate2D coordinate;
      -(NSString*)title;-(NSString*)subtitle;//协议支持Fugitive.h
      (CLLocationCoordinate2D)coordinate{
      CLLocationCoordinate2D captureCoord;
      captureCoord.latitude=[self.capturedLat doubleValue];
      captureCoord.longitude=[self.capturedLon doubleValue];
      return captureCoord;}
      -(NSString*)title{return self.name;}
      -(NSString*)subtitle{return self.desc;}//不合成属性@synthesize,手动返回值






posted @ 2012-07-26 15:18  xlongwei  阅读(341)  评论(0编辑  收藏  举报
xlongwei