ios 入门开发三

我们正在学习如何编写一个简单的iPhone程序。这是我们三部曲教程的最后一部。这个程序是用来给恐怖昆虫打分的!

在教程的第一个部分, 我们的程序能在一个table view中列出所有昆虫.

在教程的第二个部分, 我们学习编写了昆虫的详细视图.

在第三部教程中,我们会学习如何添加新的昆虫以及如何为我们的程序添加小图标和默认图片。我们还会学习如何处理需要长时间的运算。

让我们一起完成这个程序吧!

添加和删除昆虫

所有的部分看起来都还不错,但是我们的程序还不是特别容易使用。因为大家都会想加入自己的昆虫,而我们只允许他们修改已有的昆虫。

幸运的是,因为我们的DetailViewController已经可以修改昆虫信息了,并且我们的RootViewController是一个UITableViewController类,大部分的准备工作已经就位了!我们只需要做四个改动。但我会一步一步逐一解释的:

1) 设置导航栏(navigation bar)按钮

在MasterViewController.m文件里的viewDidLoad函数中加入下面几行代码:

self.navigationItem.leftBarButtonItem = self.editButtonItem;
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] 
    initWithBarButtonSystemItem:UIBarButtonSystemItemAdd 
    target:self action:@selector(addTapped:)];

我们给导航栏加入了几个按钮。”navigationItem” 和 “title” 一样,都是视图控制器的特别属性。在导航控制器呈现这个视图控制器时,”leftBarButtonItem” 和 “rightBarButtonItem” 按钮就会在导航栏中出现。

“editButtonItem” 是这个类自带的一个按钮, 我们把它设为 “leftBarButtonItem”的值。 这个按钮的标签是 “Edit”,它能使UITableView在普通模式和修改模式(这个模式允许用户删除行)之间切换。

我们实例化一个按钮作为 “rightBarButtonItem” 的值。这个按钮让用户能够添加新的昆虫。其实系统自带了一个叫 “UIBarButtonSystemItemAdd” 的组件(那个像+号的东西),我们就直接用这个组件,并在用户点击它时,调用 “addTapped:” 函数。

注意,你也可以在故事版编辑器中设置这些属性,但为了学习的目的,我们用代码来做这些设置。

2) 实现 tableView:commitEditingStyle:forRowAtIndexPath 函数

仍在MasterViewController.m中,将 tableView:commitEditingStyle:forRowAtIndexPath 函数的内容换成如下:

if (editingStyle == UITableViewCellEditingStyleDelete) {        
    [_bugs removeObjectAtIndex:indexPath.row];
    [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}

当用户对某一行进行修改时,这个函数就会被调用。我们检查用户是否是在进行删除操作,如果是,我们就将那一行删除。注意我们不仅需要在数据模型(_bugs)中删除相应信息,还要通过调用deleteRowsAtIndexPaths来通知table view更新视图。

3) 处理添加昆虫的操作

将下面这个函数加到MasterViewController.m文件中:

- (void)addTapped:(id)sender {
    ScaryBugDoc *newDoc = [[ScaryBugDoc alloc] initWithTitle:@"New Bug" rating:0 thumbImage:nil fullImage:nil];
    [_bugs addObject:newDoc];
 
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:_bugs.count-1 inSection:0];
    NSArray *indexPaths = [NSArray arrayWithObject:indexPath];    
    [self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:YES];
 
    [self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionMiddle];
    [self performSegueWithIdentifier:@"MySegue" sender:self];    
}

当用户点击添加按钮时,这个函数(addTapped)就会被调用(这是我们在步骤1中做的设置)。

这里我们实例化一个ScaryBugDoc,赋予一些默认值并将它加到昆虫的阵列中。我们然后通知table view,让它知道我们加入了一行新内容,从而更新视图。

接着,我们用代码模拟用户点击这一行内容, 然后手动使程序进入修改昆虫的界面

注意,我们之前并没有在故事版编辑器中给segue取名为 “MySegue”,所以我们下一步就是做这个。

