iOS-MultipeerConnectivity框架开发(二)(优化中。。。)
2016-03-07 17:12
537 查看
原文地址:http://www.appcoda.com/intro-ios-multipeer-connectivity-programming/
编者:在iOS-MultipeerConnectivity框架开发(一)中,我们给出了MultipeerConnectivity框架的介绍和演示应用程序内置的聊天功能。MultipeerConnectivity框架是一个许多新的框架介绍了iOS 7。正如在iOS-MultipeerConnectivity框架开发(一)中所看到的,该框架允许开发人员可以轻松地建立附近的设备之间的通信和实现数据交换的功能。在本系列的第2部分,让我们继续探索Multipeer连接框架和如何实现文件共享功能。
我们将继续在演示应用程序工作。如果你还没有读过教程系列的第一部分,你就回去看看吧。注:第一部分demo下载:http://download.csdn.net/detail/u014220518/9454625
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/ec75e298bff2d834405c8cadcc2f71ab.jpg)
UILabel
Frame: X=20, Y=20, Width=280, Height=21
Text: My files:
UITableView
Frame: X=0, Y=49, Width=320, Height=519
其次,在表格视图添加一个UITableViewCell对象,并设置行高值80。添加下面的子视图它:
UILabel
Frame: X=20, Y=8, Width=280, Height=21
Tag: 100
UILabel
Frame: X=20, Y=37, Width=280, Height=21
Tag: 200
Color: Light Gray
UIProgressView
Frame: X=20, Y=66, Width=280, Height=2
Tag: 300
最后一步,选择UITableViewCell,并在该工具窗格属性检查器中设置newfilecellidentifier值的标识符字段、视图单元格部分下。我们将使用这个标识符将细胞以后的代码。
这里是如何在所有添加了所有这些控件的场景应该看起来像:
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/11df2dd58f8e5b3602ff2bcf8ce8a257.jpg)
现在,让我们创建一个IBOutlet属性连接到表格视图。打开secondviewcontroller .h文件并添加下一个声明:
另外,在这个文件里面,我们需要执行一些代理协议,如下所示:
回到Interface Builder界面连接tblFiles属性表的视图。
去的SecondViewController.m文件的顶部导入文件AppDelegate. h:
下一步,将一个对象声明为接口的私有部分:
最后,实例化的viewDidLoad方法:
创建示例文件sample_file1.txt和sample_file2.txt,然后将它们添加到项目中。
这两个文件,连同任何文件将从其他同行转移,应该在应用程序文件目录。此外,在我们开始发送和接收任何文件之前,我们需要在我们的表格视图中显示它们。
因此,让我们开始着手进行这一切,我们的首要任务是将示例文件复制到文档目录。我们将在下一步创建的私有方法来做,但是首先,让我们在接口的私有部分中声明它。除了方法声明,我们还将声明一个NSString对象保持文件目录路径:
让我们现在去执行:
首先,我们指定和保持文件目录路径的documentsdirectory对象。这是必要的,因为我们需要它几次。下一步,指定样本文件的目标路径,如果它们中的每一个存在于文档目录中。如果他们没有找到,那么他们被复制到documents ,如果出现任何错误(希望不是),我们只需记录它的描述。
现在我们要把它,和合适的地点是在viewDidLoad方法。
从现在开始,每一次视图控制器被加载我们的应用程序都会在文件目录中查找这些文件,如果不存在,将复制它们。
关于表视图,我们希望它能够显示所有现有的文件,所以我们能够利用它们,并将它们发送给其他对等节点。这意味着随后,我们需要使用一个数组作为表视图的数据源,该数组必须包含在文件目录中存在的所有文件。所以,让我们去声明一个nsmutablearray对象:
我们应该如何添加对象到arrfiles阵列?简单地通过读取文件目录的所有文件,并得到他们的名字。因为这个原因,我们需要一个更私人的方法来做这。让我们声明它:
一直往前走,它的实现,我们将使用一个nsfilemanager对象来获取文件的目录的所有内容作为一个NSArray对象,然后返回这个数组:
这种方法没有什么困难。如果出现任何错误,我们只是向日志显示描述。现在,是时候将对象添加到第一次的arrfiles阵列,这将发生在viewDidLoad方法。
我们已准备在表格视图上显示数据。首先,让我们自行委托和数据源,在viewDidLoad:
让我们的样本文件出现在表格视图的视图控制器的负载,我们需要迫使它做我们的arrfiles阵后有其价值。因此,仅仅增加了这条线在viewDidLoad方法:
在你运行和测试,我们在这个视图控制器做了这么远,你需要执行的委托和数据源所需的表格视图的方法。所以,他们在这里:
如果你想,运行应用程序来测试它,但你现在看到的唯一的东西是示例文件的列表。
让我们继续以使应用程序发送一个文件,一旦它被选中。我们实际上要发生的事情,是在点击一个表视图行时,一个列表中的所有对等点出现,所以我们选择的对等点,选择的文件应该被发送到。为便于我们在这个例子中,我们将使用UIActionSheet的对象,每个按钮,它将代表一个同伴。我们希望这个动作片每次都出现在一排,让我们看看这是怎么做的:
我们开始实施:TableView didselectrowatindexpath:通过创建一个行动表对象的方法,为选定的文件名作为标题。可能看起来很奇怪,我们设置了无价值的所有按钮的标题,但这是在目的。如果我们能够创建一个无终止的字符串,它将包含所有的对等显示名称,并将它们设为按钮标题,这样的话,我们将非常少量,但不幸的是,没有办法做到这一点。因此,有一个循环,在这里我们添加每个单点的一个由一个作为一个按钮的动作片。右后,我们设置了取消按钮,这是必要的情况下,我们只想关闭的动作片,最后我们显示它。
使用最后的2行,我们将在2个私有成员中保留选定的文件名和选定行,因为我们需要知道这些值之后。Xcode将抛出一个错误,因为我们还没有宣布他们,所以让我们现在就做:
现在每次选择文件的时候,都会出现一个action,包含每个点的名字,所以我们可以选择文件的收件人。但是在我们有一个同行选择的行动表后,会发生什么?嗯,没有,因为我们还没有实施任何行为。
我们需要实现actionsheet:clickedbuttonatindex:委托方法(这也是为什么我们采取uiactionsheetdelegate协议之前)。在那里,有检查,对等名称已被挖掘并没有取消按钮后,我们将调用另一个新的美国Multipeer连接框架,采用sendresourceaturl:withname:withcompletionhandler等:。其实,这是一个类的方法的mcsession。在完成处理程序中,我们将检查在发送过程中是否发生错误,或者如果该文件已成功发送。在任何情况下,我们会让我们知道结果。
要来这里的一个重要的事实是,这个方法返回一个nsprogress对象。nsprogress是iOS 7的一个新类,所以参观苹果的文档,如果你想知道更多关于它。无论如何,我们关心的结果的方法,因为这是我们唯一的方式来跟踪发送进度和更新我们的用户界面显示一个百分比值完成的整个过程。然而,这里有一个大陷阱,这就是我们的接口将冻结,如果我们调用这个方法的主要线程。不要担心,因为这是一个容易的障碍,克服。我们只会做我们的整个工作在一个dispatch_async块,所以我们的界面将在发送文件保持响应。
在我们看到它的实施之前,我们只需要一个最后的说明。因为这是一个演示程序,我们想知道什么文件来自其他同行,所以我们确保我们的代码真的工作。因此,当你在执行权的下一个,我们稍微修改文件名,通过增加对等的显示名称。
实施:
让我点一些关于这个代码片段的东西:
1.任何错误说明都只是记录,但在成功发送的情况下,我们向用户显示一个警告视图。请注意,此代码在第二队列运行,所以我们在应用程序的主线程上显示了警告视图。
2.你可能会想知道这条线是什么,只是正确的后,警惕的看法:
好的,正如你会发现自己的,正如我已经说过的,一个百分比值将显示在发送过程中选择的文件名旁边,表示整个过程。发送完成后,我们需要再次显示文件名,所以这就是我们在这里做的。我们只需更新数组和表视图,然后将合并的文件名和进度值替换为单个文件名。更进一步,这显然表明为什么我们把选中的行和文件名的selectedrow和selectedfile成员分别。
3.我们在哪里记录的进展?没有没有,是说nsprogress类包含一个属性命名fractioncompleted带给我们进步的双重价值是必要的。此外,所有nsprogress类的属性,包括fractioncompleted,是KVO(键值观察),所以我们必须遵守本物业的任何值的变化和更新适当的界面。
所以,说了这么多,很明显,我们的下一个目标是观察进步的fractioncompleted价值。要做到这一点,在dispatch_async块关闭添加下一个代码片段的权利:
用这种方法,我们注册我们的类与进展对象,所以我们可以观察到任何变化。
现在,执行下一个,所以我们通知的fractioncompleted属性值的变化。
正如你看到的,我们创建了一个新的字符串值,该值包含选定的文件名和当前进展的百分比值。该字符串将在数组中的特定索引替换现有的对象,并且在主线程上更新了表视图,所以它反映了发送进度。
如果你感觉如此,去给它一个尝试。发送文件和观看它工作。接收方也不会接受任何东西,因为我们已经实现了发送者的一面,但不是接收器的时间。
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/b62bee8702a69eb3ef00c2963aaf4850.jpg)
现在让我们把焦点放在一个文件被接收时应采取的行动。在这种情况下,我们将使用我们先前在界面生成器中添加的表视图单元原型。当一个文件接收正在进行中,我们将在表格的最后一行显示这样一个单元格,显示该文件的名称、发送端和使用进度视图对象的进度。当一个文件已被成功接收时,这个单元格将被一个默认的、正常的单元格所替换,该单元格将显示该文件名,并将其显示为示例文件所示。
打开mcmanager。M文件,并定位会话:didstartreceivingresourcewithname:frompeer:withprogress:委托方法。它的名字清楚地说明它的目的,我们将使用它来跟踪进展情况,而新的文件正在接收。在这里,我们将采取行动,就像我们在前两届代表的方法,只需到NSDictionary对象中添加参数值和发布新的通知。
有更多的东西,我们会在这里做的。我们想看的是收到的文件的进度和相应地更新我们的进展,我们将与该参数的nsprogress对象注册类,所以我们可以观察到任何变化的fractioncompleted财产所在地。其实,我们要做的同样的事情,在文件发送功能的实现,我们以前,我们还观察fractioncompleted更改代码。所以,在这里:
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/b62bee8702a69eb3ef00c2963aaf4850.jpg)
如你所见,我们也使用dispatch_async块观察进展。
现在,实施下一个方法,所以我们会通知任何进展情况的变化:
每一次进步都会改变,我们会发布一个新的通知。
让我们回到secondviewcontroller。M文件,现在,让我们与这两个新的通知的处理。在viewDidLoad方法添加下一个代码段:
didstartreceivingresourcewithnotification:和updatereceivingprogresswithnotification:都是私人的方法,我们将实施右下。让我们先将它们声明为接口的私有部分:
让我们来实现这些方法:
我们在这里做什么是很清楚的:我们将通知用户信息字典作为对象的arrfiles阵列,我们加载表格视图的数据,新的文件名,发送者和项目被显示的初始值。但他们真的会被展示吗?答案是直到我们更新TableView:cellForRowAtIndexPath:表格视图的方法,因为它是目前能够显示的字符串值在默认的细胞类型。让我们先看看代码:
这里是交易:我们的策略是检查该类中的每个对象的arrfiles阵列存在。如果它是一个NSString的价值,那么我们已经写好的代码执行。然而,如果数组的即将被处理的对象是NSDictionary类,然后我们将原型细胞使用标识符建立在界面生成器,然后根据每个子视图的标签值添加到它,我们把字典值,让他们从字典对象提取后。
除此之外,还改变了以下的方法:
这是必要的,所以最后一行有适当的高度时,一个文件被接收,一切都要正确显示。
如果你运行这个应用程序,你会看到,当一个文件开始被接收,一个新的行出现在表视图,但你会注意到,有没有任何进展。那是因为我们还没有实现的updatereceivingprogresswithnotification:私有方法呢,现在是正确的时间去做它。
实际上在这里发生的很简单。在arrfiles阵列存在的字典,我们得到的文件名和同伴的显示名称,并使用更新的进展,我们把所有这三个对象到一个新的词典。然后,我们替换新的数组的最后一个索引中的现有的字典,最后我们重新加载表视图。
如果你运行它,你会看到,当一个文件被接收到的进展视图完美显示的进展。
最后一件事是要做的,就是在文件目录下保存文件。在本教程的最后一次,让我们打开mcmanager。M文件和我们走到会话:didfinishreceivingresourcewithname:frompeer:aturl:误:委托方法。当一个资源被接收时,这一个被调用,并且像往常一样,我们将发布一个新的通知。
让我们回到secondviewcontroller。M文件,让我们的课堂能够观察这个通知在viewDidLoad方法:
同样的方法让我们实现方法:didFinishReceivingResourceWithNotification: method:
正如您将看到的,我们复制的文件(一般的资源)从临时被存储到文件目录。在方法结束时,我们将所有的物体从arrfiles阵列,我们重新读取整个文件目录内容。没有什么特别的,应该进一步讨论,所以在这里它是:
文件共享功能已经完成,这也意味着我们的演示程序已经完全准备好了!
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/14cd2bc786d34a4f210935d608f5f307.jpg)
编者:在iOS-MultipeerConnectivity框架开发(一)中,我们给出了MultipeerConnectivity框架的介绍和演示应用程序内置的聊天功能。MultipeerConnectivity框架是一个许多新的框架介绍了iOS 7。正如在iOS-MultipeerConnectivity框架开发(一)中所看到的,该框架允许开发人员可以轻松地建立附近的设备之间的通信和实现数据交换的功能。在本系列的第2部分,让我们继续探索Multipeer连接框架和如何实现文件共享功能。
进入教程
我们将继续在演示应用程序工作。如果你还没有读过教程系列的第一部分,你就回去看看吧。注:第一部分demo下载:http://download.csdn.net/detail/u014220518/9454625![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/ec75e298bff2d834405c8cadcc2f71ab.jpg)
设置用户文件共享界面
在此之前,我们已经实现了聊天功能。现在让我们去实现我们的示例应用程序的第二大功能,文件共享。像往常一样,我们将开始搭建文件共享的界面,所以 点击main.storyboard文件让它出现。首先,进入第二个视图控制器场景,选择和删除它的默认内容。然后,从对象库中添加下面的控件,同时设置下所描述的属性:UILabel
Frame: X=20, Y=20, Width=280, Height=21
Text: My files:
UITableView
Frame: X=0, Y=49, Width=320, Height=519
其次,在表格视图添加一个UITableViewCell对象,并设置行高值80。添加下面的子视图它:
UILabel
Frame: X=20, Y=8, Width=280, Height=21
Tag: 100
UILabel
Frame: X=20, Y=37, Width=280, Height=21
Tag: 200
Color: Light Gray
UIProgressView
Frame: X=20, Y=66, Width=280, Height=2
Tag: 300
最后一步,选择UITableViewCell,并在该工具窗格属性检查器中设置newfilecellidentifier值的标识符字段、视图单元格部分下。我们将使用这个标识符将细胞以后的代码。
这里是如何在所有添加了所有这些控件的场景应该看起来像:
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/11df2dd58f8e5b3602ff2bcf8ce8a257.jpg)
现在,让我们创建一个IBOutlet属性连接到表格视图。打开secondviewcontroller .h文件并添加下一个声明:
@interface SecondViewController : UIViewController @property (weak, nonatomic) IBOutlet UITableView *tblFiles; @end
另外,在这个文件里面,我们需要执行一些代理协议,如下所示:
@interface SecondViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, UIActionSheetDelegate>
回到Interface Builder界面连接tblFiles属性表的视图。
分享文件
我们将开始实施secondviewcontroller类就像我们的聊天功能,这意味着我们要先声明和实例化应用程序委托对象,这样我们才能够访问mcmanager对象。去的SecondViewController.m文件的顶部导入文件AppDelegate. h:
#import "AppDelegate.h"
下一步,将一个对象声明为接口的私有部分:
@interface SecondViewController () @property (nonatomic, strong) AppDelegate *appDelegate; @end
最后,实例化的viewDidLoad方法:
1 2 3 4 5 6 | - (void)viewDidLoad { [super viewDidLoad]; _appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; } |
这两个文件,连同任何文件将从其他同行转移,应该在应用程序文件目录。此外,在我们开始发送和接收任何文件之前,我们需要在我们的表格视图中显示它们。
因此,让我们开始着手进行这一切,我们的首要任务是将示例文件复制到文档目录。我们将在下一步创建的私有方法来做,但是首先,让我们在接口的私有部分中声明它。除了方法声明,我们还将声明一个NSString对象保持文件目录路径:
1 2 3 4 5 6 7 8 | @interface SecondViewController () ... @property (nonatomic, strong) NSString *documentsDirectory; -(void)copySampleFilesToDocDirIfNeeded; @end |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | -(void)copySampleFilesToDocDirIfNeeded{ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); _documentsDirectory = [[NSString alloc] initWithString:[paths objectAtIndex:0]]; NSString *file1Path = [_documentsDirectory stringByAppendingPathComponent:@"sample_file1.txt"]; NSString *file2Path = [_documentsDirectory stringByAppendingPathComponent:@"sample_file2.txt"]; NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error; if (![fileManager fileExistsAtPath:file1Path] || ![fileManager fileExistsAtPath:file2Path]) { [fileManager copyItemAtPath:[[NSBundle mainBundle] pathForResource:@"sample_file1" ofType:@"txt"] toPath:file1Path error:&error]; if (error) { NSLog(@"%@", [error localizedDescription]); return; } [fileManager copyItemAtPath:[[NSBundle mainBundle] pathForResource:@"sample_file2" ofType:@"txt"] toPath:file2Path error:&error]; if (error) { NSLog(@"%@", [error localizedDescription]); return; } } } |
现在我们要把它,和合适的地点是在viewDidLoad方法。
1 2 3 4 5 | - (void)viewDidLoad { ... [self copySampleFilesToDocDirIfNeeded]; } |
关于表视图,我们希望它能够显示所有现有的文件,所以我们能够利用它们,并将它们发送给其他对等节点。这意味着随后,我们需要使用一个数组作为表视图的数据源,该数组必须包含在文件目录中存在的所有文件。所以,让我们去声明一个nsmutablearray对象:
1 2 3 4 5 | @interface SecondViewController () ... @property (nonatomic, strong) NSMutableArray *arrFiles; @end |
1 2 3 4 5 6 | @interface SecondViewController () ... -(NSArray *)getAllDocDirFiles; @end |
1 2 3 4 5 6 7 8 9 10 11 12 | -(NSArray *)getAllDocDirFiles{ NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error; NSArray *allFiles = [fileManager contentsOfDirectoryAtPath:_documentsDirectory error:&error]; if (error) { NSLog(@"%@", [error localizedDescription]); return nil; } return allFiles; } |
1 2 3 4 5 | - (void)viewDidLoad { ... _arrFiles = [[NSMutableArray alloc] initWithArray:[self getAllDocDirFiles]]; } |
1 2 3 4 5 6 | - (void)viewDidLoad { ... [_tblFiles setDelegate:self]; [_tblFiles setDataSource:self]; } |
1 2 3 4 5 | - (void)viewDidLoad { ... [_tblFiles reloadData]; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ return 1; } -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return [_arrFiles count]; } -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ UITableViewCell *cell; cell = [tableView dequeueReusableCellWithIdentifier:@"CellIdentifier"]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"CellIdentifier"]; [cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator]; } cell.textLabel.text = [_arrFiles objectAtIndex:indexPath.row]; [[cell textLabel] setFont:[UIFont systemFontOfSize:14.0]]; return cell; } -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ return 60.0; } |
让我们继续以使应用程序发送一个文件,一旦它被选中。我们实际上要发生的事情,是在点击一个表视图行时,一个列表中的所有对等点出现,所以我们选择的对等点,选择的文件应该被发送到。为便于我们在这个例子中,我们将使用UIActionSheet的对象,每个按钮,它将代表一个同伴。我们希望这个动作片每次都出现在一排,让我们看看这是怎么做的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ NSString *selectedFile = [_arrFiles objectAtIndex:indexPath.row]; UIActionSheet *confirmSending = [[UIActionSheet alloc] initWithTitle:selectedFile delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil]; for (int i=0; i < [[_appDelegate.mcManager.session connectedPeers] count]; i++) { [confirmSending addButtonWithTitle:[[[_appDelegate.mcManager.session connectedPeers] objectAtIndex:i] displayName]]; } [confirmSending setCancelButtonIndex:[confirmSending addButtonWithTitle:@"Cancel"]]; [confirmSending showInView:self.view]; _selectedFile = [_arrFiles objectAtIndex:indexPath.row]; _selectedRow = indexPath.row; } |
使用最后的2行,我们将在2个私有成员中保留选定的文件名和选定行,因为我们需要知道这些值之后。Xcode将抛出一个错误,因为我们还没有宣布他们,所以让我们现在就做:
1 2 3 4 5 | @interface SecondViewController () ... @property (nonatomic, strong) NSString *selectedFile; @property (nonatomic) NSInteger selectedRow; @end |
我们需要实现actionsheet:clickedbuttonatindex:委托方法(这也是为什么我们采取uiactionsheetdelegate协议之前)。在那里,有检查,对等名称已被挖掘并没有取消按钮后,我们将调用另一个新的美国Multipeer连接框架,采用sendresourceaturl:withname:withcompletionhandler等:。其实,这是一个类的方法的mcsession。在完成处理程序中,我们将检查在发送过程中是否发生错误,或者如果该文件已成功发送。在任何情况下,我们会让我们知道结果。
要来这里的一个重要的事实是,这个方法返回一个nsprogress对象。nsprogress是iOS 7的一个新类,所以参观苹果的文档,如果你想知道更多关于它。无论如何,我们关心的结果的方法,因为这是我们唯一的方式来跟踪发送进度和更新我们的用户界面显示一个百分比值完成的整个过程。然而,这里有一个大陷阱,这就是我们的接口将冻结,如果我们调用这个方法的主要线程。不要担心,因为这是一个容易的障碍,克服。我们只会做我们的整个工作在一个dispatch_async块,所以我们的界面将在发送文件保持响应。
在我们看到它的实施之前,我们只需要一个最后的说明。因为这是一个演示程序,我们想知道什么文件来自其他同行,所以我们确保我们的代码真的工作。因此,当你在执行权的下一个,我们稍微修改文件名,通过增加对等的显示名称。
实施:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | -(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{ if (buttonIndex != [[_appDelegate.mcManager.session connectedPeers] count]) { NSString *filePath = [_documentsDirectory stringByAppendingPathComponent:_selectedFile]; NSString *modifiedName = [NSString stringWithFormat:@"%@_%@", _appDelegate.mcManager.peerID.displayName, _selectedFile]; NSURL *resourceURL = [NSURL fileURLWithPath:filePath]; dispatch_async(dispatch_get_main_queue(), ^{ NSProgress *progress = [_appDelegate.mcManager.session sendResourceAtURL:resourceURL withName:modifiedName toPeer:[[_appDelegate.mcManager.session connectedPeers] objectAtIndex:buttonIndex] withCompletionHandler:^(NSError *error) { if (error) { NSLog(@"Error: %@", [error localizedDescription]); } else{ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"MCDemo" message:@"File was successfully sent." delegate:self cancelButtonTitle:nil otherButtonTitles:@"Great!", nil]; [alert performSelectorOnMainThread:@selector(show) withObject:nil waitUntilDone:NO]; [_arrFiles replaceObjectAtIndex:_selectedRow withObject:_selectedFile]; [_tblFiles performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO]; } }]; }); } } |
1.任何错误说明都只是记录,但在成功发送的情况下,我们向用户显示一个警告视图。请注意,此代码在第二队列运行,所以我们在应用程序的主线程上显示了警告视图。
2.你可能会想知道这条线是什么,只是正确的后,警惕的看法:
1 2 | [_arrFiles replaceObjectAtIndex:_selectedRow withObject:_selectedFile]; [_tblFiles performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO]; |
3.我们在哪里记录的进展?没有没有,是说nsprogress类包含一个属性命名fractioncompleted带给我们进步的双重价值是必要的。此外,所有nsprogress类的属性,包括fractioncompleted,是KVO(键值观察),所以我们必须遵守本物业的任何值的变化和更新适当的界面。
所以,说了这么多,很明显,我们的下一个目标是观察进步的fractioncompleted价值。要做到这一点,在dispatch_async块关闭添加下一个代码片段的权利:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | -(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{ if (buttonIndex != [[_appDelegate.mcManager.session connectedPeers] count]) { ... dispatch_async(dispatch_get_main_queue(), ^{ NSProgress *progress = ... [progress addObserver:self forKeyPath:@"fractionCompleted" options:NSKeyValueObservingOptionNew context:nil]; }); } } |
现在,执行下一个,所以我们通知的fractioncompleted属性值的变化。
1 2 3 4 5 6 7 8 9 10 | -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ NSString *sendingMessage = [NSString stringWithFormat:@"%@ - Sending %.f%%", _selectedFile, [(NSProgress *)object fractionCompleted] * 100 ]; [_arrFiles replaceObjectAtIndex:_selectedRow withObject:sendingMessage]; [_tblFiles performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO]; } |
如果你感觉如此,去给它一个尝试。发送文件和观看它工作。接收方也不会接受任何东西,因为我们已经实现了发送者的一面,但不是接收器的时间。
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/b62bee8702a69eb3ef00c2963aaf4850.jpg)
现在让我们把焦点放在一个文件被接收时应采取的行动。在这种情况下,我们将使用我们先前在界面生成器中添加的表视图单元原型。当一个文件接收正在进行中,我们将在表格的最后一行显示这样一个单元格,显示该文件的名称、发送端和使用进度视图对象的进度。当一个文件已被成功接收时,这个单元格将被一个默认的、正常的单元格所替换,该单元格将显示该文件名,并将其显示为示例文件所示。
打开mcmanager。M文件,并定位会话:didstartreceivingresourcewithname:frompeer:withprogress:委托方法。它的名字清楚地说明它的目的,我们将使用它来跟踪进展情况,而新的文件正在接收。在这里,我们将采取行动,就像我们在前两届代表的方法,只需到NSDictionary对象中添加参数值和发布新的通知。
有更多的东西,我们会在这里做的。我们想看的是收到的文件的进度和相应地更新我们的进展,我们将与该参数的nsprogress对象注册类,所以我们可以观察到任何变化的fractioncompleted财产所在地。其实,我们要做的同样的事情,在文件发送功能的实现,我们以前,我们还观察fractioncompleted更改代码。所以,在这里:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | -(void)session:(MCSession *)session didStartReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID withProgress:(NSProgress *)progress{ NSDictionary *dict = @{@"resourceName" : resourceName, @"peerID" : peerID, @"progress" : progress }; [[NSNotificationCenter defaultCenter] postNotificationName:@"MCDidStartReceivingResourceNotification" object:nil userInfo:dict]; dispatch_async(dispatch_get_main_queue(), ^{ [progress addObserver:self forKeyPath:@"fractionCompleted" options:NSKeyValueObservingOptionNew context:nil]; }); } |
![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/b62bee8702a69eb3ef00c2963aaf4850.jpg)
如你所见,我们也使用dispatch_async块观察进展。
现在,实施下一个方法,所以我们会通知任何进展情况的变化:
1 2 3 4 5 | -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ [[NSNotificationCenter defaultCenter] postNotificationName:@"MCReceivingProgressNotification" object:nil userInfo:@{@"progress": (NSProgress *)object}]; } |
让我们回到secondviewcontroller。M文件,现在,让我们与这两个新的通知的处理。在viewDidLoad方法添加下一个代码段:
1 2 3 4 5 6 7 8 9 10 11 12 13 | - (void)viewDidLoad { ... [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didStartReceivingResourceWithNotification:) name:@"MCDidStartReceivingResourceNotification" object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateReceivingProgressWithNotification:) name:@"MCReceivingProgressNotification" object:nil]; } |
1 2 3 4 5 | @interface SecondViewController () ... -(void)didStartReceivingResourceWithNotification:(NSNotification *)notification; -(void)updateReceivingProgressWithNotification:(NSNotification *)notification; @end |
1 2 3 4 | -(void)didStartReceivingResourceWithNotification:(NSNotification *)notification{ [_arrFiles addObject:[notification userInfo]]; [_tblFiles performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO]; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ UITableViewCell *cell; if ([[_arrFiles objectAtIndex:indexPath.row] isKindOfClass:[NSString class]]) { cell = [tableView dequeueReusableCellWithIdentifier:@"CellIdentifier"]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"CellIdentifier"]; [cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator]; } cell.textLabel.text = [_arrFiles objectAtIndex:indexPath.row]; [[cell textLabel] setFont:[UIFont systemFontOfSize:14.0]]; } else{ cell = [tableView dequeueReusableCellWithIdentifier:@"newFileCellIdentifier"]; NSDictionary *dict = [_arrFiles objectAtIndex:indexPath.row]; NSString *receivedFilename = [dict objectForKey:@"resourceName"]; NSString *peerDisplayName = [[dict objectForKey:@"peerID"] displayName]; NSProgress *progress = [dict objectForKey:@"progress"]; [(UILabel *)[cell viewWithTag:100] setText:receivedFilename]; [(UILabel *)[cell viewWithTag:200] setText:[NSString stringWithFormat:@"from %@", peerDisplayName]]; [(UIProgressView *)[cell viewWithTag:300] setProgress:progress.fractionCompleted]; } return cell; } |
除此之外,还改变了以下的方法:
1 2 3 4 5 6 7 8 | -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ if ([[_arrFiles objectAtIndex:indexPath.row] isKindOfClass:[NSString class]]) { return 60.0; } else{ return 80.0; } } |
如果你运行这个应用程序,你会看到,当一个文件开始被接收,一个新的行出现在表视图,但你会注意到,有没有任何进展。那是因为我们还没有实现的updatereceivingprogresswithnotification:私有方法呢,现在是正确的时间去做它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | -(void)updateReceivingProgressWithNotification:(NSNotification *)notification{ NSProgress *progress = [[notification userInfo] objectForKey:@"progress"]; NSDictionary *dict = [_arrFiles objectAtIndex:(_arrFiles.count - 1)]; NSDictionary *updatedDict = @{@"resourceName" : [dict objectForKey:@"resourceName"], @"peerID" : [dict objectForKey:@"peerID"], @"progress" : progress }; [_arrFiles replaceObjectAtIndex:_arrFiles.count - 1 withObject:updatedDict]; [_tblFiles performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO]; } |
如果你运行它,你会看到,当一个文件被接收到的进展视图完美显示的进展。
最后一件事是要做的,就是在文件目录下保存文件。在本教程的最后一次,让我们打开mcmanager。M文件和我们走到会话:didfinishreceivingresourcewithname:frompeer:aturl:误:委托方法。当一个资源被接收时,这一个被调用,并且像往常一样,我们将发布一个新的通知。
1 2 3 4 5 6 7 8 9 10 11 12 | -(void)session:(MCSession *)session didFinishReceivingResourceWithName:(NSString *)resourceName fromPeer:(MCPeerID *)peerID atURL:(NSURL *)localURL withError:(NSError *)error{ NSDictionary *dict = @{@"resourceName" : resourceName, @"peerID" : peerID, @"localURL" : localURL }; [[NSNotificationCenter defaultCenter] postNotificationName:@"didFinishReceivingResourceNotification" object:nil userInfo:dict]; } |
1 2 3 4 5 6 7 | { ... [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didFinishReceivingResourceWithNotification:) name:@"didFinishReceivingResourceNotification" object:nil]; } |
1 2 3 4 | @interface SecondViewController () ... -(void)didFinishReceivingResourceWithNotification:(NSNotification *)notification; @end |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | -(void)didFinishReceivingResourceWithNotification:(NSNotification *)notification{ NSDictionary *dict = [notification userInfo]; NSURL *localURL = [dict objectForKey:@"localURL"]; NSString *resourceName = [dict objectForKey:@"resourceName"]; NSString *destinationPath = [_documentsDirectory stringByAppendingPathComponent:resourceName]; NSURL *destinationURL = [NSURL fileURLWithPath:destinationPath]; NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error; [fileManager copyItemAtURL:localURL toURL:destinationURL error:&error]; if (error) { NSLog(@"%@", [error localizedDescription]); } [_arrFiles removeAllObjects]; _arrFiles = nil; _arrFiles = [[NSMutableArray alloc] initWithArray:[self getAllDocDirFiles]]; [_tblFiles performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO]; } |
编译和运行APP
如果你还没有运行应用程序,我会感到惊讶。然而,如果这是你的情况,现在是最好的和适当的时间去做它。尝试使用一对夫妇的设备,或设备和模拟。连接,发送文本信息和共享文件。![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/14cd2bc786d34a4f210935d608f5f307.jpg)
总结
MultipeerConnectivity框架是iOS 7全新的特征。在本系列教程中,我们仅从几个潜在的问题中走了,因为他们不在这里停留。还有很多东西,一个可以探索,比如如何考虑安全问题。更进一步,对新类型的应用程序的可能性现在是可用的,只是一个限制,你的想象力。这个框架提供了很好的工具,以及它们如何被使用,这取决于每个开发者的欲望。通过这个教程,我想对那些想对付MultipeerConnectivity提供了一个介绍点,我希望你找到真正有用的。快乐的Multipeer连接!![](https://oscdn.geek-share.com/Uploads/Images/Content/201603/b62bee8702a69eb3ef00c2963aaf4850.jpg)
相关文章推荐
- iOS----------SDWebimage源码解析(1)
- iOS判断邮箱,手机号码,车牌号是否合法
- iOS游戏控制手柄
- IOS开发之深拷贝与浅拷贝(mutableCopy与Copy)详解
- iOS 点击远程通知消息,如何跳转到指定页面(控制器)
- iOS图片裁剪
- iOS同步后couldn't load project
- ios7状态栏重叠问题的解决方法
- iOS 点击远程通知消息,跳转到指定页面 (控制器)
- IOS 核心动画之CAKeyframeAnimation - iBaby
- IOS的WebView请求远程html并加载本地资源
- IOS学习 scrollView的代理
- 在iOS开发中如何优雅地进行图片缩放?
- iOS 各种报错集合
- iOS后台运行机制1
- iOS WebSocket详解
- NETBIOS名 和 Host名的不同
- IOS字符串自动计算文本的宽和高
- ios开发——日常之XCode 文件后面带有问号的问题怎么解决??
- IOS There was an internal API error 错误