您的位置:首页 > 移动开发 > IOS开发

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



设置用户文件共享界面

在此之前,我们已经实现了聊天功能。现在让我们去实现我们的示例应用程序的第二大功能,文件共享。像往常一样,我们将开始搭建文件共享的界面,所以 点击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值的标识符字段、视图单元格部分下。我们将使用这个标识符将细胞以后的代码。

这里是如何在所有添加了所有这些控件的场景应该看起来像:



现在,让我们创建一个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];

}

创建示例文件sample_file1.txt和sample_file2.txt,然后将它们添加到项目中。
这两个文件,连同任何文件将从其他同行转移,应该在应用程序文件目录。此外,在我们开始发送和接收任何文件之前,我们需要在我们的表格视图中显示它们。
因此,让我们开始着手进行这一切,我们的首要任务是将示例文件复制到文档目录。我们将在下一步创建的私有方法来做,但是首先,让我们在接口的私有部分中声明它。除了方法声明,我们还将声明一个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;
}

}
}

首先,我们指定和保持文件目录路径的documentsdirectory对象。这是必要的,因为我们需要它几次。下一步,指定样本文件的目标路径,如果它们中的每一个存在于文档目录中。如果他们没有找到,那么他们被复制到documents ,如果出现任何错误(希望不是),我们只需记录它的描述。

现在我们要把它,和合适的地点是在viewDidLoad方法。

1
2
3
4
5

-
(void)viewDidLoad

{
...

[self
copySampleFilesToDocDirIfNeeded];
}

从现在开始,每一次视图控制器被加载我们的应用程序都会在文件目录中查找这些文件,如果不存在,将复制它们。

关于表视图,我们希望它能够显示所有现有的文件,所以我们能够利用它们,并将它们发送给其他对等节点。这意味着随后,我们需要使用一个数组作为表视图的数据源,该数组必须包含在文件目录中存在的所有文件。所以,让我们去声明一个nsmutablearray对象:

1
2
3
4
5

@interface
SecondViewController
()

...

@property
(nonatomic,
strong)
NSMutableArray
*arrFiles;
@end

我们应该如何添加对象到arrfiles阵列?简单地通过读取文件目录的所有文件,并得到他们的名字。因为这个原因,我们需要一个更私人的方法来做这。让我们声明它:

1
2
3
4
5
6

@interface
SecondViewController
()

...

-(NSArray
*)getAllDocDirFiles;

@end

一直往前走,它的实现,我们将使用一个nsfilemanager对象来获取文件的目录的所有内容作为一个NSArray对象,然后返回这个数组:

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;

}

这种方法没有什么困难。如果出现任何错误,我们只是向日志显示描述。现在,是时候将对象添加到第一次的arrfiles阵列,这将发生在viewDidLoad方法。

1
2
3
4
5

-
(void)viewDidLoad

{
...

_arrFiles
=
[[NSMutableArray
alloc] initWithArray:[self
getAllDocDirFiles]];
}

我们已准备在表格视图上显示数据。首先,让我们自行委托和数据源,在viewDidLoad:

1
2
3
4
5
6

-
(void)viewDidLoad

{
...

[_tblFiles setDelegate:self];
[_tblFiles setDataSource:self];

}

让我们的样本文件出现在表格视图的视图控制器的负载,我们需要迫使它做我们的arrfiles阵后有其价值。因此,仅仅增加了这条线在viewDidLoad方法:

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;
}

我们开始实施:TableView didselectrowatindexpath:通过创建一个行动表对象的方法,为选定的文件名作为标题。可能看起来很奇怪,我们设置了无价值的所有按钮的标题,但这是在目的。如果我们能够创建一个无终止的字符串,它将包含所有的对等显示名称,并将它们设为按钮标题,这样的话,我们将非常少量,但不幸的是,没有办法做到这一点。因此,有一个循环,在这里我们添加每个单点的一个由一个作为一个按钮的动作片。右后,我们设置了取消按钮,这是必要的情况下,我们只想关闭的动作片,最后我们显示它。

使用最后的2行,我们将在2个私有成员中保留选定的文件名和选定行,因为我们需要知道这些值之后。Xcode将抛出一个错误,因为我们还没有宣布他们,所以让我们现在就做:

1
2
3
4
5

@interface
SecondViewController
()

...
@property
(nonatomic,
strong)
NSString
*selectedFile;

@property
(nonatomic)
NSInteger
selectedRow;
@end

现在每次选择文件的时候,都会出现一个action,包含每个点的名字,所以我们可以选择文件的收件人。但是在我们有一个同行选择的行动表后,会发生什么?嗯,没有,因为我们还没有实施任何行为。