4) 给segue命名

打开MainStoryboard.storyboard然后点击选择master controller 和 detail view controller之间的箭头–这就是你的segue。

在右边的工具栏的第四栏(Attributes Inspector),把Identifier属性设为”MySegue”。这样,我们才能用代码手动运行这个segue。

Naming a segue in Xcode

完成了!如果你现在编译并运行程序,你应该可以添加自己的昆虫了,就像下图一样:

An Objective-C Bug

加入新的小图标和默认图片

我们的程序已经非常有意思了,但是它还缺少一个小图标。

幸运的是,加小图标是很简单的。早前,我们曾将一个叫logo1.png小图标文件加到我们的文件列表里(那个图片来自ExtraStuffForScaryBugs2.zip)。我们就把这个图片设为程序的小图标。

最简单的办法是点击选择文件目录栏中的ScaryBugs,然后在Targets列表下选择ScaryBugs。确保Summary栏是打开的,然后滚动屏幕至App Icons。按住control键点击第一个icon,然后点击select file:

Changing the icon for an app in Xcode

从文件列表中选择logo1.png,如果看到任何警告信息,选择accept就可以了。

注意这是改变 ScaryBugs-Info.plist 中选项的捷径。程序中的重要选项都存在 ScaryBugs-Info.plist 文件里。如果要验证我说的,你只需要打开 ScaryBugs-Info.plist文件,它应该类似下图:

Settings to set an app's icon in the info.plist

你本来可以手动改写这些选项的,但是我觉得用GUI比较简单。

在虚拟器或者手机中移除我们的程序然后重新安装,你应该能看到那个新的小图标了!

New icon for Scary Bugs

我们还需要修改一样东西。你运行ScaryBugs时,应该注意到了在程序启动时有一段时间的黑屏显示。

根据苹果公司的开发者文件,我们最好在这时显示一个和我们实际程序类似的图片。要做到这点很简单。打开MasterController.m并做如下改动:

// 把 tableView:numberOfRowsInSection's 返回值改为:
return 0; //return _bugs.count;

运行程序,你会看到一个空白的 table view。 在XCode上方工具栏,点击Organizer,选择你的设备,选择screenshots(截屏),然后点击”New Screenshot” (在屏幕右下方)来获得新的截屏:

Taking a screenshot with Xcode Organizer

然后点击”Save as Launch Image”(将这个截屏设为启动图片)。 确保选择ScaryBugs workspace, 文件名为Default,点击”Next”。那个图片会被储存到程序文件夹中(并出现在文件列表)。

你可以在我们程序设置的summary栏看到这个图片已经被设为启动图片了:

Setting the launch image in the Xcode project settings

将tableView:numberOfRowsInSection函数的返回值改回来。编译并启动程序。你应该看到那个屏幕截图作为启动图片出现,它取代了以前的黑屏。

附加内容:处理长时间运算操作

如果你总在虚拟器里运行程序,应该不会注意到任何问题, 但是如果你在真的iPhone上运行并试着去更换昆虫的图片,UIImagePicker需要很长的时间来初始化。在选择了一个图片后,图片需要被缩小来生成略图,这个运算操作又需要很长的时间。 这是件很糟的事,因为它使我们的程序显得非常慢。

一定要记住,在主线程上不能跑需要长时间的运算操作。我们现在有两个地方都违反了这一原则,所以我们的程序显得很慢。

我们应该在后台线程上跑这些运算。最好让这些运算在后台完成,用户可以同时继续其他操作。如果一定需要这些运算完成后,用户才能继续(比如那个图片选择器),那我们至少应该有个加载指示器来让用户了解程序只是在加载,而不是已经死机了。

所以现在我们要做的就是用后台线程来跑这些费时的运算,然后在主线程显示一个加载指示器。

显示加载指示器是个常见的问题,已经存在一些 加载指示器库,我们可以直接用。 我试过几个,其中我最喜欢 Sam Vermette写的SVProgressHUD。你可以在SVProgressHUD Github 网页上下载一份。

在你下载SVProgressHUD后,将SVProgressHUD.h 和 SVProgressHUD.m文件加到”Views”文件组中。你还需要完成两步设置:

  1. 加入所需要的库
    在文件目录栏中选择你的程序,然后选择ScaryBugs作为target。点击选择Build Phases栏并且扩展Link Binary with Libraries部分。点击+按钮然后加入QuartzCore.framework

Adding required QuartzCore.framework library in Xcode

  1. 在非ARC状态下编译。 SVProgressHUD 和 ARC 还不兼容, 我们需要在非ARC状态下编译它。 扩展Compile Sources部分然后双击SVProgressHUD.m的那一行。在弹出的输入框中输入-fno-objc-arc

你现在应该可以编译程序,不会有错误出现了。

接着对DetailViewController.m文件做如下改动:

// 文件顶端
#import "SVProgressHUD.h"
 
// 把addPictureTapped函数的内容改成:
- (IBAction)addPictureTapped:(id)sender {
    if (self.picker == nil) {   
 
        // 1) 显示状态
        [SVProgressHUD showWithStatus:@"Loading picker..."];
 
        // 2) 从系统中获取一个并行队列
        dispatch_queue_t concurrentQueue =
        dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 
        // 3) 在后台线程创造图像选择器
        dispatch_async(concurrentQueue, ^{
 
            self.picker = [[UIImagePickerController alloc] init];
            self.picker.delegate = self;
            self.picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
            self.picker.allowsEditing = NO;    
 
            // 4) 在主线程中显示那个图像选择器
            dispatch_async(dispatch_get_main_queue(), ^{
                [self.navigationController presentModalViewController:_picker animated:YES];    
                [SVProgressHUD dismiss];
            });
 
        });        
 
    }  else {        
        [self.navigationController presentModalViewController:_picker animated:YES];    
    }
}

这里出现了许多新的东西,让我们一步一步解释。

  1. 我们用刚刚加入SVProgressHUD这个辅助类来显示一个加载指示器。这样用户就会知道程序并没有死机。
  2. 我们想在后台加载那个图片选择器。IOS有个技术叫做Grand Central Dispatch可以做到这一点。我们在这个教程中不会详细阐述(但是如果你有兴趣,可以读一读 这篇教程). 现在,你只需知道这行代码给你提供了一个队列, 你可以用这个队列在后台跑一些运算。
  3. 这里,我们在后台加载一个图片选择器。如果你对blocks代码不太熟悉,可以读读 这个教程.
  4. 最后,我们在主线程显示那个选择器。注意,任何改变视图的操作都必须在主线程上完成,不能在后台线程上完成。

同样,我们也可以在后台完成修改图片大小的运算。把imagePickerController:didFinishPickingMediaWithInfo函数做如下修改:

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {    
 
    [self dismissModalViewControllerAnimated:YES];
 
    UIImage *fullImage = (UIImage *) [info objectForKey:UIImagePickerControllerOriginalImage]; 
 
    // 1) 显示状态
    [SVProgressHUD showWithStatus:@"Resizing image..."];
 
    // 2) 从系统获取一个并行队列
    dispatch_queue_t concurrentQueue =
    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 
    // 3) 在后台完成修改图片大小的运算
    dispatch_async(concurrentQueue, ^{
 
        UIImage *thumbImage = [fullImage imageByScalingAndCroppingForSize:CGSizeMake(44, 44)];
 
        // 4) 在主线程显示图片
        dispatch_async(dispatch_get_main_queue(), ^{
            self.detailItem.fullImage = fullImage;
            self.detailItem.thumbImage = thumbImage;
            self.imageView.image = fullImage;
            [SVProgressHUD dismiss];
        });
 
    });
 
}

大功告成!在你的真实设备上运行这个程序,当程序在进行费时的运算时,你会看到一个加载指示器的动画,提高了用户体验。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值