我们需要实现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];

好的,正如你会发现自己的,正如我已经说过的,一个百分比值将显示在发送过程中选择的文件名旁边,表示整个过程。发送完成后,我们需要再次显示文件名,所以这就是我们在这里做的。我们只需更新数组和表视图,然后将合并的文件名和进度值替换为单个文件名。更进一步,这显然表明为什么我们把选中的行和文件名的selectedrow和selectedfile成员分别。

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];

}

正如你看到的,我们创建了一个新的字符串值,该值包含选定的文件名和当前进展的百分比值。该字符串将在数组中的特定索引替换现有的对象,并且在主线程上更新了表视图,所以它反映了发送进度。

如果你感觉如此,去给它一个尝试。发送文件和观看它工作。接收方也不会接受任何东西,因为我们已经实现了发送者的一面,但不是接收器的时间。



现在让我们把焦点放在一个文件被接收时应采取的行动。在这种情况下,我们将使用我们先前在界面生成器中添加的表视图单元原型。当一个文件接收正在进行中,我们将在表格的最后一行显示这样一个单元格,显示该文件的名称、发送端和使用进度视图对象的进度。当一个文件已被成功接收时,这个单元格将被一个默认的、正常的单元格所替换,该单元格将显示该文件名,并将其显示为示例文件所示。

打开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];

});
}



如你所见,我们也使用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];
}

didstartreceivingresourcewithnotification:和updatereceivingprogresswithnotification:都是私人的方法,我们将实施右下。让我们先将它们声明为接口的私有部分:

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];

}

我们在这里做什么是很清楚的:我们将通知用户信息字典作为对象的arrfiles阵列,我们加载表格视图的数据,新的文件名,发送者和项目被显示的初始值。但他们真的会被展示吗?答案是直到我们更新TableView:cellForRowAtIndexPath:表格视图的方法,因为它是目前能够显示的字符串值在默认的细胞类型。让我们先看看代码:

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;

}

这里是交易:我们的策略是检查该类中的每个对象的arrfiles阵列存在。如果它是一个NSString的价值,那么我们已经写好的代码执行。然而,如果数组的即将被处理的对象是NSDictionary类,然后我们将原型细胞使用标识符建立在界面生成器,然后根据每个子视图的标签值添加到它,我们把字典值,让他们从字典对象提取后。

除此之外,还改变了以下的方法:

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];

}

实际上在这里发生的很简单。在arrfiles阵列存在的字典,我们得到的文件名和同伴的显示名称,并使用更新的进展,我们把所有这三个对象到一个新的词典。然后,我们替换新的数组的最后一个索引中的现有的字典,最后我们重新加载表视图。

如果你运行它,你会看到,当一个文件被接收到的进展视图完美显示的进展。

最后一件事是要做的,就是在文件目录下保存文件。在本教程的最后一次,让我们打开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];

}

让我们回到secondviewcontroller。M文件,让我们的课堂能够观察这个通知在viewDidLoad方法:

1
2
3
4
5
6
7

{

...
[[NSNotificationCenter
defaultCenter] addObserver:self

selector:@selector(didFinishReceivingResourceWithNotification:)
name:@"didFinishReceivingResourceNotification"

object:nil];
}

同样的方法让我们实现方法:didFinishReceivingResourceWithNotification: method:

1
2
3
4

@interface
SecondViewController
()

...
-(void)didFinishReceivingResourceWithNotification:(NSNotification
*)notification;

@end

正如您将看到的,我们复制的文件(一般的资源)从临时被存储到文件目录。在方法结束时,我们将所有的物体从arrfiles阵列,我们重新读取整个文件目录内容。没有什么特别的,应该进一步讨论,所以在这里它是:

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

如果你还没有运行应用程序,我会感到惊讶。然而,如果这是你的情况,现在是最好的和适当的时间去做它。尝试使用一对夫妇的设备,或设备和模拟。连接,发送文本信息和共享文件。



总结

MultipeerConnectivity框架是iOS 7全新的特征。在本系列教程中,我们仅从几个潜在的问题中走了,因为他们不在这里停留。还有很多东西,一个可以探索,比如如何考虑安全问题。更进一步,对新类型的应用程序的可能性现在是可用的,只是一个限制,你的想象力。这个框架提供了很好的工具,以及它们如何被使用,这取决于每个开发者的欲望。通过这个教程,我想对那些想对付MultipeerConnectivity提供了一个介绍点,我希望你找到真正有用的。快乐的Multipeer连接!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: