├── .gitignore
├── 1.JPG
├── 2.JPG
├── 3.JPG
├── 4.gif
├── LICENSE
├── README.md
├── RWFileTransferDemo.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── RWFileTransferDemo
├── AppDelegate.h
├── AppDelegate.m
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Contents.json
│ ├── rw_select.imageset
│ │ ├── Contents.json
│ │ └── ST_Common_select_2.png
│ └── rw_unselect.imageset
│ │ ├── Contents.json
│ │ └── ST_Common_select_1.png
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── Base
│ ├── Controller
│ │ ├── RWBaseViewController.h
│ │ └── RWBaseViewController.m
│ └── ViewModel
│ │ ├── RWBaseViewModel.h
│ │ └── RWBaseViewModel.m
├── Category
│ ├── UIImage+Resize.h
│ ├── UIImage+Resize.m
│ ├── UIImage+Video.h
│ └── UIImage+Video.m
├── Common
│ ├── RWCommon.h
│ └── RWCommon.m
├── Config
│ ├── RWConfig.h
│ ├── RWParamsConfig.h
│ └── RWStringConfig.h
├── Info.plist
├── Model
│ ├── RWAlbumModel.h
│ └── RWAlbumModel.m
├── RWFileTransfer
│ ├── RWBrower
│ │ ├── RWBrowser.h
│ │ └── RWBrowser.m
│ ├── RWSessionManager
│ │ ├── RWSession.h
│ │ └── RWSession.m
│ ├── RWStream
│ │ ├── RWInputStream.h
│ │ ├── RWInputStream.m
│ │ ├── RWOutputStream.h
│ │ ├── RWOutputStream.m
│ │ ├── RWStream.h
│ │ └── RWStream.m
│ └── RWUserCenter
│ │ ├── RWUserCenter.h
│ │ └── RWUserCenter.m
├── RWHeader.pch
├── Section
│ ├── File
│ │ ├── ImageSelector
│ │ │ ├── Controller
│ │ │ │ ├── RWAlbumListViewController.h
│ │ │ │ ├── RWAlbumListViewController.m
│ │ │ │ ├── RWImageSelectorViewController.h
│ │ │ │ └── RWImageSelectorViewController.m
│ │ │ ├── Model
│ │ │ │ ├── RWPhotoModel.h
│ │ │ │ └── RWPhotoModel.m
│ │ │ ├── View
│ │ │ │ ├── RWAlbumListViewCell.h
│ │ │ │ ├── RWAlbumListViewCell.m
│ │ │ │ ├── RWAlbumListViewCell.xib
│ │ │ │ ├── RWAlbumTableView.h
│ │ │ │ ├── RWAlbumTableView.m
│ │ │ │ ├── RWPhotoCollectView.h
│ │ │ │ ├── RWPhotoCollectView.m
│ │ │ │ ├── RWPhotoViewCell.h
│ │ │ │ ├── RWPhotoViewCell.m
│ │ │ │ └── RWPhotoViewCell.xib
│ │ │ └── ViewModel
│ │ │ │ ├── RWAlbumListViewModel.h
│ │ │ │ ├── RWAlbumListViewModel.m
│ │ │ │ ├── RWAlbumViewModel.h
│ │ │ │ ├── RWAlbumViewModel.m
│ │ │ │ ├── RWImageSelectorViewModel.h
│ │ │ │ ├── RWImageSelectorViewModel.m
│ │ │ │ ├── RWimageViewModel.h
│ │ │ │ └── RWimageViewModel.m
│ │ └── VideoSelector
│ │ │ ├── Controller
│ │ │ ├── RWVideoListViewController.h
│ │ │ └── RWVideoListViewController.m
│ │ │ └── View
│ │ │ ├── RWVideoListViewCell.h
│ │ │ ├── RWVideoListViewCell.m
│ │ │ ├── RWVideoListViewCell.xib
│ │ │ ├── RWVideoTableView.h
│ │ │ └── RWVideoTableView.m
│ ├── Search
│ │ └── Controller
│ │ │ ├── SearchViewController.h
│ │ │ └── SearchViewController.m
│ ├── Transfer
│ │ ├── Controller
│ │ │ ├── TransferListViewController.h
│ │ │ └── TransferListViewController.m
│ │ ├── View
│ │ │ ├── RWTransferListCell.h
│ │ │ ├── RWTransferListCell.m
│ │ │ ├── RWTransferListView.h
│ │ │ └── RWTransferListView.m
│ │ └── ViewModel
│ │ │ ├── RWTransferCenter.h
│ │ │ ├── RWTransferCenter.m
│ │ │ ├── RWTransferListViewModel.h
│ │ │ ├── RWTransferListViewModel.m
│ │ │ ├── RWTransferViewModel.h
│ │ │ └── RWTransferViewModel.m
│ └── Wait
│ │ └── Controller
│ │ ├── WaitViewController.h
│ │ ├── WaitViewController.m
│ │ └── WaitViewController.xib
├── Tool
│ ├── RWDataTransfer
│ │ ├── RWDataTransfer.h
│ │ └── RWDataTransfer.m
│ ├── RWFileHandle
│ │ ├── RWFileHandle.h
│ │ └── RWFileHandle.m
│ ├── RWFileManager
│ │ ├── RWFileManager.h
│ │ └── RWFileManager.m
│ └── RWImageLoad
│ │ ├── RWImageLoad.h
│ │ └── RWImageLoad.m
├── ViewController.h
├── ViewController.m
└── main.m
├── RWFileTransferDemoTests
├── Info.plist
└── RWFileTransferDemoTests.m
└── RWFileTransferDemoUITests
├── Info.plist
└── RWFileTransferDemoUITests.m
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | # CocoaPods
32 | #
33 | # We recommend against adding the Pods directory to your .gitignore. However
34 | # you should judge for yourself, the pros and cons are mentioned at:
35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
36 | #
37 | # Pods/
38 |
39 | # Carthage
40 | #
41 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
42 | # Carthage/Checkouts
43 |
44 | Carthage/Build
45 |
46 | # fastlane
47 | #
48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
49 | # screenshots whenever they are needed.
50 | # For more information about the recommended setup visit:
51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
52 |
53 | fastlane/report.xml
54 | fastlane/Preview.html
55 | fastlane/screenshots
56 | fastlane/test_output
57 |
58 | # Code Injection
59 | #
60 | # After new code Injection tools there's a generated folder /iOSInjectionProject
61 | # https://github.com/johnno1962/injectionforxcode
62 |
63 | iOSInjectionProject/
64 |
--------------------------------------------------------------------------------
/1.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ryan-Wong-iOS/RWFileTransfer/18237a62dd01ee9de1f7730af6cd17ad7927ded7/1.JPG
--------------------------------------------------------------------------------
/2.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ryan-Wong-iOS/RWFileTransfer/18237a62dd01ee9de1f7730af6cd17ad7927ded7/2.JPG
--------------------------------------------------------------------------------
/3.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ryan-Wong-iOS/RWFileTransfer/18237a62dd01ee9de1f7730af6cd17ad7927ded7/3.JPG
--------------------------------------------------------------------------------
/4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ryan-Wong-iOS/RWFileTransfer/18237a62dd01ee9de1f7730af6cd17ad7927ded7/4.gif
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Ryan Wong
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RWFileTransferDemo
2 |
3 | 本Demo主要是把公司项目中用到的文件传输技术整理出来,其中使用到iOS 7.0 后推出的Mutipeer Connectivity框架,借助WIFI、蓝牙来建立传输通道,达到用户互相传输媒体的功能
4 |
5 | 
6 | 
7 | 
8 | 
9 |
10 |
--------------------------------------------------------------------------------
/RWFileTransferDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/RWFileTransferDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/1/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/1/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 |
11 | @interface AppDelegate ()
12 |
13 | @end
14 |
15 | @implementation AppDelegate
16 |
17 |
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
19 | // Override point for customization after application launch.
20 | return YES;
21 | }
22 |
23 |
24 | - (void)applicationWillResignActive:(UIApplication *)application {
25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
27 | }
28 |
29 |
30 | - (void)applicationDidEnterBackground:(UIApplication *)application {
31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
33 | }
34 |
35 |
36 | - (void)applicationWillEnterForeground:(UIApplication *)application {
37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
38 | }
39 |
40 |
41 | - (void)applicationDidBecomeActive:(UIApplication *)application {
42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
43 | }
44 |
45 |
46 | - (void)applicationWillTerminate:(UIApplication *)application {
47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
48 | }
49 |
50 |
51 | @end
52 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/RWFileTransferDemo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/RWFileTransferDemo/Assets.xcassets/rw_select.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "ST_Common_select_2.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/RWFileTransferDemo/Assets.xcassets/rw_select.imageset/ST_Common_select_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ryan-Wong-iOS/RWFileTransfer/18237a62dd01ee9de1f7730af6cd17ad7927ded7/RWFileTransferDemo/Assets.xcassets/rw_select.imageset/ST_Common_select_2.png
--------------------------------------------------------------------------------
/RWFileTransferDemo/Assets.xcassets/rw_unselect.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "ST_Common_select_1.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/RWFileTransferDemo/Assets.xcassets/rw_unselect.imageset/ST_Common_select_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ryan-Wong-iOS/RWFileTransfer/18237a62dd01ee9de1f7730af6cd17ad7927ded7/RWFileTransferDemo/Assets.xcassets/rw_unselect.imageset/ST_Common_select_1.png
--------------------------------------------------------------------------------
/RWFileTransferDemo/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
31 |
38 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Base/Controller/RWBaseViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWBaseViewController.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/2/28.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface RWBaseViewController : UIViewController
12 |
13 | @property (strong, nonatomic, readonly)id baseViewModel;
14 |
15 | - (instancetype)initWithViewModel:(id)baseViewModel;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Base/Controller/RWBaseViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWBaseViewController.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/2/28.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWBaseViewController.h"
10 |
11 | @interface RWBaseViewController ()
12 |
13 | @property (strong, nonatomic, readwrite)id baseViewModel;
14 |
15 | @end
16 |
17 | @implementation RWBaseViewController
18 |
19 | - (instancetype)initWithViewModel:(id)baseViewModel{
20 | self = [super init];
21 | if (self) {
22 | self.baseViewModel = baseViewModel;
23 | self.view.backgroundColor = [UIColor whiteColor];
24 | }
25 | return self;
26 | }
27 |
28 | - (void)viewDidLoad {
29 | [super viewDidLoad];
30 | // Do any additional setup after loading the view.
31 | }
32 |
33 | - (void)didReceiveMemoryWarning {
34 | [super didReceiveMemoryWarning];
35 | // Dispose of any resources that can be recreated.
36 | }
37 |
38 | /*
39 | #pragma mark - Navigation
40 |
41 | // In a storyboard-based application, you will often want to do a little preparation before navigation
42 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
43 | // Get the new view controller using [segue destinationViewController].
44 | // Pass the selected object to the new view controller.
45 | }
46 | */
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Base/ViewModel/RWBaseViewModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWBaseViewModel.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/2/28.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface RWBaseViewModel : NSObject
12 |
13 | @property (copy, nonatomic)NSString *title;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Base/ViewModel/RWBaseViewModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWBaseViewModel.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/2/28.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWBaseViewModel.h"
10 |
11 | @implementation RWBaseViewModel
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Category/UIImage+Resize.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+Resize.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/4/5.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface UIImage(Resize)
12 |
13 | + (UIImage*)imageWithImageSimple:(UIImage*)image scaledToSize:(CGSize)newSize;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Category/UIImage+Resize.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+Resize.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/4/5.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "UIImage+Resize.h"
10 |
11 | @implementation UIImage(Resize)
12 |
13 | + (UIImage*)imageWithImageSimple:(UIImage*)image scaledToSize:(CGSize)newSize
14 | {
15 | CGSize oldSize = image.size;
16 | CGFloat radio = oldSize.width / oldSize.height;
17 | CGSize finalSize;
18 | if (radio < 1.0) {
19 | finalSize = CGSizeMake(newSize.width, newSize.width / radio);
20 | } else {
21 | finalSize = CGSizeMake(newSize.height * radio, newSize.height);
22 | }
23 |
24 | UIGraphicsBeginImageContext(finalSize);
25 | [image drawInRect:CGRectMake(0,0,finalSize.width,finalSize.height)];
26 | UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
27 | UIGraphicsEndImageContext();
28 | return newImage;
29 | }
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Category/UIImage+Video.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+Video.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/4/5.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface UIImage (Video)
13 |
14 | + (UIImage *)getThumbnailImageWithFilePath:(NSString *)filePath;
15 |
16 | + (UIImage *)getThumbnailImageWithURLAsset:(AVURLAsset *)asset;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Category/UIImage+Video.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+Video.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/4/5.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "UIImage+Video.h"
10 |
11 | @implementation UIImage (Video)
12 |
13 | + (UIImage *)getThumbnailImageWithFilePath:(NSString *)filePath {
14 | return [self getThumbnailImage:filePath];
15 | }
16 |
17 | + (UIImage *)getThumbnailImageWithURLAsset:(AVURLAsset *)asset {
18 | return [self getThumbnailImage:asset];
19 | }
20 |
21 | + (UIImage *)getThumbnailImage:(id)object
22 | {
23 | AVURLAsset *asset;
24 | if ([object isKindOfClass:[NSString class]]) {
25 | asset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:object] options:nil];
26 | } else if ([object isKindOfClass:[AVURLAsset class]]) {
27 | asset = (AVURLAsset *)object;
28 | }
29 | AVAssetImageGenerator *gen = [[AVAssetImageGenerator alloc] initWithAsset:asset];
30 | gen.appliesPreferredTrackTransform = YES;
31 | CMTime time = CMTimeMakeWithSeconds(0.0, 600);
32 | NSError *error = nil;
33 | CMTime actualTime;
34 | CGImageRef image = [gen copyCGImageAtTime:time actualTime:&actualTime error:&error];
35 | UIImage *thumb = [[UIImage alloc] initWithCGImage:image];
36 | CGImageRelease(image);
37 | return thumb;
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Common/RWCommon.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWCommon.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/31.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface RWCommon : NSObject
13 |
14 | +(NSString *)getFileSizeTextFromSize:(long long)fileSize;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Common/RWCommon.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWCommon.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/31.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWCommon.h"
10 |
11 | @implementation RWCommon
12 |
13 | +(NSString *)getFileSizeTextFromSize:(long long)fileSize {
14 | int sizeDig = 1024*1.0;
15 |
16 | const int GB = sizeDig * sizeDig * sizeDig;//定义GB的计算常量
17 | const int MB = sizeDig * sizeDig;//定义MB的计算常量
18 | const int KB = sizeDig;//定义KB的计算常量
19 |
20 | NSString *fileSizeText = @"";
21 | if (fileSize / GB >= 1) {
22 | fileSizeText = [NSString stringWithFormat:@"%.2fGB", (CGFloat)fileSize / (CGFloat)GB];
23 | }else if (fileSize / MB >= 1){
24 | fileSizeText = [NSString stringWithFormat:@"%.2fMB", (CGFloat)fileSize / (CGFloat)MB];
25 | } else if (fileSize / KB >= 1){
26 | fileSizeText = [NSString stringWithFormat:@"%.0fKB", (CGFloat)fileSize / (CGFloat)KB];
27 | }else{
28 | fileSizeText = [NSString stringWithFormat:@"%.0fB", (CGFloat)fileSize];
29 | }
30 |
31 | return fileSizeText;
32 | }
33 |
34 | @end
35 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Config/RWConfig.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWConfig.h
3 | // XKFM
4 | //
5 | // Created by RyanWong on 2017/10/14.
6 | // Copyright © 2017年 com.nest. All rights reserved.
7 | //
8 |
9 | #ifndef RWConfig_h
10 | #define RWConfig_h
11 |
12 | #define RWRGBA(r,g,b,a) [UIColor colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:a]
13 |
14 | #define RWHEXCOLOR(s) [UIColor colorWithRed:(((s & 0xFF0000) >> 16))/255.0 green:(((s &0xFF00) >>8))/255.0 blue:((s &0xFF))/255.0 alpha:1.0]
15 |
16 | #define RWHEXCOLORA(s,a) [UIColor colorWithRed:(((s & 0xFF0000) >> 16))/255.0 green:(((s &0xFF00) >>8))/255.0 blue:((s &0xFF))/255.0 alpha:a]
17 |
18 | #define kWidth [UIScreen mainScreen].bounds.size.width
19 | #define kHeight [UIScreen mainScreen].bounds.size.height
20 | #define kScale [UIScreen mainScreen].scale
21 |
22 | #define UI_IS_IPAD ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
23 | #define UI_IS_IPHONE ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
24 | #define UI_IS_IPHONE4 (UI_IS_IPHONE && [[UIScreen mainScreen] bounds].size.height < 568.0)
25 | #define UI_IS_IPHONE5 (UI_IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 568.0)
26 | #define UI_IS_IPHONE6 (UI_IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 667.0)
27 | #define UI_IS_IPHONE6PLUS (UI_IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 736.0 || [[UIScreen mainScreen] bounds].size.width == 736.0) // Both orientations
28 | #define UI_IS_IPHONEX (UI_IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 812.0)
29 |
30 | /*******************************************************************
31 | * -- 本地化
32 | *******************************************************************/
33 | #define Language(key) (NSString *)[[NSBundle mainBundle] localizedStringForKey:(key) value:key table:nil]
34 | #define LanguageWRV(key, value1) (NSString *)[Language(key) stringByReplacingOccurrencesOfString:@"[P0]" withString:value1]
35 | #define LanguageWRVS(key, value1, value2) (NSString *)[[Language(key) stringByReplacingOccurrencesOfString:@"[P0]" withString:value1] stringByReplacingOccurrencesOfString:@"[P1]" withString:value2]
36 |
37 | /*******************************************************************
38 | * -- 日志打印
39 | *******************************************************************/
40 | #ifdef DEBUG
41 | # define RWLog(fmt, ...) NSLog((@"【日志】-- 方法:%s 行号:%d 日志内容:" fmt), __FUNCTION__, __LINE__, ##__VA_ARGS__);
42 | # define RWStatus(fmt, ...) NSLog((@"【状态】-- 日志内容:" fmt), ##__VA_ARGS__);
43 | # define RWError(fmt, ...) NSLog((@"【错误】-- 方法:%s 行号:%d 日志内容:" fmt), __FUNCTION__, __LINE__, ##__VA_ARGS__);
44 | #else
45 | # define RWLog(...);
46 | # define RWStatus(...);
47 | # define RWError(...);
48 | #endif
49 |
50 | #endif /* RWConfig_h */
51 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Config/RWParamsConfig.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWParamsConfig.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/4/5.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #ifndef RWParamsConfig_h
10 | #define RWParamsConfig_h
11 |
12 | #define CoverSize CGSizeMake(150, 150)
13 |
14 | #endif /* RWParamsConfig_h */
15 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Config/RWStringConfig.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWStringConfig.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #ifndef RWStringConfig_h
10 | #define RWStringConfig_h
11 |
12 | #import
13 |
14 | static NSString *const kFileTypePicture = @"picutre";
15 |
16 | static NSString *const kFileTypeVideo = @"video";
17 |
18 | typedef NS_ENUM(NSUInteger, RWTransferDataType) {
19 | RWTransferDataTypeSendTaskInfo, //发送方:发送任务基本信息
20 | RWTransferDataTypeReceiveTaskInfo, //接收方:反馈任务信息已接收
21 | RWTransferDataTypeReceiveProgress, //接收方:反馈给发送方实际接收进度
22 | RWTransferDataTypeCancel, //发送方、接收方:取消任务
23 | RWTransferDataTypeFinish, //接收方:完成接收
24 | RWTransferDataTypeError //发送方、接收方:发生错误
25 | };
26 |
27 | #endif /* RWStringConfig_h */
28 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | NSPhotoLibraryUsageDescription
24 | 选择照片时需要相册权限,请允许
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationPortraitUpsideDown
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Model/RWAlbumModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWAlbum.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/2/26.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface RWAlbumModel : NSObject
13 |
14 | @property (copy, nonatomic)NSString *title;
15 |
16 | @property (strong, nonatomic)PHFetchResult *result;
17 |
18 | @property (assign, nonatomic)NSInteger count;
19 |
20 | @property (assign, nonatomic)NSString *fileType;
21 |
22 | @end
23 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Model/RWAlbumModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWAlbum.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/2/26.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWAlbumModel.h"
10 |
11 | @implementation RWAlbumModel
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/RWFileTransfer/RWBrower/RWBrowser.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWBrower.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/1/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | typedef void(^RWBrowserGetNearbyPeerBlock)(NSArray *peerArray);
13 |
14 | @interface RWBrowser : NSObject
15 |
16 | @property (copy, nonatomic)RWBrowserGetNearbyPeerBlock nearbyPeerBlock;
17 |
18 | + (instancetype)shareInstance;
19 |
20 | - (void)setConfigurationWithName:(NSString *)name Identifier:(NSString *)identifier;
21 |
22 | - (void)startSearchNearbyService;
23 |
24 | - (void)startWaitForConnect;
25 |
26 | - (void)stopSearch;
27 |
28 | - (void)stopWait;
29 |
30 | - (void)invitePeer:(MCPeerID *)peerId;
31 |
32 | @end
33 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/RWFileTransfer/RWBrower/RWBrowser.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWBrower.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/1/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWBrowser.h"
10 | #import
11 | #import "RWSession.h"
12 | #import "RWUserCenter.h"
13 |
14 | @interface RWBrowser()
15 |
16 | @property (strong, nonatomic)RWUserCenter *userCenter;
17 |
18 | @property (strong, nonatomic)MCNearbyServiceAdvertiser *advertiser;
19 |
20 | @property (strong, nonatomic)MCNearbyServiceBrowser *browser;
21 |
22 | @property (strong, nonatomic)NSMutableArray *peerArray;
23 |
24 | @end
25 |
26 | static RWBrowser *_instance = nil;
27 | @implementation RWBrowser
28 |
29 | + (instancetype)shareInstance {
30 | static dispatch_once_t onceToken;
31 | dispatch_once(&onceToken, ^{
32 | _instance = [[RWBrowser alloc] init];
33 | });
34 | return _instance;
35 | }
36 |
37 | - (void)setConfigurationWithName:(NSString *)name Identifier:(NSString *)identifier {
38 | _userCenter = [RWUserCenter center];
39 | _userCenter.name = name;
40 | _userCenter.identifier = identifier;
41 | _userCenter.myPeerID = [[MCPeerID alloc] initWithDisplayName:name];
42 | _userCenter.session = [[RWSession alloc] initWithPeer:_userCenter.myPeerID];
43 | }
44 |
45 | - (void)startSearchNearbyService {
46 | [self.peerArray removeAllObjects];
47 |
48 | _browser = [[MCNearbyServiceBrowser alloc] initWithPeer:_userCenter.myPeerID serviceType:_userCenter.identifier];
49 | _browser.delegate = self;
50 | [_browser startBrowsingForPeers];
51 | }
52 |
53 | - (void)startWaitForConnect {
54 | _advertiser = [[MCNearbyServiceAdvertiser alloc] initWithPeer:_userCenter.myPeerID discoveryInfo:nil serviceType:_userCenter.identifier];
55 | _advertiser.delegate = self;
56 | [_advertiser startAdvertisingPeer];
57 | }
58 |
59 | - (void)stopSearch {
60 | [_browser stopBrowsingForPeers];
61 | }
62 |
63 | - (void)stopWait {
64 | [_advertiser stopAdvertisingPeer];
65 | }
66 |
67 | - (void)invitePeer:(MCPeerID *)peerId {
68 | if ([_userCenter.session.session.connectedPeers containsObject:peerId]) {
69 | NSLog(@"与%@已经连接了,无须再次连接",peerId.displayName);
70 | return;
71 | }
72 | [_browser invitePeer:peerId toSession:_userCenter.session.session withContext:nil timeout:15];
73 | }
74 |
75 | #pragma mark - Browser Delegate
76 | -(void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info {
77 | NSLog(@"发现附近用户%@",peerID.displayName);
78 |
79 | [self checkPeerArray:peerID];
80 |
81 | [self.peerArray addObject:peerID];
82 |
83 | if (_nearbyPeerBlock) {
84 | _nearbyPeerBlock((NSArray *)_peerArray);
85 | }
86 | }
87 |
88 | -(void)browser:(MCNearbyServiceBrowser *)browser lostPeer:(MCPeerID *)peerID {
89 | NSLog(@"丢失用户%@",peerID.displayName);
90 |
91 | [self checkPeerArray:peerID];
92 |
93 | if (_nearbyPeerBlock) {
94 | _nearbyPeerBlock((NSArray *)_peerArray);
95 | }
96 | }
97 |
98 | - (void)checkPeerArray:(MCPeerID *)peerID {
99 | NSMutableArray *existArray = [NSMutableArray arrayWithCapacity:_peerArray.count];
100 | for (MCPeerID *tempPeerId in _peerArray) {
101 | if ([tempPeerId.displayName isEqualToString:peerID.displayName]) {
102 | [existArray addObject:tempPeerId];
103 | }
104 | }
105 | if (existArray.count > 0) {
106 | [_peerArray removeObjectsInArray:existArray];
107 | }
108 | }
109 |
110 | #pragma mark - MCNearbyServiceAdvertiserDelegate
111 | - (void)advertiser:(MCNearbyServiceAdvertiser *)advertiser didReceiveInvitationFromPeer:(MCPeerID *)peerID withContext:(nullable NSData *)context invitationHandler:(void (^)(BOOL accept, MCSession * __nullable session))invitationHandler {
112 | invitationHandler(YES, _userCenter.session.session);
113 | }
114 |
115 | #pragma mark - Lazy load
116 | - (NSMutableArray *)peerArray {
117 | if (!_peerArray) {
118 | _peerArray = [NSMutableArray array];
119 | }
120 | return _peerArray;
121 | }
122 | @end
123 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/RWFileTransfer/RWSessionManager/RWSession.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWSession.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/1/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | static NSString *const kRWSessionStateConnectedNotification = @"RWSessionStateConnectedNotification";
13 | static NSString *const kRWSessionStateNotConnectedNotification = @"RWSessionStateNotConnectedNotification";
14 |
15 | @class RWSession;
16 | @protocol RWSessionDelegate
17 |
18 | - (void)session:(RWSession *)session didReceiveStream:(NSInputStream *)stream WithName:(NSString *)streamName;
19 | - (void)session:(RWSession *)session didReceiveData:(NSData *)data;
20 |
21 | @end
22 |
23 | @interface RWSession : NSObject
24 |
25 | @property (strong, nonatomic)MCSession *session;
26 |
27 | @property (assign, nonatomic)id delegate;
28 |
29 | - (instancetype)initWithPeer:(MCPeerID *)peerId;
30 |
31 | + (void)kickPeer:(MCPeerID *)peerId;
32 |
33 | - (NSArray *)connectedPeers;
34 |
35 | - (NSOutputStream *)outputStreamForPeer:(MCPeerID *)peer With:(NSString *)name;
36 |
37 | - (BOOL)sendData:(NSData *)data toPeers:(NSArray *)peers;
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/RWFileTransfer/RWSessionManager/RWSession.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWSessionManager.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/1/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWSession.h"
10 | #import "RWUserCenter.h"
11 |
12 | @interface RWSession()
13 |
14 | @end
15 |
16 | @implementation RWSession
17 |
18 | - (instancetype)initWithPeer:(MCPeerID *)peerId {
19 | self = [super init];
20 | if (self) {
21 | _session = [[MCSession alloc] initWithPeer:peerId securityIdentity:nil encryptionPreference:MCEncryptionNone];
22 | _session.delegate = self;
23 | }
24 | return self;
25 | }
26 |
27 | + (void)kickPeer:(MCPeerID *)peerId {
28 | [[RWUserCenter center].session.session cancelConnectPeer:peerId];
29 | }
30 |
31 | - (NSArray *)connectedPeers {
32 | return [self.session connectedPeers];
33 | }
34 |
35 | - (NSOutputStream *)outputStreamForPeer:(MCPeerID *)peer With:(NSString *)name {
36 | NSError *error;
37 | NSOutputStream *outputStream = [self.session startStreamWithName:name toPeer:peer error:&error];
38 | if (error) {
39 | RWError(@"%@", [error userInfo].description);
40 | }
41 | return outputStream;
42 | }
43 |
44 | - (BOOL)sendData:(NSData *)data toPeers:(NSArray *)peers {
45 | NSError *error;
46 | BOOL result = [self.session sendData:data toPeers:peers withMode:MCSessionSendDataReliable error:&error];
47 | if (error) {
48 | RWError(@"%@", [error userInfo].description);
49 | }
50 | return result;
51 | }
52 |
53 | #pragma mark - MCSession Delegate
54 | -(void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state {
55 | if (state == MCSessionStateConnected) {
56 | RWStatus(@"与%@连接上",peerID.displayName);
57 | self.session = session;
58 |
59 | [[NSNotificationCenter defaultCenter] postNotificationName:kRWSessionStateConnectedNotification object:nil];
60 | } else if (state == MCSessionStateConnecting) {
61 | RWStatus(@"与%@连接中",peerID.displayName);
62 | } else if (state == MCSessionStateNotConnected) {
63 | RWStatus(@"与%@连接断开",peerID.displayName);
64 | self.session = nil;
65 |
66 | [[NSNotificationCenter defaultCenter] postNotificationName:kRWSessionStateNotConnectedNotification object:nil];
67 | }
68 | }
69 |
70 | -(void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID {
71 | if (_delegate && [_delegate respondsToSelector:@selector(session:didReceiveData:)]) {
72 | [_delegate session:self didReceiveData:data];
73 | }
74 | }
75 |
76 | -(void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID {
77 | if (_delegate && [_delegate respondsToSelector:@selector(session:didReceiveStream:WithName:)]) {
78 | [_delegate session:self didReceiveStream:stream WithName:streamName];
79 | }
80 | }
81 |
82 | // Start receiving a resource from remote peer.
83 | - (void) session:(MCSession *)session
84 | didStartReceivingResourceWithName:(NSString *)resourceName
85 | fromPeer:(MCPeerID *)peerID
86 | withProgress:(NSProgress *)progress{}
87 |
88 | // Finished receiving a resource from remote peer and saved the content
89 | // in a temporary location - the app is responsible for moving the file
90 | // to a permanent location within its sandbox.
91 | - (void) session:(MCSession *)session
92 | didFinishReceivingResourceWithName:(NSString *)resourceName
93 | fromPeer:(MCPeerID *)peerID
94 | atURL:(nullable NSURL *)localURL
95 | withError:(nullable NSError *)error{}
96 |
97 | @end
98 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/RWFileTransfer/RWStream/RWInputStream.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWInputStream.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/3.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class RWInputStream;
12 | @protocol RWInputStreamDelegate
13 |
14 | - (void)inputStream:(RWInputStream *)inputStream streamName:(NSString *)streamName progress:(long long)progress;
15 |
16 | - (void)inputStream:(RWInputStream *)inputStream transferEndWithStreamName:(NSString *)streamName filePath:(NSString *)filePath;
17 |
18 | - (void)inputStream:(RWInputStream *)inputStream transferErrorWithStreamName:(NSString *)streamName;
19 |
20 | @end
21 |
22 | @interface RWInputStream : NSObject
23 |
24 | @property (copy, nonatomic)NSString *streamName;
25 |
26 | @property (assign, nonatomic)id delegate;
27 |
28 | - (instancetype)initWithInputStream:(NSInputStream *)inputStream;
29 |
30 | - (void)start;
31 |
32 | - (void)stop;
33 |
34 | @end
35 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/RWFileTransfer/RWStream/RWInputStream.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWInputStream.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/3.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWInputStream.h"
10 | #import "RWStream.h"
11 | #import "RWFileManager.h"
12 |
13 | UInt32 const kRWStreamReadMaxLength = 4096;
14 |
15 | @interface RWInputStream()
16 |
17 | @property (strong, nonatomic)RWStream *stream;
18 |
19 | @property (strong, nonatomic)NSThread *streamThread;
20 |
21 | @property (strong, nonatomic)NSMutableData *appendData;
22 |
23 | @property (strong, nonatomic)NSFileHandle *fileHandle;
24 |
25 | @property (assign, nonatomic)long long progress;
26 |
27 | @end
28 |
29 | @implementation RWInputStream
30 |
31 | - (instancetype)initWithInputStream:(NSInputStream *)inputStream {
32 | self = [super init];
33 | if (self) {
34 | self.stream = [[RWStream alloc] initWithInputStream:inputStream];
35 | self.stream.delegate = self;
36 | }
37 | return self;
38 | }
39 |
40 | - (void)start {
41 | if (![[NSThread currentThread] isMainThread]) {
42 | return [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:YES];
43 | }
44 |
45 | _progress = 0;
46 |
47 | self.streamThread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
48 | [self.streamThread start];
49 | }
50 |
51 | - (void)run {
52 | @autoreleasepool {
53 | [self.stream open];
54 |
55 | while ([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
56 | }
57 | }
58 |
59 | - (void)stop
60 | {
61 | [self performSelector:@selector(stopThread) onThread:self.streamThread withObject:nil waitUntilDone:YES];
62 | }
63 |
64 | - (void)stopThread
65 | {
66 | [self.stream close];
67 | self.appendData = nil;
68 | [self.fileHandle synchronizeFile];
69 | [self.fileHandle closeFile];
70 | self.fileHandle = nil;
71 | [self.streamThread cancel];
72 | self.stream = nil;
73 | self.delegate = nil;
74 | RWStatus(@"Stop");
75 | }
76 |
77 | #pragma mark - RWStreamDelegate
78 | - (void)rwStream:(RWStream *)stream handleEvent:(RWStreamEvent)event {
79 | switch (event) {
80 | case RWStreamEventHasData: {
81 | @autoreleasepool {
82 | uint8_t bytes[kRWStreamReadMaxLength];
83 | UInt32 length = [stream readData:bytes maxLength:kRWStreamReadMaxLength];
84 | NSData *data = [NSData dataWithBytes:(const void *)bytes length:length];
85 | [self.fileHandle seekToEndOfFile];
86 | [self.fileHandle writeData:data];
87 | _progress += length;
88 |
89 | if (_delegate && [_delegate respondsToSelector:@selector(inputStream:streamName:progress:)]) {
90 | [_delegate inputStream:self streamName:_streamName progress:_progress];
91 | }
92 | RWLog(@"Transfering %d / %lld", (unsigned int)length, _progress);
93 | }
94 | break;
95 | }
96 |
97 | case RWStreamEventEnd:
98 | RWStatus(@"Transfer End");
99 | if (_delegate && [_delegate respondsToSelector:@selector(inputStream:transferEndWithStreamName:filePath:)]) {
100 | [_delegate inputStream:self transferEndWithStreamName:_streamName filePath:[self getTmpFilePath]];
101 | }
102 | break;
103 |
104 | case RWStreamEventError:
105 | RWStatus(@"Transfer Error");
106 | if (_delegate && [_delegate respondsToSelector:@selector(inputStream:transferErrorWithStreamName:)]) {
107 | [_delegate inputStream:self transferErrorWithStreamName:_streamName];
108 | }
109 | break;
110 |
111 | default:
112 | break;
113 | }
114 | }
115 |
116 | #pragma mark - Lazy load
117 |
118 | -(NSMutableData *)appendData {
119 | if (!_appendData) {
120 | _appendData = [NSMutableData data];
121 | }
122 | return _appendData;
123 | }
124 |
125 | -(NSFileHandle *)fileHandle {
126 | if (!_fileHandle) {
127 | NSString *path = [self getTmpFilePath];
128 | [RWFileManager createBlankFileAtPath:path];
129 | _fileHandle = [NSFileHandle fileHandleForWritingAtPath:path];
130 | }
131 | return _fileHandle;
132 | }
133 |
134 | - (NSString *)getTmpFilePath {
135 | NSString *tmp = [RWFileManager tmpPath];
136 | NSString *path = [NSString stringWithFormat:@"%@%@", tmp, _streamName];
137 | return path;
138 | }
139 |
140 | -(void)dealloc {
141 | RWStatus(@"RWInputStream 销毁");
142 | }
143 |
144 | @end
145 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/RWFileTransfer/RWStream/RWOutputStream.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWOutputStream.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/3.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class RWOutputStream;
12 | @protocol RWOutputStreamDelegate
13 |
14 | - (void)outputStream:(RWOutputStream *)outputStream progress:(long long)progress;
15 |
16 | - (void)outputStream:(RWOutputStream *)outputStream transferEndWithStreamName:(NSString *)name;
17 |
18 | - (void)outputStream:(RWOutputStream *)outputStream transferErrorWithStreamName:(NSString *)name;
19 |
20 | @end
21 |
22 | @interface RWOutputStream : NSObject
23 |
24 | @property (copy, nonatomic)NSString *streamName;
25 |
26 | @property (assign, nonatomic)id delegate;
27 |
28 | - (instancetype)initWithOutputStream:(NSOutputStream *)outputStream;
29 |
30 | - (void)streamWithAsset:(id)asset fileType:(NSString *)fileType;
31 |
32 | - (void)start;
33 |
34 | - (void)stop;
35 |
36 | @end
37 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/RWFileTransfer/RWStream/RWOutputStream.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWOutputStream.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/3.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWOutputStream.h"
10 | #import "RWStream.h"
11 |
12 | #import "RWImageLoad.h"
13 |
14 | UInt32 const kRWStreamWriteMaxLength = 4096;
15 |
16 | @interface RWOutputStream()
17 |
18 | @property (strong, nonatomic)RWStream *stream;
19 |
20 | @property (strong, nonatomic)NSOutputStream *outputStream;
21 |
22 | @property (strong, nonatomic)NSThread *streamThread;
23 |
24 | @property (strong, nonatomic)NSData *imageData;
25 |
26 | @property (assign, nonatomic)long long sendSize;
27 |
28 | @property (assign, nonatomic)long long totalSize;
29 |
30 | @property (assign, nonatomic)BOOL readyForSend;
31 |
32 | @end
33 |
34 | @implementation RWOutputStream
35 |
36 | - (instancetype)initWithOutputStream:(NSOutputStream *)outputStream {
37 | self = [super init];
38 | if (self) {
39 | RWStatus(@"Init");
40 | self.stream = [[RWStream alloc] initWithOutputStream:outputStream];
41 | self.stream.delegate = self;
42 | self.readyForSend = NO;
43 | self.outputStream = outputStream;
44 | }
45 | return self;
46 | }
47 |
48 | - (void)start {
49 | if (![[NSThread currentThread] isMainThread]) {
50 | return [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:YES];
51 | }
52 |
53 | RWStatus(@"Start");
54 | self.streamThread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
55 | [self.streamThread start];
56 | }
57 |
58 | - (void)run {
59 | @autoreleasepool {
60 | [self.stream open];
61 |
62 | RWStatus(@"Loop");
63 |
64 | // while (self.readyForSend && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
65 | while (!_readyForSend) {
66 | NSLog(@"等待视频获取");
67 | }
68 | while ([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) {
69 | }
70 |
71 | RWStatus(@"Done");
72 | }
73 | }
74 |
75 | - (void)stop
76 | {
77 | [self performSelector:@selector(stopThread) onThread:self.streamThread withObject:nil waitUntilDone:YES];
78 | }
79 |
80 | - (void)stopThread
81 | {
82 | self.readyForSend = NO;
83 | [self.stream close];
84 | self.stream = nil;
85 | self.outputStream = nil;
86 | self.imageData = nil;
87 | [self.streamThread cancel];
88 | RWStatus(@"Stop");
89 | }
90 |
91 | - (void)streamWithAsset:(id)asset fileType:(NSString *)fileType {
92 | __weak typeof(self)weakSelf = self;
93 |
94 | if (fileType == kFileTypePicture) {
95 | [[RWImageLoad shareLoad] getPhotoDataWithAsset:asset completion:^(NSData *imageData, NSString *dataUTI, NSDictionary *info) {
96 | weakSelf.imageData = [NSData dataWithData:imageData];
97 | weakSelf.totalSize = weakSelf.imageData.length;
98 | weakSelf.sendSize = 0;
99 | weakSelf.readyForSend = YES;
100 |
101 | RWLog(@"成功获取图片数据");
102 | }];
103 | } else if (fileType == kFileTypeVideo) {
104 | [[RWImageLoad shareLoad] getVideoDataWithAsset:asset completion:^(NSData *data) {
105 | weakSelf.imageData = [NSData dataWithData:data];
106 | weakSelf.totalSize = weakSelf.imageData.length;
107 | weakSelf.sendSize = 0;
108 | weakSelf.readyForSend = YES;
109 |
110 | RWLog(@"成功获取视频数据");
111 | }];
112 | }
113 | }
114 |
115 | - (void)sendDataChunk {
116 | @autoreleasepool {
117 | NSMutableData *data = [NSMutableData dataWithData:_imageData];
118 | uint8_t *readBytes = (uint8_t *)[data mutableBytes];
119 | readBytes += _sendSize;
120 | NSUInteger data_len = [data length];
121 | unsigned long long len = (data_len - _sendSize >= kRWStreamWriteMaxLength) ? kRWStreamWriteMaxLength : (data_len - _sendSize);
122 | uint8_t buf[len];
123 | (void)memcpy(buf, readBytes, len);
124 | len = [self.stream writeData:(const uint8_t *)buf maxLength:(UInt32)len];
125 | _sendSize += len;
126 | RWLog(@"Sending : %u", (unsigned int)len);
127 | RWLog(@"Sending progress : %u / %u", (unsigned int)_sendSize, (unsigned int)_totalSize);
128 | }
129 | }
130 |
131 | #pragma mark - RWStreamDelegate
132 | - (void)rwStream:(RWStream *)stream handleEvent:(RWStreamEvent)event {
133 | switch (event) {
134 | case RWStreamEventHasSpace:{
135 | RWStatus(@"Sending");
136 | [self sendDataChunk];
137 | break;
138 | }
139 |
140 | case RWStreamEventEnd:{
141 | RWStatus(@"Send End");
142 | if (_delegate && [_delegate respondsToSelector:@selector(outputStream:transferEndWithStreamName:)]) {
143 | [_delegate outputStream:self transferEndWithStreamName:_streamName];
144 | }
145 | [self stop];
146 | break;
147 | }
148 |
149 | case RWStreamEventError:{
150 | RWStatus(@"Send Error");
151 | if (_delegate && [_delegate respondsToSelector:@selector(outputStream:transferErrorWithStreamName:)]) {
152 | [_delegate outputStream:self transferErrorWithStreamName:_streamName];
153 | }
154 | [self stop];
155 | break;
156 | }
157 |
158 | default:
159 | break;
160 | }
161 | }
162 |
163 | -(void)dealloc {
164 | RWStatus(@"RWOutputStream 销毁")
165 | }
166 |
167 | @end
168 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/RWFileTransfer/RWStream/RWStream.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWStream.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/3.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | typedef NS_ENUM(NSUInteger, RWStreamEvent) {
12 | RWStreamEventHasData,
13 | RWStreamEventHasSpace,
14 | RWStreamEventError,
15 | RWStreamEventEnd,
16 | };
17 |
18 | @class RWStream;
19 | @protocol RWStreamDelegate
20 |
21 | - (void)rwStream:(RWStream *)stream handleEvent:(RWStreamEvent)event;
22 |
23 | @end
24 |
25 | @interface RWStream : NSObject
26 |
27 | @property (strong, nonatomic)NSStream *stream;
28 |
29 | @property (assign, nonatomic) id delegate;
30 |
31 | - (instancetype)initWithInputStream:(NSInputStream *)inputStream;
32 | - (instancetype)initWithOutputStream:(NSOutputStream *)outputStream;
33 |
34 | - (void)open;
35 | - (void)close;
36 | - (UInt32)readData:(uint8_t *)data maxLength:(UInt32)maxLength;
37 | - (UInt32)writeData:(const uint8_t *)data maxLength:(UInt32)maxLength;
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/RWFileTransfer/RWStream/RWStream.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWStream.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/3.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWStream.h"
10 |
11 | @interface RWStream()
12 |
13 | @end
14 |
15 | @implementation RWStream
16 |
17 | - (instancetype) initWithInputStream:(NSInputStream *)inputStream {
18 | self = [super init];
19 | if (self) {
20 | self.stream = inputStream;
21 | }
22 | return self;
23 | }
24 |
25 | - (instancetype) initWithOutputStream:(NSOutputStream *)outputStream {
26 | self = [super init];
27 | if (self) {
28 | self.stream = outputStream;
29 | }
30 | return self;
31 | }
32 |
33 | - (void)open {
34 | self.stream.delegate = self;
35 | [self.stream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
36 | [self.stream open];
37 | }
38 |
39 | - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
40 | switch (eventCode) {
41 | case NSStreamEventHasBytesAvailable:
42 | if (_delegate && [_delegate respondsToSelector:@selector(rwStream:handleEvent:)]) {
43 | [_delegate rwStream:self handleEvent:RWStreamEventHasData];
44 | }
45 | break;
46 |
47 | case NSStreamEventHasSpaceAvailable:
48 | if (_delegate && [_delegate respondsToSelector:@selector(rwStream:handleEvent:)]) {
49 | [_delegate rwStream:self handleEvent:RWStreamEventHasSpace];
50 | }
51 | break;
52 |
53 | case NSStreamEventErrorOccurred:
54 | if (_delegate && [_delegate respondsToSelector:@selector(rwStream:handleEvent:)]) {
55 | [_delegate rwStream:self handleEvent:RWStreamEventError];
56 | }
57 | break;
58 |
59 | case NSStreamEventEndEncountered:
60 | if (_delegate && [_delegate respondsToSelector:@selector(rwStream:handleEvent:)]) {
61 | [_delegate rwStream:self handleEvent:RWStreamEventEnd];
62 | }
63 | break;
64 |
65 | default:
66 | {
67 | // RWStatus(@"Send Other Status %lu", (unsigned long)eventCode);
68 | }
69 | break;
70 | }
71 | }
72 |
73 | - (UInt32)readData:(uint8_t *)data maxLength:(UInt32)maxLength {
74 | return (UInt32)[(NSInputStream *)self.stream read:data maxLength:maxLength];
75 | }
76 |
77 | - (UInt32)writeData:(const uint8_t *)data maxLength:(UInt32)maxLength {
78 | return (UInt32)[(NSOutputStream *)self.stream write:data maxLength:maxLength];
79 | }
80 |
81 | - (void)close {
82 | [self.stream close];
83 | self.stream.delegate = nil;
84 | [self.stream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
85 | }
86 |
87 | - (void)dealloc {
88 | RWStatus(@"Stream 销毁");
89 | // if (self.stream) {
90 | // [self close];
91 | // }
92 | // if (self.delegate) {
93 | // self.delegate = nil;
94 | // }
95 | }
96 |
97 | @end
98 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/RWFileTransfer/RWUserCenter/RWUserCenter.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWUserCenter.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/1/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "RWSession.h"
12 |
13 | @interface RWUserCenter : NSObject
14 |
15 | @property (strong, nonatomic)NSString *name;
16 |
17 | @property (strong, nonatomic)NSString *identifier;
18 |
19 | @property (strong, nonatomic)MCPeerID *myPeerID;
20 |
21 | @property (strong, nonatomic)RWSession *session;
22 |
23 | + (instancetype)center;
24 |
25 | - (void)remove;
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/RWFileTransfer/RWUserCenter/RWUserCenter.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWUserCenter.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/1/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWUserCenter.h"
10 |
11 | static RWUserCenter *_instance = nil;
12 | @implementation RWUserCenter
13 |
14 | + (instancetype)center {
15 | static dispatch_once_t onceToken;
16 | dispatch_once(&onceToken, ^{
17 | _instance = [[RWUserCenter alloc] init];
18 | });
19 | return _instance;
20 | }
21 |
22 | - (void)remove {
23 | self.name = nil;
24 | self.identifier = nil;
25 | self.myPeerID = nil;
26 | self.session = nil;
27 | }
28 |
29 | @end
30 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/RWHeader.pch:
--------------------------------------------------------------------------------
1 | //
2 | // RWHeader.pch
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #ifndef RWHeader_pch
10 | #define RWHeader_pch
11 |
12 | // Include any system framework and library headers here that should be included in all compilation units.
13 | // You will also need to set the Prefix Header build setting of one or more of your targets to reference this file.
14 |
15 | #import "RWConfig.h"
16 | #import "RWStringConfig.h"
17 | #import "RWParamsConfig.h"
18 | #import "RWCommon.h"
19 |
20 | #endif /* RWHeader_pch */
21 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/Controller/RWAlbumListViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWAlbumListViewController.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/2/28.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "RWBaseViewController.h"
11 | #import "RWAlbumListViewModel.h"
12 |
13 | @interface RWAlbumListViewController : RWBaseViewController
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/Controller/RWAlbumListViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWAlbumListViewController.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/2/28.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWAlbumListViewController.h"
10 | #import "RWAlbumTableView.h"
11 |
12 | @interface RWAlbumListViewController ()
13 |
14 | @property (strong, nonatomic)RWAlbumListViewModel *viewModel;
15 |
16 | @property (strong, nonatomic)RWAlbumTableView *albumTv;
17 |
18 | @property (strong, nonatomic)UIButton *sendBtn;
19 |
20 | @end
21 |
22 | @implementation RWAlbumListViewController
23 |
24 | - (void)viewWillAppear:(BOOL)animated {
25 | [super viewWillAppear:animated];
26 | [_albumTv reloadData];
27 | }
28 |
29 | - (void)viewDidLoad {
30 | [super viewDidLoad];
31 |
32 | self.viewModel = (RWAlbumListViewModel *)self.baseViewModel;
33 |
34 | self.title = self.viewModel.title;
35 |
36 | [self.view addSubview:self.albumTv];
37 |
38 | [self.view addSubview:self.sendBtn];
39 |
40 | [self.viewModel loadAlbumDataContentType:RWAlbumListContentTypePhoto success:^(id responseObject) {
41 | [self.albumTv reloadWithData:self.viewModel.albums];
42 | } failure:^(NSError *error) {
43 |
44 | }];
45 | }
46 |
47 | - (void)sendAction {
48 | [self.viewModel submitAllTransferDatas];
49 | [self.navigationController popViewControllerAnimated:YES];
50 | }
51 |
52 | - (void)didReceiveMemoryWarning {
53 | [super didReceiveMemoryWarning];
54 | // Dispose of any resources that can be recreated.
55 | }
56 |
57 | -(RWAlbumTableView *)albumTv {
58 | if (!_albumTv) {
59 | _albumTv = [[RWAlbumTableView alloc] initWithViewController:self frame:self.view.frame];
60 | }
61 | return _albumTv;
62 | }
63 |
64 | -(UIButton *)sendBtn {
65 | if (!_sendBtn) {
66 | _sendBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - 50, self.view.frame.size.width, 50)];
67 | [_sendBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
68 | [_sendBtn setTitle:@"发送" forState:UIControlStateNormal];
69 | [_sendBtn addTarget:self action:@selector(sendAction) forControlEvents:UIControlEventTouchUpInside];
70 | }
71 | return _sendBtn;
72 | }
73 |
74 | @end
75 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/Controller/RWImageSelectorViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWImageSelectorViewController.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/2/26.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "RWBaseViewController.h"
11 |
12 | @interface RWImageSelectorViewController : RWBaseViewController
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/Controller/RWImageSelectorViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWImageSelectorViewController.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/2/26.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWImageSelectorViewController.h"
10 | #import "RWImageSelectorViewModel.h"
11 | #import "RWAlbumViewModel.h"
12 | #import "RWimageViewModel.h"
13 | #import "RWPhotoCollectView.h"
14 |
15 | #import "RWImageLoad.h"
16 |
17 | @interface RWImageSelectorViewController ()
18 |
19 | @property (strong, nonatomic)RWPhotoCollectView *photoCV;
20 |
21 | @property (strong, nonatomic)RWImageSelectorViewModel *viewModel;
22 |
23 | @end
24 |
25 | @implementation RWImageSelectorViewController
26 |
27 | - (void)viewDidLoad {
28 | [super viewDidLoad];
29 |
30 | self.viewModel = self.baseViewModel;
31 | self.title = self.viewModel.title;
32 |
33 | [self.view addSubview:self.photoCV];
34 |
35 | [_photoCV reloadWithData:_viewModel.albumViewModel];
36 | }
37 |
38 | - (void)didReceiveMemoryWarning {
39 | [super didReceiveMemoryWarning];
40 | // Dispose of any resources that can be recreated.
41 | }
42 |
43 | - (RWPhotoCollectView *)photoCV {
44 | if (!_photoCV) {
45 | _photoCV = [[RWPhotoCollectView alloc] initWithFrame:self.view.frame];
46 | }
47 | return _photoCV;
48 | }
49 |
50 | @end
51 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/Model/RWPhotoModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWPhotoModel.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/1.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class PHAsset;
12 | @interface RWPhotoModel : NSObject
13 |
14 | @property (copy, nonatomic)NSString *name;
15 |
16 | @property (copy, nonatomic)PHAsset *asset;
17 |
18 | @property (assign, nonatomic)long long size;
19 |
20 | @property (copy, nonatomic)NSString *fileType;
21 |
22 | @property (copy, nonatomic)NSString *pathExtension;
23 |
24 | - (instancetype)initWithDictionary:(NSDictionary *)dict;
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/Model/RWPhotoModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWPhotoModel.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/1.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWPhotoModel.h"
10 | #import
11 | @implementation RWPhotoModel
12 |
13 | - (instancetype)initWithDictionary:(NSDictionary *)dict {
14 | self = [super init];
15 | if (self) {
16 | _name = dict[@"name"];
17 | _size = [dict[@"size"] longLongValue];
18 | _fileType = dict[@"fileType"];
19 | _pathExtension = dict[@"pathExtension"];
20 | }
21 | return self;
22 | }
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/View/RWAlbumListViewCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWAlbumListViewCell.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/2/28.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class RWAlbumViewModel;
12 | @interface RWAlbumListViewCell : UITableViewCell
13 |
14 | - (void)bindViewModel:(RWAlbumViewModel *)viewModel;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/View/RWAlbumListViewCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWAlbumListViewCell.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/2/28.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWAlbumListViewCell.h"
10 | #import "RWAlbumViewModel.h"
11 |
12 | @interface RWAlbumListViewCell()
13 |
14 | @property (weak, nonatomic) IBOutlet UIButton *selectBtn;
15 | @property (weak, nonatomic) IBOutlet UILabel *titleLab;
16 | @property (weak, nonatomic) IBOutlet UILabel *countLab;
17 |
18 | @property (strong, nonatomic) RWAlbumViewModel *viewModel;
19 |
20 | @end
21 |
22 | @implementation RWAlbumListViewCell
23 |
24 | - (void)awakeFromNib {
25 | [super awakeFromNib];
26 | // Initialization code
27 | }
28 |
29 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated {
30 | [super setSelected:selected animated:animated];
31 |
32 | // Configure the view for the selected state
33 | }
34 |
35 | - (void)bindViewModel:(RWAlbumViewModel *)viewModel {
36 | _viewModel = viewModel;
37 |
38 | _titleLab.text = _viewModel.title;
39 | _countLab.text = [NSString stringWithFormat:@"%zd/%zd", _viewModel.selectedCount, _viewModel.count];
40 | _selectBtn.selected = _viewModel.selected;
41 | }
42 |
43 | - (IBAction)selectAction:(id)sender {
44 | UIButton *button = (UIButton *)sender;
45 | if (!button.selected) {
46 | [_viewModel selectedAll];
47 | } else {
48 | [_viewModel removeAll];
49 | }
50 | button.selected = !button.selected;
51 | _countLab.text = [NSString stringWithFormat:@"%zd/%zd", _viewModel.selectedCount, _viewModel.count];
52 | }
53 |
54 | @end
55 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/View/RWAlbumListViewCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
33 |
39 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/View/RWAlbumTableView.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWAlbumTableView.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/2/27.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface RWAlbumTableView : UIView
12 |
13 | - (instancetype) initWithViewController:(UIViewController *)vc frame:(CGRect)frame;
14 |
15 | - (void)reloadData;
16 |
17 | - (void)reloadWithData:(NSArray *)data;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/View/RWAlbumTableView.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWAlbumTableView.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/2/27.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWAlbumTableView.h"
10 | #import "RWAlbumModel.h"
11 | #import "RWAlbumViewModel.h"
12 | #import "RWAlbumListViewCell.h"
13 | #import "RWImageSelectorViewModel.h"
14 |
15 | #import "RWImageSelectorViewController.h"
16 |
17 | @interface RWAlbumTableView()
18 |
19 | @property (strong, nonatomic)UITableView *tv;
20 |
21 | @property (strong, nonatomic)NSMutableArray *albums;
22 |
23 | @property (strong, nonatomic)UIViewController *parentViewController;
24 |
25 | @end
26 |
27 | @implementation RWAlbumTableView
28 |
29 | - (instancetype) initWithViewController:(UIViewController *)vc frame:(CGRect)frame {
30 | self = [super initWithFrame:frame];
31 | if (self) {
32 | _parentViewController = vc;
33 | [self setupView];
34 | }
35 | return self;
36 | }
37 |
38 | - (void)setupView {
39 | [self addSubview:self.tv];
40 | }
41 |
42 | #pragma mark - TableView delegate datasource
43 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
44 | return self.albums.count;
45 | }
46 |
47 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
48 | RWAlbumListViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([RWAlbumListViewCell class]) forIndexPath:indexPath];
49 |
50 | [cell bindViewModel:_albums[indexPath.row]];
51 |
52 | return cell;
53 | }
54 |
55 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
56 | return 50.0;
57 | }
58 |
59 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
60 | [tableView deselectRowAtIndexPath:indexPath animated:YES];
61 |
62 | RWImageSelectorViewModel *imageSelectorViewModel = [[RWImageSelectorViewModel alloc] init];
63 | RWAlbumViewModel *albumViewModel = _albums[indexPath.row];
64 | imageSelectorViewModel.albumViewModel = albumViewModel;
65 | imageSelectorViewModel.title = albumViewModel.title;
66 | RWImageSelectorViewController *vc = [[RWImageSelectorViewController alloc] initWithViewModel:imageSelectorViewModel];
67 | [self.parentViewController.navigationController pushViewController:vc animated:YES];
68 | }
69 |
70 | #pragma mark - reload
71 | - (void)reloadData {
72 | [_tv reloadData];
73 | }
74 |
75 | - (void)reloadWithData:(NSArray *)data {
76 | [self.albums removeAllObjects];
77 | [self.albums setArray:data];
78 | [_tv reloadData];
79 | }
80 |
81 | #pragma mark - Lazy load
82 |
83 | - (UITableView *)tv {
84 | if (!_tv) {
85 | _tv = [[UITableView alloc] initWithFrame:self.frame style:UITableViewStylePlain];
86 | _tv.delegate = self;
87 | _tv.dataSource = self;
88 |
89 | [_tv registerNib:[UINib nibWithNibName:NSStringFromClass([RWAlbumListViewCell class]) bundle:nil] forCellReuseIdentifier:NSStringFromClass([RWAlbumListViewCell class])];
90 | }
91 | return _tv;
92 | }
93 |
94 | - (NSMutableArray *)albums {
95 | if (!_albums) {
96 | _albums = [NSMutableArray array];
97 | }
98 | return _albums;
99 | }
100 |
101 | @end
102 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/View/RWPhotoCollectView.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWPhotoCollectView.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/1.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 | @class RWAlbumViewModel;
11 | @interface RWPhotoCollectView : UIView
12 |
13 | - (void)reloadWithData:(RWAlbumViewModel *)viewModel;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/View/RWPhotoCollectView.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWPhotoCollectView.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/1.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWPhotoCollectView.h"
10 | #import "RWPhotoViewCell.h"
11 | #import "RWAlbumViewModel.h"
12 | #import "RWimageViewModel.h"
13 | #import
14 |
15 | static CGFloat const margin = 3;
16 | static NSInteger const cols = 3;
17 | #define cellWH ((kWidth - (cols - 1) * margin) / cols)
18 |
19 | @interface RWPhotoCollectView()
20 |
21 | @property (strong, nonatomic)UICollectionView *cv;
22 |
23 | @property (strong, nonatomic)RWAlbumViewModel *albumViewModel;
24 |
25 | @property (nonatomic, assign) CGRect previousPreheatRect;
26 |
27 | @property (nonatomic, strong) PHCachingImageManager *imageManager;
28 |
29 | @end
30 |
31 | @implementation RWPhotoCollectView
32 |
33 | - (instancetype)initWithFrame:(CGRect)frame {
34 | self = [super initWithFrame:frame];
35 | if (self) {
36 | [self setupView];
37 | }
38 | return self;
39 | }
40 |
41 | - (PHCachingImageManager *)imageManager
42 | {
43 | if (_imageManager == nil) {
44 | _imageManager = [PHCachingImageManager new];
45 | }
46 | return _imageManager;
47 | }
48 |
49 | - (void)setupView {
50 | UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
51 | layout.itemSize = CGSizeMake(cellWH, cellWH);
52 | layout.minimumInteritemSpacing = margin;
53 | layout.minimumLineSpacing = margin;
54 |
55 | _cv = [[UICollectionView alloc] initWithFrame:self.frame collectionViewLayout:layout];
56 | _cv.delegate = self;
57 | _cv.dataSource = self;
58 | _cv.backgroundColor = [UIColor whiteColor];
59 |
60 | [self addSubview:_cv];
61 |
62 | [_cv registerNib:[UINib nibWithNibName:NSStringFromClass([RWPhotoViewCell class]) bundle:nil] forCellWithReuseIdentifier:NSStringFromClass([RWPhotoViewCell class])];
63 | }
64 |
65 | - (nonnull __kindof UICollectionViewCell *)collectionView:(nonnull UICollectionView *)collectionView cellForItemAtIndexPath:(nonnull NSIndexPath *)indexPath {
66 | RWPhotoViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([RWPhotoViewCell class]) forIndexPath:indexPath];
67 | [cell bindViewModel:_albumViewModel.allAssets[indexPath.row]];
68 |
69 | __weak typeof(self) weakSelf = self;
70 | cell.selectAction = ^(BOOL selected) {
71 | if (selected) {
72 | [weakSelf.albumViewModel selectOne:indexPath.row];
73 | } else {
74 | [weakSelf.albumViewModel removeOne:indexPath.row];
75 | }
76 | };
77 | return cell;
78 | }
79 |
80 | - (NSInteger)collectionView:(nonnull UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
81 | return _albumViewModel.count;
82 | }
83 |
84 | - (void)reloadData {
85 | [_cv reloadData];
86 | }
87 |
88 | - (void)reloadWithData:(RWAlbumViewModel *)viewModel {
89 | _albumViewModel = viewModel;
90 | [_cv reloadData];
91 | }
92 |
93 | #pragma mark - UIScrollViewDelegate
94 |
95 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView
96 | {
97 | if (self.albumViewModel.allAssets.count>1) {
98 | [self updateCachedAssets];
99 | }
100 | }
101 |
102 | - (void)updateCachedAssets{
103 |
104 | // The preheat window is twice the height of the visible rect
105 | CGRect preheatRect = self.cv.bounds;
106 | preheatRect = CGRectInset(preheatRect, 0.0, -0.5 * CGRectGetHeight(preheatRect));
107 |
108 | // If scrolled by a "reasonable" amount...
109 | CGFloat delta = ABS(CGRectGetMidY(preheatRect) - CGRectGetMidY(self.previousPreheatRect));
110 |
111 | if (delta > CGRectGetHeight(self.cv.bounds) / 3.0) {
112 | // Compute the assets to start caching and to stop caching
113 | NSMutableArray *addedIndexPaths = [NSMutableArray array];
114 | NSMutableArray *removedIndexPaths = [NSMutableArray array];
115 |
116 | [self computeDifferenceBetweenRect:self.previousPreheatRect andRect:preheatRect addedHandler:^(CGRect addedRect) {
117 | NSArray *indexPaths = [self qb_indexPathsForElementsInRect:addedRect];
118 | [addedIndexPaths addObjectsFromArray:indexPaths];
119 | } removedHandler:^(CGRect removedRect) {
120 | NSArray *indexPaths = [self qb_indexPathsForElementsInRect:removedRect];
121 | [removedIndexPaths addObjectsFromArray:indexPaths];
122 | }];
123 |
124 | NSArray *assetsToStartCaching = [self assetsAtIndexPaths:addedIndexPaths];
125 | NSArray *assetsToStopCaching = [self assetsAtIndexPaths:removedIndexPaths];
126 |
127 | CGSize itemSize = CGSizeMake(cellWH, cellWH);
128 |
129 | [self.imageManager startCachingImagesForAssets:assetsToStartCaching
130 | targetSize:itemSize
131 | contentMode:PHImageContentModeAspectFill
132 | options:nil];
133 | [self.imageManager stopCachingImagesForAssets:assetsToStopCaching
134 | targetSize:itemSize
135 | contentMode:PHImageContentModeAspectFill
136 | options:nil];
137 |
138 | self.previousPreheatRect = preheatRect;
139 | }
140 |
141 |
142 | }
143 |
144 | - (NSArray *)qb_indexPathsForElementsInRect:(CGRect)rect
145 | {
146 | NSArray *allLayoutAttributes = [self.cv.collectionViewLayout layoutAttributesForElementsInRect:rect];
147 | if (allLayoutAttributes.count == 0) { return nil; }
148 |
149 | NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:allLayoutAttributes.count];
150 | for (UICollectionViewLayoutAttributes *layoutAttributes in allLayoutAttributes) {
151 | NSIndexPath *indexPath = layoutAttributes.indexPath;
152 | [indexPaths addObject:indexPath];
153 | }
154 | return indexPaths;
155 | }
156 |
157 | - (void)computeDifferenceBetweenRect:(CGRect)oldRect andRect:(CGRect)newRect addedHandler:(void (^)(CGRect addedRect))addedHandler removedHandler:(void (^)(CGRect removedRect))removedHandler
158 | {
159 | if (CGRectIntersectsRect(newRect, oldRect)) {
160 | CGFloat oldMaxY = CGRectGetMaxY(oldRect);
161 | CGFloat oldMinY = CGRectGetMinY(oldRect);
162 | CGFloat newMaxY = CGRectGetMaxY(newRect);
163 | CGFloat newMinY = CGRectGetMinY(newRect);
164 |
165 | if (newMaxY > oldMaxY) {
166 | CGRect rectToAdd = CGRectMake(newRect.origin.x, oldMaxY, newRect.size.width, (newMaxY - oldMaxY));
167 | addedHandler(rectToAdd);
168 | }
169 | if (oldMinY > newMinY) {
170 | CGRect rectToAdd = CGRectMake(newRect.origin.x, newMinY, newRect.size.width, (oldMinY - newMinY));
171 | addedHandler(rectToAdd);
172 | }
173 | if (newMaxY < oldMaxY) {
174 | CGRect rectToRemove = CGRectMake(newRect.origin.x, newMaxY, newRect.size.width, (oldMaxY - newMaxY));
175 | removedHandler(rectToRemove);
176 | }
177 | if (oldMinY < newMinY) {
178 | CGRect rectToRemove = CGRectMake(newRect.origin.x, oldMinY, newRect.size.width, (newMinY - oldMinY));
179 | removedHandler(rectToRemove);
180 | }
181 | } else {
182 | addedHandler(newRect);
183 | removedHandler(oldRect);
184 | }
185 | }
186 |
187 | - (NSArray *)assetsAtIndexPaths:(NSArray *)indexPaths
188 | {
189 | if (indexPaths.count == 0) { return nil; }
190 | NSMutableArray *assets = [NSMutableArray arrayWithCapacity:indexPaths.count];
191 | for (NSIndexPath *indexPath in indexPaths) {
192 | if (indexPath.item < self.albumViewModel.count && indexPath.item != 0) {
193 | RWimageViewModel *imageViewModel = self.albumViewModel.allAssets[self.albumViewModel.count-indexPath.item];
194 | PHAsset *asset = imageViewModel.asset;
195 | [assets addObject:asset];
196 | }
197 | }
198 | return assets;
199 | }
200 |
201 | @end
202 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/View/RWPhotoViewCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWPhotoViewCell.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/1.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | typedef void(^RWPhotoViewCellSelectAction)(BOOL selected);
12 |
13 | @class RWimageViewModel;
14 | @interface RWPhotoViewCell : UICollectionViewCell
15 |
16 | @property (copy, nonatomic)RWPhotoViewCellSelectAction selectAction;
17 |
18 | - (void)bindViewModel:(RWimageViewModel *)viewModel;
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/View/RWPhotoViewCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWPhotoViewCell.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/1.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWPhotoViewCell.h"
10 | #import "RWimageViewModel.h"
11 |
12 | @interface RWPhotoViewCell()
13 |
14 | @property (strong, nonatomic)CALayer *imageLayer;
15 |
16 | @property (weak, nonatomic) IBOutlet UIView *bgView;
17 |
18 | @property (weak, nonatomic) IBOutlet UIButton *selectBtn;
19 |
20 | @property (copy, nonatomic)RWimageViewModel *viewModel;
21 |
22 | //@property (strong, nonatomic)UIImageView *testView;
23 |
24 | @end
25 |
26 | @implementation RWPhotoViewCell
27 |
28 | - (void)awakeFromNib {
29 | [super awakeFromNib];
30 | // Initialization code
31 |
32 | _imageLayer = ({
33 | CALayer *layer = [CALayer layer];
34 | layer.backgroundColor = [UIColor grayColor].CGColor;
35 | layer.contentsGravity = kCAGravityResizeAspectFill;
36 | layer;
37 | });
38 | [self.bgView.layer addSublayer:_imageLayer];
39 |
40 | }
41 |
42 | - (void)bindViewModel:(RWimageViewModel *)viewModel {
43 | _viewModel = viewModel;
44 | _selectBtn.selected = _viewModel.selected;
45 | __weak typeof(self) weakSelf = self;
46 | [_viewModel loadImageDataWithPhotoWidth:150 success:^(id responseObject) {
47 | UIImage *image = (UIImage *)responseObject;
48 | [weakSelf setupImageViewByLayer:image];
49 | } failure:^(NSError *error) {
50 |
51 | }];
52 | }
53 |
54 | - (void)setupImageViewByLayer:(UIImage *)image {
55 | _imageLayer.frame = self.bounds;
56 | _imageLayer.contents = (id)image.CGImage;
57 | }
58 |
59 | - (IBAction)selectAction:(id)sender {
60 |
61 | UIButton *button =(UIButton *)sender;
62 | button.selected = !button.selected;
63 | _viewModel.selected = button.selected;
64 | !_selectAction?:_selectAction(_viewModel.selected);
65 | }
66 |
67 | @end
68 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/View/RWPhotoViewCell.xib:
--------------------------------------------------------------------------------
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 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/ViewModel/RWAlbumListViewModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWAlbumListViewModel.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/2/28.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "RWBaseViewModel.h"
11 |
12 | typedef NS_ENUM(NSUInteger, RWAlbumListContentType) {
13 | RWAlbumListContentTypePhoto,
14 | RWAlbumListContentTypeVideo,
15 | };
16 |
17 | @class RWAlbumViewModel;
18 | @interface RWAlbumListViewModel : RWBaseViewModel
19 |
20 | @property (strong, nonatomic, readonly)NSArray *albums;
21 |
22 | - (void)loadAlbumDataContentType:(RWAlbumListContentType)contentType success:(void (^)(id))success failure:(void (^)(NSError *))failure;
23 |
24 | - (void)submitAllTransferDatas;
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/ViewModel/RWAlbumListViewModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWAlbumListViewModel.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/2/28.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWAlbumListViewModel.h"
10 | #import "RWAlbumViewModel.h"
11 | #import "RWTransferCenter.h"
12 |
13 | #import "RWImageLoad.h"
14 |
15 | @interface RWAlbumListViewModel()
16 |
17 | @property (strong, nonatomic, readwrite)NSArray *albums;
18 |
19 | @end
20 |
21 | @implementation RWAlbumListViewModel
22 |
23 | - (void)loadAlbumDataContentType:(RWAlbumListContentType)contentType success:(void (^)(id))success failure:(void (^)(NSError *))failure {
24 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
25 | BOOL contentImage = NO;
26 | BOOL contentVideo = NO;
27 | switch (contentType) {
28 | case RWAlbumListContentTypePhoto:
29 | {
30 | contentImage = YES;
31 | contentVideo = NO;
32 | break;
33 | }
34 | case RWAlbumListContentTypeVideo:
35 | {
36 | contentImage = NO;
37 | contentVideo = YES;
38 | break;
39 | }
40 |
41 | default:
42 | break;
43 | }
44 |
45 | [[RWImageLoad shareLoad] getAlbumContentImage:contentImage contentVideo:contentVideo completion:^(NSMutableArray *albums) {
46 | NSMutableArray *array = [NSMutableArray array];
47 | for (RWAlbumModel *model in albums) {
48 | RWAlbumViewModel *viewModel = [[RWAlbumViewModel alloc] initWithModel:model];
49 | [array addObject:viewModel];
50 | }
51 | self.albums = (NSArray *)array;
52 | !success?:success(nil);
53 | }];
54 | });
55 | }
56 |
57 | - (void)submitAllTransferDatas {
58 | NSMutableArray *array = [NSMutableArray array];
59 | for (RWAlbumViewModel *albums in _albums) {
60 | [array addObjectsFromArray:[albums returnSelectedModels]];
61 | }
62 |
63 | [[RWTransferCenter center] setupReadyTaskDatas:array];
64 | }
65 |
66 | -(NSArray *)albums {
67 | if (!_albums) {
68 | _albums = [NSMutableArray array];
69 | }
70 | return _albums;
71 | }
72 |
73 | @end
74 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/ViewModel/RWAlbumViewModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWAlbumViewModel.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/2/27.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 | @class PHFetchResult,RWAlbumModel;
11 | @interface RWAlbumViewModel : NSObject
12 |
13 | @property (copy, nonatomic, readonly)NSString *title;
14 |
15 | @property (copy, nonatomic, readonly)PHFetchResult *result;
16 |
17 | @property (assign, nonatomic, readonly)NSInteger count;
18 |
19 | @property (strong, nonatomic, readonly)NSArray *allAssets;
20 |
21 | @property (strong, nonatomic, readwrite)NSMutableArray *selectedAssets;
22 |
23 | @property (assign, nonatomic, readonly)NSInteger selectedCount;
24 |
25 | @property (assign, nonatomic, readonly)BOOL selected;
26 |
27 | @property (assign, nonatomic, readonly)NSString *fileType;
28 |
29 | - (instancetype)initWithModel:(RWAlbumModel *)model;
30 |
31 | - (void)selectOne:(NSInteger)index;
32 |
33 | - (void)selectedAll;
34 |
35 | - (void)removeOne:(NSInteger)index;
36 |
37 | - (void)removeAll;
38 |
39 | - (NSArray *)returnSelectedModels;
40 |
41 | @end
42 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/ViewModel/RWAlbumViewModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWAlbumViewModel.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/2/27.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWAlbumViewModel.h"
10 | #import "RWAlbumModel.h"
11 | #import "RWPhotoModel.h"
12 | #import "RWimageViewModel.h"
13 |
14 | @interface RWAlbumViewModel()
15 |
16 | @property (strong, nonatomic)RWAlbumModel *albumModel;
17 |
18 | @property (copy, nonatomic, readwrite)NSString *title;
19 |
20 | @property (copy, nonatomic, readwrite)PHFetchResult *result;
21 |
22 | @property (assign, nonatomic, readwrite)NSInteger count;
23 |
24 | @property (strong, nonatomic, readwrite)NSArray *allAssets;
25 |
26 | @property (assign, nonatomic, readwrite)NSInteger selectedCount;
27 |
28 | @property (assign, nonatomic, readwrite)NSString *fileType;
29 |
30 | @end
31 |
32 | @implementation RWAlbumViewModel
33 |
34 | - (instancetype)initWithModel:(RWAlbumModel *)model {
35 | self = [super init];
36 | if (self) {
37 | _albumModel = model;
38 | _title = model.title;
39 | _result = model.result;
40 | _count = model.count;
41 | _fileType = model.fileType;
42 | _allAssets = [self readAllAssets];
43 | _selectedAssets = [NSMutableArray array];
44 | }
45 | return self;
46 | }
47 |
48 | - (NSArray *)readAllAssets {
49 | NSMutableArray *photos = [NSMutableArray arrayWithCapacity:_result.count];
50 | NSRange range = NSMakeRange(0, _result.count);
51 | NSIndexSet *indexSet = [[NSIndexSet alloc] initWithIndexesInRange:range];
52 | NSArray *array = [[_result objectsAtIndexes:indexSet] mutableCopy];
53 |
54 | for (PHAsset *asset in array) {
55 | RWPhotoModel *photoModel = [[RWPhotoModel alloc] init];
56 | photoModel.name = [self getFileNameWith:asset];
57 | photoModel.asset = asset;
58 | photoModel.fileType = _fileType;
59 | photoModel.pathExtension = [photoModel.name pathExtension];
60 | RWimageViewModel *imageViewModel = [[RWimageViewModel alloc] initWithModel:photoModel];
61 | [photos addObject:imageViewModel];
62 | }
63 | return photos;
64 | }
65 |
66 | - (NSString *)getFileNameWith:(PHAsset *)asset {
67 | NSString *name = [NSString stringWithFormat:@"%ld", (long)([[NSDate date] timeIntervalSince1970] * 100000)];
68 | name = [name stringByReplacingOccurrencesOfString:@"." withString:@"_"];
69 | NSString *pathExtension = [self getPathExtensionWith:asset];
70 | name = [NSString stringWithFormat:@"%@.%@", name, pathExtension];
71 | return name;
72 | }
73 |
74 | - (NSString *)getPathExtensionWith:(PHAsset *)asset {
75 | NSString *filename = [asset valueForKey:@"filename"];
76 | NSString *pathExtension = [filename pathExtension];
77 | return pathExtension;
78 | }
79 |
80 | - (void)selectOne:(NSInteger)index {
81 | RWimageViewModel *imageViewModel = _allAssets[index];
82 | if (![_selectedAssets containsObject:imageViewModel]) {
83 | [_selectedAssets addObject:imageViewModel];
84 | }
85 | }
86 |
87 | - (void)selectedAll {
88 | _selectedAssets = [_allAssets mutableCopy];
89 | for (RWimageViewModel *viewModel in _selectedAssets) {
90 | viewModel.selected = YES;
91 | }
92 | }
93 |
94 | - (void)removeOne:(NSInteger)index {
95 | RWimageViewModel *imageViewModel = _allAssets[index];
96 | if ([_selectedAssets containsObject:imageViewModel]) {
97 | [_selectedAssets removeObject:imageViewModel];
98 | }
99 | }
100 |
101 | - (void)removeAll {
102 | for (RWimageViewModel *viewModel in _selectedAssets) {
103 | viewModel.selected = NO;
104 | }
105 | [_selectedAssets removeAllObjects];
106 | }
107 |
108 | - (BOOL)selected {
109 | if (self.count == self.selectedCount) {
110 | return YES;
111 | } else {
112 | return NO;
113 | }
114 | }
115 |
116 | - (NSInteger)selectedCount {
117 | return _selectedAssets.count;
118 | }
119 |
120 | -(NSArray *)returnSelectedModels {
121 | NSMutableArray *array = [NSMutableArray array];
122 | for (RWimageViewModel *imageModel in _selectedAssets) {
123 | [array addObject:imageModel.model];
124 | }
125 | return array;
126 | }
127 |
128 | @end
129 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/ViewModel/RWImageSelectorViewModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWImageSelectorViewModel.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/1.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWBaseViewModel.h"
10 |
11 | @class RWAlbumViewModel;
12 | @interface RWImageSelectorViewModel : RWBaseViewModel
13 |
14 | @property (strong, nonatomic)RWAlbumViewModel *albumViewModel;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/ViewModel/RWImageSelectorViewModel.m:
--------------------------------------------------------------------------------
1 |
2 | //
3 | // RWImageSelectorViewModel.m
4 | // RWFileTransferDemo
5 | //
6 | // Created by RyanWong on 2018/3/1.
7 | // Copyright © 2018年 RyanWong. All rights reserved.
8 | //
9 |
10 | #import "RWImageSelectorViewModel.h"
11 | #import "RWAlbumViewModel.h"
12 | #import "RWImageLoad.h"
13 |
14 | @implementation RWImageSelectorViewModel
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/ViewModel/RWimageViewModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWimageViewModel.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/1.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "RWBaseViewModel.h"
12 |
13 | @class RWPhotoModel;
14 | @interface RWimageViewModel : RWBaseViewModel
15 |
16 | @property (copy, nonatomic)RWPhotoModel *model;
17 |
18 | @property (copy, nonatomic, readonly)NSString *name;
19 |
20 | @property (copy, nonatomic, readonly)PHAsset *asset;
21 |
22 | @property (assign, nonatomic, readonly)long long size;
23 |
24 | @property (copy, nonatomic, readonly)NSString *fileType;
25 |
26 | @property (copy, nonatomic, readonly)NSString *pathExtension;
27 |
28 | @property (assign, nonatomic)BOOL selected;
29 |
30 | - (instancetype)initWithModel:(RWPhotoModel *)model;
31 |
32 | - (void)loadImageDataWithPhotoWidth:(CGFloat)photoWidth success:(void(^)(id))success failure:(void (^)(NSError *))failure;
33 |
34 | - (void)loadVideoDataSuccess:(void(^)(long long size, UIImage *image))success failure:(void (^)(NSError *))failure;
35 |
36 | @end
37 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/ImageSelector/ViewModel/RWimageViewModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWimageViewModel.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/1.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWimageViewModel.h"
10 | #import "RWPhotoModel.h"
11 | #import "RWImageLoad.h"
12 | #import
13 |
14 | @interface RWimageViewModel()
15 |
16 | @property (copy, nonatomic, readwrite)NSString *name;
17 |
18 | @property (copy, nonatomic, readwrite)PHAsset *asset;
19 |
20 | @property (assign, nonatomic, readwrite)long long size;
21 |
22 | @property (copy, nonatomic, readwrite)NSString *fileType;
23 |
24 | @property (copy, nonatomic, readwrite)NSString *pathExtension;
25 |
26 | @end
27 |
28 | @implementation RWimageViewModel
29 |
30 | - (instancetype)initWithModel:(RWPhotoModel *)model {
31 | self = [super init];
32 | if (self) {
33 | _model = model;
34 | _name = model.name;
35 | _asset = model.asset;
36 | _size = model.size;
37 | _fileType = model.fileType;
38 | _pathExtension = model.pathExtension;
39 | }
40 | return self;
41 | }
42 |
43 | - (void)loadImageDataWithPhotoWidth:(CGFloat)photoWidth success:(void(^)(id))success failure:(void (^)(NSError *))failure {
44 |
45 | [[RWImageLoad shareLoad] getPhotoWithAsset:_asset photoWidth:photoWidth completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
46 | !success?:success(photo);
47 | } progressHandler:nil networkAccessAllowed:NO];
48 | }
49 |
50 | - (void)loadVideoDataSuccess:(void(^)(long long size, UIImage *image))success failure:(void (^)(NSError *))failure {
51 |
52 | [[RWImageLoad shareLoad] getVideoInfoWithAsset:_asset completion:^(long long size, UIImage *image) {
53 | !success?:success(size, image);
54 | }];
55 | }
56 |
57 | @end
58 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/VideoSelector/Controller/RWVideoListViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWVideoListViewController.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/9.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWBaseViewController.h"
10 |
11 | @interface RWVideoListViewController : RWBaseViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/VideoSelector/Controller/RWVideoListViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWVideoListViewController.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/9.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWVideoListViewController.h"
10 | #import "RWAlbumListViewModel.h"
11 | #import "RWVideoTableView.h"
12 |
13 | @interface RWVideoListViewController ()
14 |
15 | @property (strong, nonatomic)RWVideoTableView *tv;
16 |
17 | @property (strong, nonatomic)RWAlbumListViewModel *viewModel;
18 |
19 | @property (strong, nonatomic)UIButton *sendBtn;
20 |
21 | @end
22 |
23 | @implementation RWVideoListViewController
24 |
25 | - (void)viewDidLoad {
26 | [super viewDidLoad];
27 |
28 | self.viewModel = (RWAlbumListViewModel *)self.baseViewModel;
29 |
30 | self.title = self.viewModel.title;
31 |
32 | [self.view addSubview:self.tv];
33 |
34 | [self.view addSubview:self.sendBtn];
35 |
36 | [self.viewModel loadAlbumDataContentType:RWAlbumListContentTypeVideo success:^(id responseObject) {
37 | NSLog(@"%@",self.viewModel.albums);
38 | [self.tv reloadWithData:self.viewModel.albums];
39 | } failure:^(NSError *error) {
40 |
41 | }];
42 |
43 | }
44 |
45 | - (void)sendAction {
46 | [self.viewModel submitAllTransferDatas];
47 | [self.navigationController popViewControllerAnimated:YES];
48 | }
49 |
50 | - (void)didReceiveMemoryWarning {
51 | [super didReceiveMemoryWarning];
52 | // Dispose of any resources that can be recreated.
53 | }
54 |
55 | -(RWVideoTableView *)tv {
56 | if (!_tv) {
57 | _tv = [[RWVideoTableView alloc] initWithFrame:self.view.frame];
58 | }
59 | return _tv;
60 | }
61 |
62 | -(UIButton *)sendBtn {
63 | if (!_sendBtn) {
64 | _sendBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - 50, self.view.frame.size.width, 50)];
65 | [_sendBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
66 | [_sendBtn setTitle:@"发送" forState:UIControlStateNormal];
67 | [_sendBtn addTarget:self action:@selector(sendAction) forControlEvents:UIControlEventTouchUpInside];
68 | }
69 | return _sendBtn;
70 | }
71 |
72 | @end
73 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/VideoSelector/View/RWVideoListViewCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWVideoListViewCell.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/9.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | typedef void(^RWVideoListViewCellSelectAction)(BOOL selected);
12 |
13 | @class RWimageViewModel;
14 | @interface RWVideoListViewCell : UITableViewCell
15 |
16 | @property (copy, nonatomic) RWVideoListViewCellSelectAction selectAction;
17 |
18 | - (void)bindViewModel:(RWimageViewModel *)viewModel;
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/VideoSelector/View/RWVideoListViewCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWVideoListViewCell.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/9.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWVideoListViewCell.h"
10 | #import "RWimageViewModel.h"
11 |
12 | @interface RWVideoListViewCell()
13 |
14 | @property (weak, nonatomic) IBOutlet UIImageView *coverV;
15 | @property (weak, nonatomic) IBOutlet UILabel *titleLab;
16 | @property (weak, nonatomic) IBOutlet UILabel *sizeLab;
17 | @property (weak, nonatomic) IBOutlet UIButton *selectBtn;
18 |
19 | @property (strong, nonatomic) RWimageViewModel *viewModel;
20 |
21 | @end
22 |
23 | @implementation RWVideoListViewCell
24 |
25 | - (void)awakeFromNib {
26 | [super awakeFromNib];
27 | // Initialization code
28 | }
29 |
30 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated {
31 | [super setSelected:selected animated:animated];
32 |
33 | // Configure the view for the selected state
34 | }
35 |
36 | - (void)bindViewModel:(RWimageViewModel *)viewModel {
37 | _viewModel = viewModel;
38 |
39 | _titleLab.text = _viewModel.name;
40 | _selectBtn.selected = _viewModel.selected;
41 |
42 | __weak typeof(self) weakSelf = self;
43 | [_viewModel loadVideoDataSuccess:^(long long size, UIImage *image) {
44 | dispatch_async(dispatch_get_main_queue(), ^{
45 | weakSelf.coverV.image = image;
46 | weakSelf.sizeLab.text = [NSString stringWithFormat:@"%.2fMB",(CGFloat)(size / 1024.0 / 1024.0)];
47 | });
48 | } failure:^(NSError *error) {
49 |
50 | }];
51 | }
52 |
53 | - (IBAction)selectAction:(id)sender {
54 | UIButton *button = (UIButton *)sender;
55 | button.selected = !button.selected;
56 | _viewModel.selected = button.selected;
57 | !_selectAction?:_selectAction(_viewModel.selected);
58 | }
59 |
60 | @end
61 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/VideoSelector/View/RWVideoListViewCell.xib:
--------------------------------------------------------------------------------
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 |
35 |
41 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/VideoSelector/View/RWVideoTableView.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWVideoTableView.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/9.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class RWAlbumViewModel;
12 | @interface RWVideoTableView : UIView
13 |
14 | - (void)reloadWithData:(NSArray *)data;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/File/VideoSelector/View/RWVideoTableView.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWVideoTableView.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/9.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWVideoTableView.h"
10 | #import "RWVideoListViewCell.h"
11 | #import "RWAlbumViewModel.h"
12 |
13 | @interface RWVideoTableView()
14 |
15 | @property (strong, nonatomic) UITableView *tv;
16 |
17 | @property (strong, nonatomic)NSMutableArray *albums;
18 |
19 | @property (strong, nonatomic)RWAlbumViewModel *albumViewModel;
20 |
21 | @end
22 |
23 | @implementation RWVideoTableView
24 |
25 | - (instancetype) initWithFrame:(CGRect)frame {
26 | self = [super initWithFrame:frame];
27 | if (self) {
28 | [self setupView];
29 | }
30 | return self;
31 | }
32 |
33 | - (void)setupView {
34 | [self addSubview:self.tv];
35 | }
36 |
37 | #pragma mark - TableView delegate datasource
38 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
39 | return _albumViewModel.allAssets.count;
40 | }
41 |
42 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
43 | RWVideoListViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([RWVideoListViewCell class]) forIndexPath:indexPath];
44 |
45 | [cell bindViewModel:_albumViewModel.allAssets[indexPath.row]];
46 |
47 | __weak typeof(self) weakSelf = self;
48 | cell.selectAction = ^(BOOL selected) {
49 | if (selected) {
50 | [weakSelf.albumViewModel selectOne:indexPath.row];
51 | } else {
52 | [weakSelf.albumViewModel removeOne:indexPath.row];
53 | }
54 | };
55 | return cell;
56 | }
57 |
58 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
59 | return 70.0;
60 | }
61 |
62 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
63 | [tableView deselectRowAtIndexPath:indexPath animated:YES];
64 | }
65 |
66 | #pragma mark - reload
67 | - (void)reloadData {
68 | [_tv reloadData];
69 | }
70 |
71 | - (void)reloadWithData:(NSArray *)data {
72 | // [self.albums removeAllObjects];
73 | // [self.albums setArray:data];
74 | _albumViewModel = data[0];
75 | [_tv reloadData];
76 | }
77 |
78 | #pragma mark - Lazy load
79 |
80 | - (UITableView *)tv {
81 | if (!_tv) {
82 | _tv = [[UITableView alloc] initWithFrame:self.frame style:UITableViewStylePlain];
83 | _tv.delegate = self;
84 | _tv.dataSource = self;
85 |
86 | [_tv registerNib:[UINib nibWithNibName:NSStringFromClass([RWVideoListViewCell class]) bundle:nil] forCellReuseIdentifier:NSStringFromClass([RWVideoListViewCell class])];
87 | }
88 | return _tv;
89 | }
90 |
91 | - (NSMutableArray *)albums {
92 | if (!_albums) {
93 | _albums = [NSMutableArray array];
94 | }
95 | return _albums;
96 | }
97 |
98 | @end
99 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/Search/Controller/SearchViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // SearchViewController.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/1/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface SearchViewController : UITableViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/Search/Controller/SearchViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // SearchViewController.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/1/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "SearchViewController.h"
10 | #import "RWBrowser.h"
11 | #import "RWSession.h"
12 | #import "RWUserCenter.h"
13 | #import "TransferListViewController.h"
14 |
15 | @interface SearchViewController ()
16 |
17 | @property (strong, nonatomic)NSMutableArray *peerArray;
18 |
19 | @end
20 |
21 | @implementation SearchViewController
22 |
23 | - (void)viewDidDisappear:(BOOL)animated{
24 | [super viewDidDisappear:animated];
25 |
26 | [[RWBrowser shareInstance] stopSearch];
27 | [RWSession kickPeer:[RWUserCenter center].myPeerID];
28 |
29 | [[NSNotificationCenter defaultCenter] removeObserver:self];
30 | }
31 |
32 | - (void)viewWillAppear:(BOOL)animated {
33 | [super viewWillAppear:animated];
34 |
35 | [[RWUserCenter center].session.session disconnect];
36 |
37 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(connect) name:kRWSessionStateConnectedNotification object:nil];
38 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notConnect) name:kRWSessionStateNotConnectedNotification object:nil];
39 | }
40 |
41 | - (void)viewDidLoad {
42 | [super viewDidLoad];
43 |
44 | self.title = @"搜索设备";
45 |
46 | NSString *deviceName = [UIDevice currentDevice].name;
47 | [[RWBrowser shareInstance] setConfigurationWithName:deviceName Identifier:@"rw"];
48 |
49 | [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"PeerIdCell"];
50 |
51 | [[RWBrowser shareInstance] startSearchNearbyService];
52 | [RWBrowser shareInstance].nearbyPeerBlock = ^(NSArray *peerArray) {
53 | [self.peerArray setArray:peerArray];
54 | [self.tableView reloadData];
55 | };
56 | }
57 |
58 | - (void)connect {
59 | dispatch_async(dispatch_get_main_queue(), ^{
60 | RWTransferListViewModel *vm = [[RWTransferListViewModel alloc] init];
61 | vm.title = @"传输圈";
62 | TransferListViewController *vc = [[TransferListViewController alloc] initWithViewModel:vm];
63 | [self.navigationController pushViewController:vc animated:YES];
64 | });
65 | }
66 |
67 | - (void)notConnect {
68 | dispatch_async(dispatch_get_main_queue(), ^{
69 | [self.navigationController popViewControllerAnimated:YES];
70 | });
71 | }
72 |
73 | - (void)didReceiveMemoryWarning {
74 | [super didReceiveMemoryWarning];
75 | // Dispose of any resources that can be recreated.
76 | }
77 |
78 | #pragma mark - Table view data source
79 |
80 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
81 | return 1;
82 | }
83 |
84 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
85 | return self.peerArray.count;
86 | }
87 |
88 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
89 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"PeerIdCell" forIndexPath:indexPath];
90 | MCPeerID *peerId = self.peerArray[indexPath.row];
91 | cell.textLabel.text = peerId.displayName;
92 | return cell;
93 | }
94 |
95 | -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
96 | [tableView deselectRowAtIndexPath:indexPath animated:YES];
97 |
98 | MCPeerID *peerId = self.peerArray[indexPath.row];
99 | [[RWBrowser shareInstance] invitePeer:peerId];
100 | }
101 |
102 | - (NSMutableArray *)peerArray {
103 | if (!_peerArray) {
104 | _peerArray = [NSMutableArray array];
105 | }
106 | return _peerArray;
107 | }
108 |
109 | @end
110 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/Transfer/Controller/TransferListViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // TransferViewController.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/1/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "RWBaseViewController.h"
11 | #import "RWTransferListViewModel.h"
12 | @interface TransferListViewController : RWBaseViewController
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/Transfer/Controller/TransferListViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // TransferViewController.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/1/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "TransferListViewController.h"
10 | #import "RWAlbumListViewController.h"
11 | #import "RWVideoListViewController.h"
12 | #import "RWTransferListView.h"
13 | #import "RWTransferViewModel.h"
14 | #import "RWTransferCenter.h"
15 |
16 | #import "RWUserCenter.h"
17 | #import "RWSession.h"
18 | #import "RWOutputStream.h"
19 | #import "RWInputStream.h"
20 |
21 | #import "RWFileManager.h"
22 | #import "RWFileHandle.h"
23 |
24 | @interface TransferListViewController ()
25 |
26 | @property (strong, nonatomic)RWTransferListViewModel *viewModel;
27 |
28 | @property (strong, nonatomic)RWTransferListView *transferListView;
29 |
30 | @property (strong, nonatomic)RWSession *session;
31 | @property (strong, nonatomic)RWOutputStream *outputStream;
32 | @property (strong, nonatomic)RWInputStream *inputStream;
33 |
34 | @end
35 |
36 | @implementation TransferListViewController
37 |
38 | - (void)viewWillAppear:(BOOL)animated {
39 | [super viewWillAppear:animated];
40 |
41 | [self sendTaskInfo];
42 | }
43 |
44 | - (void)viewDidLoad {
45 | [super viewDidLoad];
46 |
47 | _viewModel = (RWTransferListViewModel *)self.baseViewModel;
48 | [_viewModel setTarget:self];
49 |
50 | self.title = _viewModel.title;
51 |
52 | [self.view addSubview:self.transferListView];
53 |
54 | self.session.delegate = self;
55 |
56 | UIBarButtonItem *chooseBtn = [[UIBarButtonItem alloc] initWithTitle:@"选择文件" style:UIBarButtonItemStylePlain target:self action:@selector(chooseAction)];
57 | self.navigationItem.rightBarButtonItem = chooseBtn;
58 |
59 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notConnect) name:kRWSessionStateNotConnectedNotification object:nil];
60 |
61 | [self registerSenderTaskListener];
62 | }
63 |
64 | - (void)notConnect {
65 | dispatch_async(dispatch_get_main_queue(), ^{
66 | [self.navigationController popViewControllerAnimated:YES];
67 | });
68 | }
69 |
70 | - (void)chooseAction {
71 | UIAlertController *alertCtrl = [UIAlertController alertControllerWithTitle:@"请选择发送的文件目录" message:nil preferredStyle:UIAlertControllerStyleActionSheet];
72 |
73 | [alertCtrl addAction:[UIAlertAction actionWithTitle:@"照片" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
74 | dispatch_async(dispatch_get_main_queue(), ^{
75 | RWAlbumListViewModel *viewModel = [[RWAlbumListViewModel alloc] init];
76 | viewModel.title = @"选择相册";
77 | RWAlbumListViewController *vc = [[RWAlbumListViewController alloc] initWithViewModel:viewModel];
78 | [self.navigationController pushViewController:vc animated:YES];
79 | });
80 | }]];
81 |
82 | [alertCtrl addAction:[UIAlertAction actionWithTitle:@"视频" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
83 | dispatch_async(dispatch_get_main_queue(), ^{
84 | RWAlbumListViewModel *viewModel = [[RWAlbumListViewModel alloc] init];
85 | viewModel.title = @"选择视频";
86 | RWVideoListViewController *vc = [[RWVideoListViewController alloc] initWithViewModel:viewModel];
87 | [self.navigationController pushViewController:vc animated:YES];
88 | });
89 | }]];
90 |
91 | [alertCtrl addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
92 |
93 | }]];
94 |
95 | [self.navigationController presentViewController:alertCtrl animated:YES completion:nil];
96 | }
97 |
98 | - (void)sendTaskInfo {
99 | [_viewModel sendPeerTaskInfo];
100 | [_transferListView reloadTableView];
101 | }
102 |
103 | - (void)didReceiveMemoryWarning {
104 | [super didReceiveMemoryWarning];
105 | // Dispose of any resources that can be recreated.
106 | }
107 |
108 | #pragma mark - RWSession Delegate
109 |
110 | -(void)session:(RWSession *)session didReceiveData:(NSData *)data {
111 | [_viewModel handleReceiveData:data];
112 | }
113 |
114 | -(void)session:(RWSession *)session didReceiveStream:(NSInputStream *)stream WithName:(NSString *)streamName {
115 | [_viewModel createReceiveStreamWithStream:stream streamName:streamName];
116 | [_transferListView reloadTableView];
117 | }
118 |
119 | #pragma mark - RWOutputStream Delegate
120 |
121 | -(void)outputStream:(RWOutputStream *)outputStream progress:(long long)progress {
122 |
123 | }
124 |
125 | -(void)outputStream:(RWOutputStream *)outputStream transferEndWithStreamName:(NSString *)name {
126 | outputStream.delegate = nil;
127 | outputStream = nil;
128 |
129 | __weak typeof(self) weakSelf = self;
130 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
131 | [weakSelf.viewModel nextReadyTask];
132 | [self sendTaskInfo];
133 | });
134 | }
135 |
136 | -(void)outputStream:(RWOutputStream *)outputStream transferErrorWithStreamName:(NSString *)name {
137 | outputStream.delegate = nil;
138 | outputStream = nil;
139 |
140 | // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
141 | // [self sendFile];
142 | // });
143 | }
144 |
145 | #pragma mark - RWInputStream Delegate
146 | - (void)inputStream:(RWInputStream *)inputStream streamName:(NSString *)streamName progress:(long long)progress {
147 | [_viewModel receiveTaskProgressWithStreamName:streamName progress:progress];
148 | NSInteger index = [_viewModel getTaskIndexWithStreamName:streamName];
149 | [_transferListView reloadProgressCell:index];
150 | }
151 |
152 | - (void)inputStream:(RWInputStream *)inputStream transferEndWithStreamName:(NSString *)streamName filePath:(NSString *)filePath {
153 | __weak typeof(self) weakSelf = self;
154 | [_viewModel handleTmpFile:filePath name:streamName completion:^(NSString *targetName) {
155 | NSInteger index = [weakSelf.viewModel getTaskIndexWithStreamName:targetName];
156 | [weakSelf.transferListView reloadSingleCell:index];
157 | }];
158 | [_viewModel receiveTaskFinishWithStreamName:streamName];
159 |
160 | [inputStream stop];
161 | inputStream.delegate = nil;
162 | inputStream = nil;
163 |
164 | NSInteger index = [_viewModel getTaskIndexWithStreamName:streamName];
165 | [_transferListView reloadSingleCell:index];
166 | }
167 |
168 | - (void)inputStream:(RWInputStream *)inputStream transferErrorWithStreamName:(NSString *)streamName {
169 | [_viewModel receiveTaskErrorWithStreamName:streamName];
170 |
171 | [inputStream stop];
172 | inputStream.delegate = nil;
173 | inputStream = nil;
174 |
175 | NSInteger index = [_viewModel getTaskIndexWithStreamName:streamName];
176 | [_transferListView reloadSingleCell:index];
177 | }
178 |
179 | #pragma mark - Data From Receiver
180 | - (void)registerSenderTaskListener {
181 | __weak typeof(self) weakSelf = self;
182 | [_viewModel sendTaskBegin:^(NSDictionary *dict) {
183 | NSString *streamName = dict[@"timestamp"];
184 | NSInteger index = [weakSelf.viewModel getTaskIndexWithStreamName:streamName];
185 | [weakSelf.transferListView reloadSingleCell:index];
186 | }];
187 |
188 | [_viewModel sendTaskProgress:^(NSDictionary *dict) {
189 | NSString *streamName = dict[@"timestamp"];
190 | NSInteger index = [weakSelf.viewModel getTaskIndexWithStreamName:streamName];
191 | [weakSelf.transferListView reloadProgressCell:index];
192 | }];
193 |
194 | [_viewModel sendTaskFinish:^(NSDictionary *dict) {
195 | NSString *streamName = dict[@"timestamp"];
196 | NSInteger index = [weakSelf.viewModel getTaskIndexWithStreamName:streamName];
197 | [weakSelf.transferListView reloadSingleCell:index];
198 |
199 | // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
200 | // [_viewModel nextReadyTask];
201 | // [self sendTaskInfo];
202 | // });
203 | }];
204 |
205 | [_viewModel sendTaskError:^(NSDictionary *dict) {
206 | NSString *streamName = dict[@"timestamp"];
207 | NSInteger index = [weakSelf.viewModel getTaskIndexWithStreamName:streamName];
208 | [weakSelf.transferListView reloadSingleCell:index];
209 | }];
210 | }
211 |
212 | #pragma mark - Lazy load
213 | -(RWTransferListView *)transferListView {
214 | if (!_transferListView) {
215 | _transferListView = [[RWTransferListView alloc] initWithFrame:self.view.frame];
216 | }
217 | return _transferListView;
218 | }
219 |
220 | -(RWSession *)session {
221 | return [RWUserCenter center].session;
222 | }
223 |
224 | -(void)dealloc {
225 | RWStatus(@"传输圈销毁");
226 | [[NSNotificationCenter defaultCenter] removeObserver:self];
227 | [[RWUserCenter center] remove];
228 | [[RWTransferCenter center] removeAllTaskData];
229 | }
230 |
231 | @end
232 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/Transfer/View/RWTransferListCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWTransferListCell.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/22.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class RWTransferViewModel;
12 | @interface RWTransferListCell : UITableViewCell
13 |
14 | - (void)bindViewModel:(RWTransferViewModel *)viewModel;
15 |
16 | - (void)inNormalModel:(RWTransferViewModel *)viewModel;
17 |
18 | - (void)inProgressModel:(RWTransferViewModel *)viewModel;
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/Transfer/View/RWTransferListCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWTransferListCell.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/22.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWTransferListCell.h"
10 | #import "RWTransferViewModel.h"
11 | #import "RWImageLoad.h"
12 |
13 | static float const imageWidth = 50;
14 | static float const spaceWidth = 10;
15 |
16 | static float const buttonWidth = 50;
17 |
18 | @interface RWTransferListCell()
19 |
20 | @property (strong, nonatomic)CALayer *imageLayer;
21 | @property (strong, nonatomic)CATextLayer *nameLayer;
22 | @property (strong, nonatomic)CATextLayer *statusLayer;
23 |
24 | @property (strong, nonatomic)CALayer *progressLayer;
25 | @property (strong, nonatomic)CALayer *progressBgLayer;
26 | @property (strong, nonatomic)CATextLayer *sizeLayer;
27 | @property (strong, nonatomic)UIButton *cancelBtn;
28 | @property (assign, nonatomic)CGFloat fullProgressWidth;
29 |
30 | @property (strong, nonatomic)RWTransferViewModel *viewModel;
31 |
32 | @end
33 |
34 | @implementation RWTransferListCell
35 |
36 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
37 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
38 | if (self) {
39 | [self setupView];
40 | }
41 | return self;
42 | }
43 |
44 | - (void)setupView {
45 | _imageLayer = ({
46 | CALayer *layer = [CALayer layer];
47 | layer.frame = CGRectMake(spaceWidth, spaceWidth, imageWidth, imageWidth);
48 | layer.backgroundColor = [UIColor grayColor].CGColor;
49 | layer.contentsGravity = kCAGravityResize;
50 | layer;
51 | });
52 | [self.contentView.layer addSublayer:_imageLayer];
53 |
54 | _nameLayer = ({
55 | CGFloat width = kWidth - spaceWidth - imageWidth - spaceWidth - spaceWidth;
56 | CATextLayer *nameLayer = [CATextLayer layer];
57 | nameLayer.frame = CGRectMake(spaceWidth + imageWidth + spaceWidth, spaceWidth, width, 20);
58 | nameLayer.contentsScale = [UIScreen mainScreen].scale;
59 | nameLayer.foregroundColor =[UIColor blackColor].CGColor;
60 | nameLayer.fontSize = 15.0f;
61 | nameLayer;
62 | });
63 | [self.contentView.layer addSublayer:_nameLayer];
64 |
65 | _statusLayer = ({
66 | CGFloat width = kWidth - spaceWidth - imageWidth - spaceWidth - spaceWidth;
67 | CATextLayer *statusLayer = [CATextLayer layer];
68 | statusLayer.frame = CGRectMake(spaceWidth + imageWidth + spaceWidth, 35, width, 20);
69 | statusLayer.contentsScale = [UIScreen mainScreen].scale;
70 | statusLayer.foregroundColor =[UIColor blackColor].CGColor;
71 | statusLayer.fontSize = 13.0f;
72 | statusLayer;
73 | });
74 | [_statusLayer setHidden:YES];
75 | [self.contentView.layer addSublayer:_statusLayer];
76 |
77 |
78 | _fullProgressWidth = kWidth - spaceWidth - imageWidth - spaceWidth - spaceWidth - buttonWidth - spaceWidth;
79 | _progressBgLayer = ({
80 | CALayer *layer = [CALayer layer];
81 | layer.frame = CGRectMake(spaceWidth + imageWidth + spaceWidth, 35, _fullProgressWidth, 2);
82 | layer.backgroundColor = [UIColor grayColor].CGColor;
83 | layer;
84 | });
85 | [_progressBgLayer setHidden:YES];
86 | [self.contentView.layer addSublayer:_progressBgLayer];
87 |
88 | _progressLayer = ({
89 | CALayer *layer = [CALayer layer];
90 | layer.frame = CGRectMake(spaceWidth + imageWidth + spaceWidth, 35, 0, 2);
91 | layer.backgroundColor = [UIColor blueColor].CGColor;
92 | layer;
93 | });
94 | [_progressLayer setHidden:YES];
95 | [self.contentView.layer addSublayer:_progressLayer];
96 |
97 | _sizeLayer = ({
98 | CATextLayer *statusLayer = [CATextLayer layer];
99 | statusLayer.frame = CGRectMake(spaceWidth + imageWidth + spaceWidth, 40, _fullProgressWidth, 20);
100 | statusLayer.contentsScale = [UIScreen mainScreen].scale;
101 | statusLayer.foregroundColor =[UIColor blackColor].CGColor;
102 | statusLayer.fontSize = 13.0f;
103 | statusLayer;
104 | });
105 | [_sizeLayer setHidden:YES];
106 | [self.contentView.layer addSublayer:_sizeLayer];
107 |
108 | _cancelBtn = ({
109 | UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(spaceWidth + imageWidth + spaceWidth +_fullProgressWidth + spaceWidth, 10, buttonWidth, buttonWidth)];
110 | [button setBackgroundColor:[UIColor redColor]];
111 | [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
112 | [button setTitle:@"取消" forState:UIControlStateNormal];
113 | button;
114 | });
115 | [_cancelBtn setHidden:YES];
116 | [self.contentView addSubview:_cancelBtn];
117 | }
118 |
119 | - (void)bindViewModel:(RWTransferViewModel *)viewModel {
120 | _viewModel = viewModel;
121 |
122 | _nameLayer.string = viewModel.name;
123 |
124 | if (viewModel.cover) {
125 | _imageLayer.contents = (id)viewModel.cover.CGImage;
126 | } else {
127 | __weak typeof(self)weakSelf = self;
128 | if (viewModel.asset) {
129 | [viewModel loadImageDataWithPhotoWidth:150 success:^(id responseObject) {
130 | UIImage *image = (UIImage *)responseObject;
131 | weakSelf.imageLayer.contents = (id)image.CGImage;
132 | } failure:^(NSError *error) {
133 |
134 | }];
135 | } else if (viewModel.sandboxPath) {
136 | [viewModel loadSandBoxImageWithIsCoverSize:YES completion:^(UIImage *coverImage) {
137 | weakSelf.imageLayer.contents = (id)coverImage.CGImage;
138 | }];
139 | }
140 | }
141 |
142 | if (viewModel.status == RWTransferStatusTransfer) {
143 | [self inProgressModel:viewModel];
144 | } else {
145 | [self inNormalModel:viewModel];
146 | }
147 | }
148 |
149 | - (void)inNormalModel:(RWTransferViewModel *)viewModel {
150 |
151 | [self setProgress:0.0];
152 |
153 | [_statusLayer setHidden:NO];
154 | [_sizeLayer setHidden:YES];
155 | [_progressLayer setHidden:YES];
156 | [_progressBgLayer setHidden:YES];
157 | [_cancelBtn setHidden:YES];
158 |
159 | NSString *sizeStr = [RWCommon getFileSizeTextFromSize:viewModel.size];
160 | _statusLayer.string = [NSString stringWithFormat:@"%@(%@)", viewModel.statusText, sizeStr];
161 | }
162 |
163 | - (void)inProgressModel:(RWTransferViewModel *)viewModel {
164 |
165 | [_statusLayer setHidden:YES];
166 | [_sizeLayer setHidden:NO];
167 | [_progressLayer setHidden:NO];
168 | [_progressBgLayer setHidden:NO];
169 | [_cancelBtn setHidden:NO];
170 |
171 | NSString *transferSizeStr = [RWCommon getFileSizeTextFromSize:viewModel.transferSize];
172 | NSString *sizeStr = [RWCommon getFileSizeTextFromSize:viewModel.size];
173 | _sizeLayer.string = [NSString stringWithFormat:@"%@/%@", transferSizeStr, sizeStr];
174 |
175 | CGFloat progress = (CGFloat)_viewModel.transferSize / (CGFloat)_viewModel.size;
176 | [self setProgress:progress];
177 | }
178 |
179 | - (void)setProgress:(CGFloat)progress {
180 | if (progress <= 0) {
181 | self.progressLayer.frame = CGRectMake(spaceWidth + imageWidth + spaceWidth, 35, 0, 2);
182 | }else if (progress <= 1) {
183 | self.progressLayer.frame = CGRectMake(spaceWidth + imageWidth + spaceWidth, 35, progress * _fullProgressWidth, 2);
184 | }else {
185 | self.progressLayer.frame = CGRectMake(spaceWidth + imageWidth + spaceWidth, 35, _fullProgressWidth, 2);
186 | }
187 | }
188 |
189 |
190 | @end
191 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/Transfer/View/RWTransferListView.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWTransferListView.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/22.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface RWTransferListView : UIView
12 |
13 | - (void)reloadTableView;
14 |
15 | - (void)reloadSingleCell:(NSInteger)index;
16 |
17 | - (void)reloadProgressCell:(NSInteger)index;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/Transfer/View/RWTransferListView.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWTransferListView.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/22.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWTransferListView.h"
10 | #import "RWTransferListCell.h"
11 |
12 | #import "RWTransferViewModel.h"
13 | #import "RWTransferCenter.h"
14 |
15 | static NSString *const cellId = @"RWTransferListCell";
16 | @interface RWTransferListView ()
17 |
18 | @property (strong, nonatomic)UITableView *tb;
19 |
20 | @property (strong, nonatomic)RWTransferCenter *transferCenter;
21 |
22 | @end
23 |
24 | @implementation RWTransferListView
25 |
26 | #pragma mark - TableView
27 |
28 | -(instancetype)initWithFrame:(CGRect)frame {
29 | self = [super initWithFrame:frame];
30 | if (self) {
31 | [self setupView];
32 | }
33 | return self;
34 | }
35 |
36 | -(void)setupView {
37 | [self addSubview:self.tb];
38 | }
39 |
40 | -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
41 | return self.transferCenter.allTaskDatas.count;
42 | }
43 |
44 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
45 | RWTransferViewModel *taskViewModel = self.transferCenter.allTaskDatas[indexPath.row];
46 | RWTransferListCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId forIndexPath:indexPath];
47 | [cell bindViewModel:taskViewModel];
48 | return cell;
49 | }
50 |
51 | -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
52 | return 70;
53 | }
54 |
55 | - (void)reloadTableView {
56 | dispatch_async(dispatch_get_main_queue(), ^{
57 | [self.tb reloadData];
58 | });
59 | }
60 |
61 | - (void)reloadSingleCell:(NSInteger)index {
62 | dispatch_async(dispatch_get_main_queue(), ^{
63 | if (index >= 0) {
64 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
65 | RWTransferListCell *cell = [self.tb cellForRowAtIndexPath:indexPath];
66 | RWTransferViewModel *taskModel = self.transferCenter.allTaskDatas[index];
67 | [cell bindViewModel:taskModel];
68 | }
69 | });
70 | }
71 |
72 | - (void)reloadProgressCell:(NSInteger)index {
73 | dispatch_async(dispatch_get_main_queue(), ^{
74 | if (index >= 0) {
75 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
76 | RWTransferListCell *cell = [self.tb cellForRowAtIndexPath:indexPath];
77 | RWTransferViewModel *taskModel = self.transferCenter.allTaskDatas[index];
78 | [cell inProgressModel:taskModel];
79 | }
80 | });
81 | }
82 |
83 | #pragma mark - Lazy load
84 |
85 | -(UITableView *)tb {
86 | if (!_tb) {
87 | _tb = [[UITableView alloc] initWithFrame:self.frame style:UITableViewStylePlain];
88 | _tb.delegate = self;
89 | _tb.dataSource = self;
90 | [_tb registerClass:[RWTransferListCell class] forCellReuseIdentifier:cellId];
91 | }
92 | return _tb;
93 | }
94 |
95 | -(RWTransferCenter *)transferCenter {
96 | return [RWTransferCenter center];
97 | }
98 |
99 | @end
100 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/Transfer/ViewModel/RWTransferCenter.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWTransferCenter.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/3.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class RWPhotoModel,RWTransferViewModel;
12 | @interface RWTransferCenter : NSObject
13 |
14 | @property (strong, nonatomic)NSMutableArray *readyTaskDatas;
15 |
16 | @property (strong, nonatomic)NSMutableArray *allTaskDatas;
17 |
18 | + (instancetype)center;
19 |
20 | - (void)setupReadyTaskDatas:(NSArray *)datas;
21 |
22 | - (RWTransferViewModel *)currentReadyTask;
23 |
24 | - (RWTransferViewModel *)getTaskWithTimestampText:(NSString *)timestampText;
25 |
26 | - (NSInteger)getTaskIndexWithTimestampText:(NSString *)timestampText;
27 |
28 | - (void)nextReadyTask;
29 |
30 | - (void)createReceiveTask:(RWPhotoModel *)model withTimestampText:(NSString *)timestampText;
31 |
32 | - (void)removeAllTaskData;
33 |
34 | @end
35 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/Transfer/ViewModel/RWTransferCenter.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWTransferCenter.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/3.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWTransferCenter.h"
10 | #import "RWTransferViewModel.h"
11 |
12 | static RWTransferCenter *_center = nil;
13 |
14 | @interface RWTransferCenter()
15 |
16 | @end
17 |
18 | @implementation RWTransferCenter
19 |
20 | + (instancetype)center {
21 | static dispatch_once_t onceToken;
22 | dispatch_once(&onceToken, ^{
23 | _center = [[RWTransferCenter alloc] init];
24 | });
25 | return _center;
26 | }
27 |
28 | - (void)setupReadyTaskDatas:(NSArray *)datas {
29 | NSMutableArray *array = [NSMutableArray arrayWithCapacity:datas.count];
30 | for (RWPhotoModel *model in datas) {
31 | RWTransferViewModel *viewModel = [[RWTransferViewModel alloc] initWithModel:model];
32 | viewModel.source = RWTransferSourceMine;
33 | [array addObject:viewModel];
34 | RWLog(@"文件名 :%@", viewModel.name);
35 | }
36 | @synchronized(self) {
37 | [self.readyTaskDatas addObjectsFromArray:array];
38 | [self.allTaskDatas addObjectsFromArray:array];
39 | }
40 | }
41 |
42 | - (RWTransferViewModel *)currentReadyTask {
43 | RWTransferViewModel *viewModel;
44 | @synchronized(self) {
45 | viewModel = self.readyTaskDatas[0];
46 | }
47 | return viewModel;
48 | }
49 |
50 | - (RWTransferViewModel *)getTaskWithTimestampText:(NSString *)timestampText {
51 | RWTransferViewModel *viewModel;
52 | NSPredicate *pre = [NSPredicate predicateWithFormat:@"%K CONTAINS %@", @"timestampText", timestampText];
53 | NSArray *array = [self.allTaskDatas filteredArrayUsingPredicate:pre];
54 | if (array.count) {
55 | viewModel = array[0];
56 | }
57 | return viewModel;
58 | }
59 |
60 | - (NSInteger)getTaskIndexWithTimestampText:(NSString *)timestampText {
61 | RWTransferViewModel *viewModel = [self getTaskWithTimestampText:timestampText];
62 | NSInteger index = -1;
63 | index = [self.allTaskDatas indexOfObject:viewModel];
64 | return index;
65 | }
66 |
67 | - (void)nextReadyTask {
68 | @synchronized(self) {
69 | [self.readyTaskDatas removeObjectAtIndex:0];
70 | }
71 | }
72 |
73 | - (void)createReceiveTask:(RWPhotoModel *)model withTimestampText:(NSString *)timestampText{
74 | RWTransferViewModel *viewModel = [[RWTransferViewModel alloc] initWithModel:model];
75 | viewModel.source = RWTransferSourceOther;
76 | viewModel.timestampText = timestampText;
77 | [self addTaskToAllList:viewModel];
78 | }
79 |
80 | - (void)addTaskToAllList:(RWTransferViewModel *)taskModel {
81 | @synchronized(self) {
82 | [self.allTaskDatas addObject:taskModel];
83 | }
84 | }
85 |
86 | #pragma mark - Remove
87 | - (void)removeAllTaskData {
88 | [self.allTaskDatas removeAllObjects];
89 | [self.readyTaskDatas removeAllObjects];
90 | }
91 |
92 | #pragma mark - Lazy load
93 |
94 | - (NSMutableArray *)readyTaskDatas {
95 | if (!_readyTaskDatas) {
96 | _readyTaskDatas = [NSMutableArray array];
97 | }
98 | return _readyTaskDatas;
99 | }
100 |
101 | - (NSMutableArray *)allTaskDatas {
102 | if (!_allTaskDatas) {
103 | _allTaskDatas = [NSMutableArray array];
104 | }
105 | return _allTaskDatas;
106 | }
107 |
108 | @end
109 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/Transfer/ViewModel/RWTransferListViewModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWTransferListViewModel.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWBaseViewModel.h"
10 |
11 | typedef void(^fromReceiveBeginBlock)(NSDictionary *dict);
12 | typedef void(^fromReceiveProgressBlock)(NSDictionary *dict);
13 | typedef void(^fromReceiveFinishBlock)(NSDictionary *dict);
14 | typedef void(^fromReceiveErrorBlock)(NSDictionary *dict);
15 | typedef void(^fromReceiveCancelBlock)(NSDictionary *dict);
16 |
17 | @interface RWTransferListViewModel : RWBaseViewModel
18 |
19 | - (void)setTarget:(id)target;
20 |
21 | - (NSInteger)getTaskIndexWithStreamName:(NSString *)streamName;
22 |
23 | #pragma mark - Sender
24 |
25 | - (void)sendPeerTaskInfo;
26 |
27 | - (void)nextReadyTask;
28 |
29 | #pragma mark - From Receiver
30 |
31 | - (void)sendTaskBegin:(fromReceiveBeginBlock)beginBlock;
32 |
33 | - (void)sendTaskProgress:(fromReceiveProgressBlock)progressBlock;
34 |
35 | - (void)sendTaskFinish:(fromReceiveFinishBlock)finishBlock;
36 |
37 | - (void)sendTaskError:(fromReceiveErrorBlock)errorBlock;
38 |
39 | #pragma mark - Receiver
40 |
41 | - (void)handleReceiveData:(NSData *)data;
42 |
43 | - (void)createReceiveStreamWithStream:(NSInputStream *)stream streamName:(NSString *)streamName;
44 |
45 | #pragma mark - Tell Sender Task Status
46 |
47 | - (void)receiveTaskProgressWithStreamName:(NSString *)streamName progress:(long long)progress;
48 |
49 | - (void)receiveTaskFinishWithStreamName:(NSString *)streamName;
50 |
51 | - (void)receiveTaskErrorWithStreamName:(NSString *)streamName;
52 |
53 | #pragma mark - Common
54 |
55 | - (void)cancelTaskWithStreamName:(NSString *)streamName;
56 |
57 | - (void)sendTaskCancel:(fromReceiveCancelBlock)cancelBlock;
58 |
59 | #pragma mark - Handler Receive File
60 |
61 | - (void)handleTmpFile:(NSString *)filePath name:(NSString *)name completion:(void(^)(NSString *targetName))completion;
62 |
63 | @end
64 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/Transfer/ViewModel/RWTransferListViewModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWTransferListViewModel.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWTransferListViewModel.h"
10 | #import "RWDataTransfer.h"
11 | #import "RWFileManager.h"
12 | #import "RWFileHandle.h"
13 |
14 | #import "RWInputStream.h"
15 | #import "RWOutputStream.h"
16 |
17 | #import "RWUserCenter.h"
18 | #import "RWSession.h"
19 | #import "RWTransferCenter.h"
20 | #import "RWTransferViewModel.h"
21 |
22 | @interface RWTransferListViewModel()
23 |
24 | @property (strong, nonatomic)RWTransferCenter *center;
25 | @property (strong, nonatomic)RWSession *session;
26 |
27 | @property (assign, nonatomic)id target;
28 |
29 | @property (copy, nonatomic)fromReceiveBeginBlock beginBlock;
30 | @property (copy, nonatomic)fromReceiveProgressBlock progressBlock;
31 | @property (copy, nonatomic)fromReceiveFinishBlock finishBlock;
32 | @property (copy, nonatomic)fromReceiveErrorBlock errorBlock;
33 | @property (copy, nonatomic)fromReceiveCancelBlock cancelBlock;
34 |
35 | @end
36 |
37 | @implementation RWTransferListViewModel
38 |
39 | - (MCPeerID *)getFirstPeer {
40 | NSArray *peers = [self.session connectedPeers];
41 | if (peers.count) {
42 | return peers[0];
43 | }
44 | return nil;
45 | }
46 |
47 | - (NSInteger)getTaskIndexWithStreamName:(NSString *)streamName {
48 | NSInteger index = [self.center getTaskIndexWithTimestampText:streamName];
49 | return index;
50 | }
51 |
52 | #pragma mark - Send
53 |
54 | - (void)sendPeerTaskInfo {
55 | RWLog(@"任务准备:%@", self.center.readyTaskDatas);
56 | if (self.center.readyTaskDatas.count <= 0) {
57 | return;
58 | }
59 | RWTransferViewModel *viewModel = [self.center currentReadyTask];
60 | if (viewModel.status != RWTransferStatusReady) {
61 | RWLog(@"目标任务不在准备状态:%@", viewModel);
62 | return;
63 | }
64 | MCPeerID *peer = [self getFirstPeer];
65 | if (peer) {
66 | NSData *data = [viewModel getTaskData];
67 | [self.session sendData:data toPeers:@[peer]];
68 | }
69 |
70 | viewModel.status = RWTransferStatusPrepare;
71 | }
72 |
73 | - (void)createSendStreamWithTarget:(id)target task:(RWTransferViewModel *)taskModel {
74 | if (self.center.readyTaskDatas.count <= 0) {
75 | return;
76 | }
77 | if (taskModel.status != RWTransferStatusPrepare) {
78 | RWLog(@"目标任务不在准备状态:%@", taskModel);
79 | return;
80 | }
81 | RWLog(@"准备发送 %@", taskModel.timestampText);
82 | NSArray *peers = [self.session connectedPeers];
83 |
84 | if (peers.count) {
85 | RWOutputStream *outputStream = [[RWOutputStream alloc] initWithOutputStream:[self.session outputStreamForPeer:peers[0] With:taskModel.timestampText]];
86 | outputStream.streamName = taskModel.timestampText;
87 | outputStream.delegate = target;
88 | [outputStream streamWithAsset:taskModel.asset fileType:taskModel.fileType];
89 | [outputStream start];
90 |
91 | taskModel.status = RWTransferStatusTransfer;
92 | taskModel.outputStream = outputStream;
93 | }
94 | }
95 |
96 | - (void)nextReadyTask {
97 | [self.center nextReadyTask];
98 | }
99 |
100 | - (void)sendTaskCurrentProgressUpdate:(NSDictionary *)dict {
101 |
102 | NSString *timestampText = dict[@"timestamp"];
103 | long long progress = [dict[@"progress"] longLongValue];
104 | RWTransferViewModel *taskViewModel = [self.center getTaskWithTimestampText:timestampText];
105 | taskViewModel.transferSize = progress;
106 |
107 | if (_progressBlock) {
108 | _progressBlock(dict);
109 | }
110 | }
111 |
112 | - (void)sendTaskFinishUpdate:(NSDictionary *)dict {
113 |
114 | NSString *timestampText = dict[@"timestamp"];
115 | RWTransferViewModel *taskViewModel = [self.center getTaskWithTimestampText:timestampText];
116 | taskViewModel.status = RWTransferStatusFinish;
117 |
118 | if (_finishBlock) {
119 | _finishBlock(dict);
120 | }
121 | }
122 |
123 | - (void)sendTaskErrorUpdate:(NSDictionary *)dict {
124 |
125 | NSString *timestampText = dict[@"timestamp"];
126 | RWTransferViewModel *taskViewModel = [self.center getTaskWithTimestampText:timestampText];
127 | taskViewModel.status = RWTransferStatusError;
128 |
129 | if (_errorBlock) {
130 | _errorBlock(dict);
131 | }
132 | }
133 |
134 | #pragma mark - From Receiver
135 |
136 | - (void)sendTaskBegin:(fromReceiveBeginBlock)beginBlock {
137 | _beginBlock = beginBlock;
138 | }
139 |
140 | - (void)sendTaskProgress:(fromReceiveProgressBlock)progressBlock {
141 | _progressBlock = progressBlock;
142 | }
143 |
144 | - (void)sendTaskFinish:(fromReceiveFinishBlock)finishBlock {
145 | _finishBlock = finishBlock;
146 | }
147 |
148 | - (void)sendTaskError:(fromReceiveErrorBlock)errorBlock {
149 | _errorBlock = errorBlock;
150 | }
151 |
152 | #pragma mark - Receive
153 |
154 | - (void)handleReceiveData:(NSData *)data {
155 | NSDictionary *dict = [RWDataTransfer dataToDictionary:data];
156 | RWLog(@"收到数据 %@", dict);
157 | RWTransferDataType dataType = [dict[@"dataType"] integerValue];
158 | NSDictionary *dataDic = dict[@"data"];
159 | switch (dataType) {
160 | case RWTransferDataTypeSendTaskInfo:
161 | {
162 | [self receiveTaskInfo:dataDic];
163 | break;
164 | }
165 | case RWTransferDataTypeReceiveTaskInfo:
166 | {
167 | [self receiveTaskInfoCallback:dataDic];
168 | break;
169 | }
170 | case RWTransferDataTypeReceiveProgress:
171 | {
172 | [self sendTaskCurrentProgressUpdate:dataDic];
173 | break;
174 | }
175 | case RWTransferDataTypeFinish:
176 | {
177 | RWStatus(@"接收方接收完成");
178 | [self sendTaskFinishUpdate:dataDic];
179 | break;
180 | }
181 | case RWTransferDataTypeError:
182 | {
183 | RWStatus(@"接收方报错");
184 | [self sendTaskErrorUpdate:dataDic];
185 | break;
186 | }
187 |
188 | default:
189 | break;
190 | }
191 | }
192 |
193 | - (void)createReceiveStreamWithStream:(NSInputStream *)stream streamName:(NSString *)streamName {
194 | RWInputStream *inputStream = [[RWInputStream alloc] initWithInputStream:stream];
195 | inputStream.streamName = streamName;
196 | inputStream.delegate = _target;
197 | [inputStream start];
198 |
199 | //任务进入传输状态
200 | RWTransferViewModel *taskViewModel = [self.center getTaskWithTimestampText:streamName];
201 | taskViewModel.status = RWTransferStatusTransfer;
202 | taskViewModel.inputStream = inputStream;
203 | }
204 |
205 | #pragma mark - Handle Receive Data
206 | - (void)receiveTaskInfo:(NSDictionary *)data {
207 | RWStatus(@"接收到发送者发来的任务信息 %@", data);
208 | RWPhotoModel *model = [[RWPhotoModel alloc] initWithDictionary:data];
209 | NSString *timestampText = data[@"timestamp"];
210 | [self.center createReceiveTask:model withTimestampText:timestampText];
211 |
212 | NSDictionary *dict = @{
213 | @"dataType":@(RWTransferDataTypeReceiveTaskInfo),
214 | @"data":@{
215 | @"timestamp":timestampText
216 | }
217 | };
218 | [self sendDataWithDictionary:dict];
219 |
220 | RWTransferViewModel *taskViewModel = [self.center getTaskWithTimestampText:timestampText];
221 | taskViewModel.status = RWTransferStatusPrepare;
222 | }
223 |
224 | - (void)receiveTaskInfoCallback:(NSDictionary *)data {
225 | NSString *timestampText = data[@"timestamp"];
226 | RWTransferViewModel *taskViewModel = [self.center getTaskWithTimestampText:timestampText];
227 | if (taskViewModel) {
228 | [self createSendStreamWithTarget:_target task:taskViewModel];
229 | !_beginBlock?:_beginBlock(data);
230 | }
231 |
232 | RWStatus(@"发送者收到接收者通知具体哪个任务 %@", timestampText);
233 | }
234 |
235 | #pragma mark - Tell Sender Task Status
236 | - (void)receiveTaskProgressWithStreamName:(NSString *)streamName progress:(long long)progress {
237 | RWTransferViewModel *taskViewModel = [self.center getTaskWithTimestampText:streamName];
238 | taskViewModel.transferSize = progress;
239 |
240 | NSDictionary *dict = @{
241 | @"dataType":@(RWTransferDataTypeReceiveProgress),
242 | @"data":@{
243 | @"timestamp":streamName,
244 | @"progress":@(progress)
245 | }
246 | };
247 | [self sendDataWithDictionary:dict];
248 | }
249 |
250 | - (void)receiveTaskFinishWithStreamName:(NSString *)streamName {
251 | RWTransferViewModel *taskViewModel = [self.center getTaskWithTimestampText:streamName];
252 | taskViewModel.status = RWTransferStatusFinish;
253 |
254 | NSDictionary *dict = @{
255 | @"dataType":@(RWTransferDataTypeFinish),
256 | @"data":@{
257 | @"timestamp":streamName,
258 | }
259 | };
260 | [self sendDataWithDictionary:dict];
261 | }
262 |
263 | - (void)receiveTaskErrorWithStreamName:(NSString *)streamName {
264 | NSDictionary *dict = @{
265 | @"dataType":@(RWTransferDataTypeError),
266 | @"data":@{
267 | @"timestamp":streamName,
268 | }
269 | };
270 | [self sendDataWithDictionary:dict];
271 | }
272 |
273 | - (void)sendDataWithDictionary:(NSDictionary *)dict {
274 | MCPeerID *peer = [self getFirstPeer];
275 | if (peer) {
276 | NSData *data = [RWDataTransfer dictionaryToData:dict];
277 | [self.session sendData:data toPeers:@[peer]];
278 | }
279 | }
280 |
281 | #pragma mark - Common
282 |
283 | - (void)cancelTaskWithStreamName:(NSString *)streamName {
284 | RWTransferViewModel *taskViewModel = [self.center getTaskWithTimestampText:streamName];
285 | taskViewModel.status = RWTransferStatusCancel;
286 | [taskViewModel.outputStream stop];
287 | [taskViewModel.inputStream stop];
288 |
289 | NSDictionary *dict = @{
290 | @"dataType":@(RWTransferDataTypeCancel),
291 | @"data":@{
292 | @"timestamp":streamName,
293 | }
294 | };
295 | [self sendDataWithDictionary:dict];
296 | }
297 |
298 | - (void)sendTaskCancel:(fromReceiveCancelBlock)cancelBlock {
299 | _cancelBlock = cancelBlock;
300 | }
301 |
302 | - (void)sendTaskCancelUpdate:(NSDictionary *)dict {
303 | NSString *timestampText = dict[@"timestamp"];
304 | RWTransferViewModel *taskViewModel = [self.center getTaskWithTimestampText:timestampText];
305 | taskViewModel.status = RWTransferStatusCancel;
306 |
307 | if (_cancelBlock) {
308 | _cancelBlock(dict);
309 | }
310 | }
311 |
312 | #pragma mark - Handler Receive File
313 |
314 | - (void)handleTmpFile:(NSString *)filePath name:(NSString *)name completion:(void(^)(NSString *targetName))completion {
315 | RWLog(@"传输完成 得到临时文件路径 : %@ |||| %@", filePath, name);
316 |
317 | __weak typeof(self) weakSelf = self;
318 | dispatch_async(dispatch_get_global_queue(0, 0), ^{
319 | RWTransferViewModel *taskViewModel = [self.center getTaskWithTimestampText:name];
320 | NSString *fileDirectory;
321 | if ([taskViewModel.fileType isEqualToString:kFileTypePicture]) {
322 | fileDirectory = [RWFileManager picturesDirectory];
323 | } else if ([taskViewModel.fileType isEqualToString:kFileTypeVideo]) {
324 | fileDirectory = [RWFileManager videosDirectory];
325 | }
326 | NSString *fileName = taskViewModel.name;
327 | NSString *targetPath = [NSString stringWithFormat:@"%@/%@", fileDirectory, fileName];
328 | [weakSelf moveItemFrom:filePath to:targetPath];
329 | taskViewModel.sandboxPath = targetPath;
330 |
331 | dispatch_async(dispatch_get_main_queue(), ^{
332 | !completion?:completion(name);
333 | });
334 | });
335 | }
336 |
337 | - (void)moveItemFrom:(NSString *)filePath to:(NSString *)targetPath {
338 | [RWFileHandle copyFileFromPath:filePath toPath:targetPath];
339 | [RWFileManager deleteFileAtPath:filePath];
340 | }
341 |
342 | #pragma mark - Lazy load
343 |
344 | -(RWSession *)session {
345 | return [RWUserCenter center].session;
346 | }
347 |
348 | -(RWTransferCenter *)center {
349 | return [RWTransferCenter center];
350 | }
351 |
352 | @end
353 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/Transfer/ViewModel/RWTransferViewModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWTransferViewModel.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/2.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "RWPhotoModel.h"
11 | #import
12 |
13 | typedef NS_ENUM(NSUInteger, RWTransferStatus) {
14 | RWTransferStatusReady = 0, //任务初始状态
15 | RWTransferStatusPrepare, //任务准备状态 个别任务在发送前需要时间进行特别处理
16 | RWTransferStatusTransfer, //任务传输状态
17 | RWTransferStatusFinish, //任务完成状态
18 |
19 | RWTransferStatusCancel = 99, //任务取消状态
20 | RWTransferStatusError = 999, //任务错误状态
21 | };
22 |
23 | typedef NS_ENUM(NSUInteger, RWTransferSource) {
24 | RWTransferSourceNone,
25 | RWTransferSourceMine,
26 | RWTransferSourceOther,
27 | };
28 |
29 | @class RWOutputStream, RWInputStream;
30 | @interface RWTransferViewModel : NSObject
31 |
32 | @property (copy, nonatomic, readonly)NSString *name;
33 |
34 | @property (copy, nonatomic, readonly)PHAsset *asset;
35 |
36 | @property (copy, nonatomic)NSString *sandboxPath;
37 |
38 | @property (assign, nonatomic)long long size;
39 |
40 | @property (assign, nonatomic)long long transferSize;
41 |
42 | @property (assign, nonatomic, readonly)float progressValue;
43 |
44 | @property (assign, nonatomic)RWTransferStatus status;
45 |
46 | @property (copy, nonatomic, readonly)NSString *statusText;
47 |
48 | @property (assign, nonatomic)RWTransferSource source;
49 |
50 | @property (copy, nonatomic)NSString *timestampText;
51 |
52 | @property (copy, nonatomic, readonly)NSString *fileType;
53 |
54 | @property (copy, nonatomic, readonly)NSString *pathExtension;
55 |
56 | @property (strong, nonatomic)UIImage *cover;
57 |
58 | //*******************传输任务对应的流 Start********************//
59 |
60 | @property (strong, nonatomic)RWOutputStream *outputStream;
61 |
62 | @property (strong, nonatomic)RWInputStream *inputStream;
63 |
64 | //*******************传输任务对应的流 End********************//
65 |
66 | - (instancetype) initWithModel:(RWPhotoModel *)model;
67 |
68 | - (NSData *)getTaskData;
69 |
70 | - (void)loadImageDataWithPhotoWidth:(CGFloat)photoWidth success:(void(^)(id))success failure:(void (^)(NSError *))failure;
71 |
72 | - (void)loadSandBoxImageWithIsCoverSize:(BOOL)isCoverSize completion:(void(^)(UIImage *coverImage))completion;
73 |
74 | @end
75 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/Transfer/ViewModel/RWTransferViewModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWTransferViewModel.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/2.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWTransferViewModel.h"
10 |
11 | #import "RWDataTransfer.h"
12 | #import "RWImageLoad.h"
13 | #import "RWFileManager.h"
14 |
15 | #import "UIImage+Resize.h"
16 | #import "UIImage+Video.h"
17 |
18 | #import "RWOutputStream.h"
19 | #import "RWInputStream.h"
20 |
21 | static NSString *const RWTransferStatusReadyText = @"初始化";
22 | static NSString *const RWTransferStatusPrepareText = @"准备中";
23 | static NSString *const RWTransferStatusTransferText = @"传输中";
24 | static NSString *const RWTransferStatusFinishText = @"完成";
25 | static NSString *const RWTransferStatusCancelText = @"取消";
26 | static NSString *const RWTransferStatusErrorText = @"错误";
27 |
28 | @interface RWTransferViewModel()
29 |
30 | @property (copy, nonatomic)RWPhotoModel *model;
31 |
32 | @property (copy, nonatomic, readwrite)NSString *name;
33 |
34 | @property (copy, nonatomic, readwrite)PHAsset *asset;
35 |
36 | @property (assign, nonatomic, readwrite)float progressValue;
37 |
38 | @property (copy, nonatomic, readwrite)NSString *statusText;
39 |
40 | @property (copy, nonatomic, readwrite)NSString *fileType;
41 |
42 | @property (copy, nonatomic, readwrite)NSString *pathExtension;
43 |
44 | @end
45 |
46 | @implementation RWTransferViewModel
47 |
48 | - (instancetype) initWithModel:(RWPhotoModel *)model {
49 | self = [super init];
50 | if (self) {
51 | _model = model;
52 | _name = model.name;
53 | _asset = model.asset;
54 | _size = model.size;
55 | _progressValue = 0.0;
56 | _status = RWTransferStatusReady;
57 | _statusText = RWTransferStatusReadyText;
58 | _source = RWTransferSourceNone;
59 | _timestampText = [self getDefaultTimestamp];
60 | _fileType = model.fileType;
61 | _pathExtension = model.pathExtension;
62 | }
63 | return self;
64 | }
65 |
66 | - (NSData *)getTaskData {
67 | __weak typeof(self) weakSelf = self;
68 | NSLock *lock = [[NSLock alloc] init];
69 | __block NSInteger tag = 0;
70 | while(true) {
71 | [lock lock];
72 | if (tag != 0) {
73 | break;
74 | }
75 | if ([_fileType isEqualToString:kFileTypeVideo]) {
76 | [[RWImageLoad shareLoad] getVideoInfoWithAsset:_asset completion:^(long long size, UIImage *image) {
77 | weakSelf.size = size;
78 | tag = 1;
79 | [lock unlock];
80 | }];
81 | } else if ([_fileType isEqualToString:kFileTypePicture]) {
82 | [[RWImageLoad shareLoad] getPhotoDataWithAsset:_asset completion:^(NSData *imageData, NSString *dataUTI, NSDictionary *info) {
83 | weakSelf.size = imageData.length;
84 | tag = 1;
85 | [lock unlock];
86 | }];
87 | }
88 |
89 | }
90 |
91 | NSDictionary *dict = @{
92 | @"dataType":@(RWTransferDataTypeSendTaskInfo),
93 | @"data":@{
94 | @"name":_name,
95 | @"size":@(_size),
96 | @"timestamp":_timestampText,
97 | @"fileType":_fileType,
98 | @"pathExtension":_pathExtension
99 | }
100 | };
101 |
102 | NSData *data = [RWDataTransfer dictionaryToData:dict];
103 | return data;
104 | }
105 |
106 | - (void)setStatus:(RWTransferStatus)status {
107 | _status = status;
108 | switch (status) {
109 | case RWTransferStatusReady:
110 | _statusText = RWTransferStatusReadyText;
111 | break;
112 |
113 | case RWTransferStatusPrepare:
114 | _statusText = RWTransferStatusPrepareText;
115 | break;
116 |
117 | case RWTransferStatusTransfer:
118 | _statusText = RWTransferStatusTransferText;
119 | break;
120 |
121 | case RWTransferStatusFinish:
122 | _statusText = RWTransferStatusFinishText;
123 | break;
124 |
125 | case RWTransferStatusCancel:
126 | _statusText = RWTransferStatusCancelText;
127 | break;
128 |
129 | case RWTransferStatusError:
130 | _statusText = RWTransferStatusErrorText;
131 | break;
132 |
133 | default:
134 | break;
135 | }
136 | }
137 |
138 | - (void)loadImageDataWithPhotoWidth:(CGFloat)photoWidth success:(void(^)(id))success failure:(void (^)(NSError *))failure {
139 |
140 | __weak typeof(self) weakSelf = self;
141 | [[RWImageLoad shareLoad] getPhotoWithAsset:_asset photoWidth:photoWidth completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) {
142 | weakSelf.cover = photo;
143 | !success?:success(photo);
144 | } progressHandler:nil networkAccessAllowed:NO];
145 | }
146 |
147 | - (void)loadSandBoxImageWithIsCoverSize:(BOOL)isCoverSize completion:(void(^)(UIImage *coverImage))completion {
148 |
149 | if ([RWFileManager fileExistsAtPath:_sandboxPath]) {
150 | UIImage *cover = nil;
151 | if ([_fileType isEqualToString:kFileTypePicture]) {
152 | cover = [UIImage imageWithContentsOfFile:_sandboxPath];
153 | } else if ([_fileType isEqualToString:kFileTypeVideo]) {
154 | cover = [UIImage getThumbnailImageWithFilePath:_sandboxPath];
155 | }
156 | if (isCoverSize) {
157 | cover = [UIImage imageWithImageSimple:cover scaledToSize:CoverSize];
158 | _cover = cover;
159 | }
160 | !completion?:completion(cover);
161 | }
162 | }
163 |
164 | - (NSString *)getDefaultTimestamp {
165 | NSTimeInterval timestamp = [[NSDate date] timeIntervalSince1970];
166 | NSString *timestampText = [NSString stringWithFormat:@"%f", timestamp];
167 | return timestampText;
168 | }
169 |
170 | @end
171 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/Wait/Controller/WaitViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // WaitViewController.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/1/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface WaitViewController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/Wait/Controller/WaitViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // WaitViewController.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/1/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "WaitViewController.h"
10 | #import "RWBrowser.h"
11 | #import "RWSession.h"
12 | #import "RWUserCenter.h"
13 | #import "TransferListViewController.h"
14 |
15 | @interface WaitViewController ()
16 |
17 | @end
18 |
19 | @implementation WaitViewController
20 |
21 | - (void)viewDidDisappear:(BOOL)animated{
22 | [super viewDidDisappear:animated];
23 |
24 | [[RWBrowser shareInstance] stopWait];
25 |
26 | [[NSNotificationCenter defaultCenter] removeObserver:self];
27 | }
28 |
29 | - (void)viewWillAppear:(BOOL)animated {
30 | [super viewWillAppear:animated];
31 |
32 | [[RWUserCenter center].session.session disconnect];
33 |
34 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(connect) name:kRWSessionStateConnectedNotification object:nil];
35 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notConnect) name:kRWSessionStateNotConnectedNotification object:nil];
36 | }
37 |
38 | - (void)viewDidLoad {
39 | [super viewDidLoad];
40 |
41 | self.title = @"等待连接";
42 |
43 | NSString *deviceName = [UIDevice currentDevice].name;
44 | [[RWBrowser shareInstance] setConfigurationWithName:deviceName Identifier:@"rw"];
45 |
46 | [[RWBrowser shareInstance] startWaitForConnect];
47 | }
48 |
49 | - (void)connect {
50 | dispatch_async(dispatch_get_main_queue(), ^{
51 | RWTransferListViewModel *vm = [[RWTransferListViewModel alloc] init];
52 | vm.title = @"传输圈";
53 | TransferListViewController *vc = [[TransferListViewController alloc] initWithViewModel:vm];
54 | [self.navigationController pushViewController:vc animated:YES];
55 | });
56 | }
57 |
58 | - (void)notConnect {
59 | dispatch_async(dispatch_get_main_queue(), ^{
60 | [self.navigationController popViewControllerAnimated:YES];
61 | });
62 | }
63 |
64 | - (void)didReceiveMemoryWarning {
65 | [super didReceiveMemoryWarning];
66 | // Dispose of any resources that can be recreated.
67 | }
68 |
69 | /*
70 | #pragma mark - Navigation
71 |
72 | // In a storyboard-based application, you will often want to do a little preparation before navigation
73 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
74 | // Get the new view controller using [segue destinationViewController].
75 | // Pass the selected object to the new view controller.
76 | }
77 | */
78 |
79 | @end
80 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Section/Wait/Controller/WaitViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Tool/RWDataTransfer/RWDataTransfer.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWDataTransfer.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface RWDataTransfer : NSObject
12 |
13 | + (NSData *)dictionaryToData:(NSDictionary *)dict;
14 |
15 | + (NSDictionary *)dataToDictionary:(NSData *)data;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Tool/RWDataTransfer/RWDataTransfer.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWDataTransfer.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWDataTransfer.h"
10 |
11 | @implementation RWDataTransfer
12 |
13 | + (NSData *)dictionaryToData:(NSDictionary *)dict {
14 | NSError *error;
15 | NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:&error];
16 | if (error) {
17 | RWError(@"%@", [error userInfo].description);
18 | }
19 | return data;
20 | }
21 |
22 | + (NSDictionary *)dataToDictionary:(NSData *)data {
23 | NSError *error;
24 | NSDictionary *dictionary =[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:&error];
25 | if (error) {
26 | RWError(@"%@", [error userInfo].description);
27 | }
28 | return dictionary;
29 | }
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Tool/RWFileHandle/RWFileHandle.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWFileHandle.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/8.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface RWFileHandle : NSObject
12 |
13 | + (void)copyFileFromPath:(NSString *)path1 toPath:(NSString *)path2;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Tool/RWFileHandle/RWFileHandle.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWFileHandle.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/8.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWFileHandle.h"
10 | #import "RWFileManager.h"
11 |
12 | @implementation RWFileHandle
13 |
14 | + (void)copyFileFromPath:(NSString *)path1 toPath:(NSString *)path2
15 | {
16 | NSFileHandle * fh1 = [NSFileHandle fileHandleForReadingAtPath:path1];//读到内存
17 | [[NSFileManager defaultManager] createFileAtPath:path2 contents:nil attributes:nil];//写之前必须有该文件
18 | NSFileHandle * fh2 = [NSFileHandle fileHandleForWritingAtPath:path2];//写到文件
19 | NSData * _data = nil;
20 | unsigned long long ret = [fh1 seekToEndOfFile];//返回文件大小
21 | if (ret < 1024 * 1024 * 5) {//小于5M的文件一次读写
22 | [fh1 seekToFileOffset:0];
23 | _data = [fh1 readDataToEndOfFile];
24 | [fh2 writeData:_data];
25 | }else{
26 | NSUInteger n = ret / (1024 * 1024 * 5);
27 | if (ret % (1024 * 1024 * 5) != 0) {
28 | n++;
29 | }
30 | NSUInteger offset = 0;
31 | NSUInteger size = 1024 * 1024 * 5;
32 | for (NSUInteger i = 0; i < n - 1; i++) {
33 | //大于5M的文件多次读写
34 | [fh1 seekToFileOffset:offset];
35 | @autoreleasepool {
36 | /*该自动释放池必须要有否则内存一会就爆了
37 | 原因在于readDataOfLength方法返回了一个自动释放的对象,它只能在遇到自动释放池的时候才释放.如果不手动写这个自动释放池,会导致_data指向的对象不能及时释放,最终导致内存爆了.
38 | */
39 | _data = [fh1 readDataOfLength:size];
40 | [fh2 seekToEndOfFile];
41 | [fh2 writeData:_data];
42 | NSLog(@"%lu/%lu", i + 1, n - 1);
43 | }
44 | offset += size;
45 | }
46 | //最后一次剩余的字节
47 | [fh1 seekToFileOffset:offset];
48 | _data = [fh1 readDataToEndOfFile];
49 | [fh2 seekToEndOfFile];
50 | [fh2 writeData:_data];
51 | }
52 | [fh1 closeFile];
53 | [fh2 closeFile];
54 | }
55 |
56 | @end
57 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Tool/RWFileManager/RWFileManager.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWFileManager.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/8.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | static NSString *const PictureDirectoryName = @"pictures";
12 |
13 | static NSString *const VideoDirectoryName = @"videos";
14 |
15 | @interface RWFileManager : NSObject
16 |
17 |
18 | + (NSString *)picturesDirectory;
19 |
20 | + (NSString *)videosDirectory;
21 |
22 | + (NSString *)documentPath;
23 |
24 | + (NSString *)cachePath;
25 |
26 | + (NSString *)tmpPath;
27 |
28 |
29 | + (BOOL)fileExistsAtPath:(NSString *)path;
30 |
31 | + (BOOL)createBlankFileAtPath:(NSString *)path;
32 |
33 | + (BOOL)createFileAtPath:(NSString *)path contents:(NSData *)data;
34 |
35 | + (BOOL)createDirectoryAtPath:(NSString *)path;
36 |
37 | + (BOOL)deleteFileAtPath:(NSString *)path;
38 |
39 | + (NSArray *)subPathAtPath:(NSString *)path;
40 |
41 | @end
42 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Tool/RWFileManager/RWFileManager.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWFileManager.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/3/8.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWFileManager.h"
10 |
11 | @implementation RWFileManager
12 |
13 | + (NSString *)picturesDirectory {
14 | NSString *picturesDirectory = [[self documentPath] stringByAppendingFormat:@"/%@", PictureDirectoryName];
15 | if (![self fileExistsAtPath:picturesDirectory]) {
16 | [self createDirectoryAtPath:picturesDirectory];
17 | }
18 | return picturesDirectory;
19 | }
20 |
21 | + (NSString *)videosDirectory {
22 | NSString *videosDirectory = [[self documentPath] stringByAppendingFormat:@"/%@", VideoDirectoryName];
23 | if (![self fileExistsAtPath:videosDirectory]) {
24 | [self createDirectoryAtPath:videosDirectory];
25 | }
26 | return videosDirectory;
27 | }
28 |
29 | + (NSString *)documentPath {
30 | return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
31 | }
32 |
33 | + (NSString *)cachePath {
34 | return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
35 | }
36 |
37 | + (NSString *)tmpPath {
38 | return NSTemporaryDirectory();
39 | }
40 |
41 |
42 |
43 | + (BOOL)fileExistsAtPath:(NSString *)path {
44 | return [[NSFileManager defaultManager] fileExistsAtPath:path];
45 | }
46 |
47 | + (BOOL)createBlankFileAtPath:(NSString *)path {
48 | return [self createFileAtPath:path contents:nil];
49 | }
50 |
51 | + (BOOL)createFileAtPath:(NSString *)path contents:(NSData *)data{
52 | return [[NSFileManager defaultManager] createFileAtPath:path contents:data attributes:nil];
53 | }
54 |
55 | + (BOOL)createDirectoryAtPath:(NSString *)path {
56 | NSError *error;
57 | BOOL isSuccess = [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error];
58 | if (error) {
59 | NSLog(@"createDirectoryError : %@", error);
60 | }
61 | return isSuccess;
62 | }
63 |
64 | + (BOOL)deleteFileAtPath:(NSString *)path {
65 | NSError *error;
66 | BOOL isSuccess = [[NSFileManager defaultManager] removeItemAtPath:path error:&error];
67 | if (error) {
68 | NSLog(@"deleteFileError : %@", error);
69 | }
70 | return isSuccess;
71 | }
72 |
73 | + (NSArray *)subPathAtPath:(NSString *)path {
74 | NSError *error;
75 | NSArray *array = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:path error:&error];
76 | if (error) {
77 | NSLog(@"subpathsOfDirectoryError : %@", error);
78 | }
79 | return array;
80 | }
81 |
82 | @end
83 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Tool/RWImageLoad/RWImageLoad.h:
--------------------------------------------------------------------------------
1 | //
2 | // RWImageLoad.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/2/26.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import
12 |
13 | @interface RWImageLoad : NSObject
14 |
15 | +(instancetype)shareLoad;
16 |
17 | - (void)getAlbumContentImage:(BOOL)contentImage contentVideo:(BOOL)contentVideo completion:(void (^)(NSMutableArray *albums))completion;
18 |
19 | - (PHImageRequestID)getPhotoWithAsset:(id)asset photoWidth:(CGFloat)photoWidth completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler networkAccessAllowed:(BOOL)networkAccessAllowed;
20 |
21 | - (PHImageRequestID)getPhotoDataWithAsset:(id)asset completion:(void (^)(NSData *imageData, NSString *dataUTI, NSDictionary * info))completion;
22 |
23 | - (PHImageRequestID)getVideoInfoWithAsset:(id)asset completion:(void (^)(long long size, UIImage *image))completion;
24 |
25 | - (PHImageRequestID)getVideoDataWithAsset:(id)asset completion:(void (^)(NSData *data))completion;
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/Tool/RWImageLoad/RWImageLoad.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWImageLoad.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/2/26.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "RWImageLoad.h"
10 | #import "RWAlbumModel.h"
11 | #import "UIImage+Resize.h"
12 | #import "UIImage+Video.h"
13 |
14 | static RWImageLoad *_instance = nil;
15 | @implementation RWImageLoad
16 |
17 | + (instancetype)shareLoad {
18 | static dispatch_once_t onceToken;
19 | dispatch_once(&onceToken, ^{
20 | _instance = [[RWImageLoad alloc] init];
21 | });
22 | return _instance;
23 | }
24 |
25 | - (void)getAlbumContentImage:(BOOL)contentImage contentVideo:(BOOL)contentVideo completion:(void (^)(NSMutableArray *albums))completion
26 | {
27 |
28 | PHFetchOptions *option = [[PHFetchOptions alloc] init];
29 | if (!contentVideo) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", PHAssetMediaTypeImage];
30 | if (!contentImage) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld",
31 | PHAssetMediaTypeVideo];
32 | option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
33 |
34 | PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
35 |
36 | __block NSMutableArray *albums = [[NSMutableArray alloc] initWithCapacity:smartAlbums.count];
37 |
38 | if (!contentVideo) {
39 | for (PHAssetCollection *collection in smartAlbums)
40 | {
41 | if (![collection isKindOfClass:[PHAssetCollection class]]) continue; // 有可能是PHCollectionList类的的对象,过滤掉
42 | PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:collection options:option];
43 | if (fetchResult.count > 0) {
44 | RWAlbumModel *model = [self modelWithResult:fetchResult name:collection.localizedTitle];
45 | model.fileType = kFileTypePicture;
46 | [albums addObject:model];
47 | }
48 | }
49 | }
50 |
51 | // 获得相机胶卷
52 | PHAssetCollection *cameraRoll = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeSmartAlbumUserLibrary options:nil].lastObject;
53 | PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:cameraRoll options:option];
54 | if (fetchResult.count > 0) {
55 | RWAlbumModel *model = [self modelWithResult:fetchResult name:cameraRoll.localizedTitle];
56 | if (!contentVideo) {
57 | model.fileType = kFileTypePicture;
58 | } else {
59 | model.fileType = kFileTypeVideo;
60 | }
61 | [albums insertObject:model atIndex:0];
62 | }
63 |
64 | if (completion) {
65 | completion(albums);
66 | }
67 | }
68 |
69 | - (RWAlbumModel *)modelWithResult:(PHFetchResult*)result name:(NSString *)name {
70 | RWAlbumModel *album = [[RWAlbumModel alloc] init];
71 | album.title = name;
72 | album.result = result;
73 | album.count = result.count;
74 | return album;
75 | }
76 |
77 | - (PHImageRequestID)getPhotoWithAsset:(id)asset photoWidth:(CGFloat)photoWidth completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler networkAccessAllowed:(BOOL)networkAccessAllowed
78 | {
79 |
80 | PHImageManager *manger = [PHImageManager defaultManager];
81 | int32_t imageRequestID = [manger requestImageForAsset:asset targetSize:CGSizeMake(([[UIScreen mainScreen] bounds].size.width-15)/3, ([[UIScreen mainScreen] bounds].size.width-15)/3) contentMode:PHImageContentModeAspectFit options:nil resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
82 | completion(result,info,true);
83 | }];
84 | return imageRequestID;
85 | }
86 |
87 | - (PHImageRequestID)getPhotoDataWithAsset:(id)asset completion:(void (^)(NSData *imageData, NSString *dataUTI, NSDictionary * info))completion {
88 |
89 | PHImageManager *manger = [PHImageManager defaultManager];
90 | PHImageRequestOptions *option = [[PHImageRequestOptions alloc] init];
91 | option.synchronous = YES;
92 | int32_t imageRequestID = [manger requestImageDataForAsset:asset options:option resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
93 | UIImage* image = [UIImage imageWithData:imageData];
94 | imageData = UIImageJPEGRepresentation(image, 1.0);
95 |
96 | !completion?:completion(imageData, dataUTI, info);
97 |
98 | }];
99 | return imageRequestID;
100 | }
101 |
102 | - (PHImageRequestID)getVideoInfoWithAsset:(id)asset completion:(void (^)(long long size, UIImage *image))completion {
103 | return [self getVideoInfo:YES AndData:NO WithAsset:asset completion:^(long long size, UIImage *image, NSData *data) {
104 | !completion?:completion(size, image);
105 | }];
106 | }
107 |
108 | - (PHImageRequestID)getVideoDataWithAsset:(id)asset completion:(void (^)(NSData *data))completion {
109 | return [self getVideoInfo:NO AndData:YES WithAsset:asset completion:^(long long size, UIImage *image, NSData *data) {
110 | !completion?:completion(data);
111 | }];
112 | }
113 |
114 | - (PHImageRequestID)getVideoInfo:(BOOL)returnInfo AndData:(BOOL)returnData WithAsset:(id)asset completion:(void (^)(long long size, UIImage *image, NSData *data))completion {
115 | PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
116 | options.version = PHVideoRequestOptionsVersionOriginal;
117 | int32_t imageRequestID = [[PHImageManager defaultManager] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) {
118 | if ([asset isKindOfClass:[AVURLAsset class]]) {
119 | AVURLAsset* urlAsset = (AVURLAsset*)asset;
120 | if (returnInfo) {
121 | NSNumber *size;
122 | [urlAsset.URL getResourceValue:&size forKey:NSURLFileSizeKey error:nil];
123 |
124 | UIImage *image = [UIImage getThumbnailImageWithURLAsset:urlAsset];
125 | image = [UIImage imageWithImageSimple:image scaledToSize:CoverSize];
126 | !completion?:completion([size longLongValue], image, nil);
127 | }
128 |
129 | if (returnData) {
130 | NSError *error = nil;
131 | NSData *data = [NSData dataWithContentsOfURL:urlAsset.URL options:NSDataReadingMappedIfSafe error:&error];
132 | if (error) {
133 | NSLog(@"视频错误 :%@", error);
134 | }
135 | NSLog(@"data length %lu", (unsigned long)data.length);
136 | !completion?:completion(0, nil, data);
137 | }
138 | }
139 | }];
140 | return imageRequestID;
141 | }
142 |
143 | @end
144 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/1/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/1/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 | #import "SearchViewController.h"
11 | #import "WaitViewController.h"
12 | #import "RWAlbumListViewController.h"
13 | #import "RWVideoListViewController.h"
14 | #import "RWAlbumListViewModel.h"
15 | #import "RWBrowser.h"
16 |
17 |
18 | #import "RWImageLoad.h"
19 |
20 | @interface ViewController ()
21 |
22 | @end
23 |
24 | @implementation ViewController
25 |
26 | - (void)viewDidLoad {
27 | [super viewDidLoad];
28 | }
29 |
30 |
31 | - (void)didReceiveMemoryWarning {
32 | [super didReceiveMemoryWarning];
33 | // Dispose of any resources that can be recreated.
34 | }
35 |
36 | - (IBAction)sendAction:(id)sender {
37 | SearchViewController *vc = [[SearchViewController alloc] init];
38 | [self.navigationController pushViewController:vc animated:YES];
39 | }
40 | - (IBAction)receiveAction:(id)sender {
41 | WaitViewController *vc = [[WaitViewController alloc] init];
42 | [self.navigationController pushViewController:vc animated:YES];
43 | }
44 | - (IBAction)checkAction:(id)sender {
45 | RWAlbumListViewModel *viewModel = [[RWAlbumListViewModel alloc] init];
46 | // viewModel.title = @"选择相册";
47 | // RWAlbumListViewController *vc = [[RWAlbumListViewController alloc] initWithViewModel:viewModel];
48 | viewModel.title = @"选择视频";
49 | RWVideoListViewController *vc = [[RWVideoListViewController alloc] initWithViewModel:viewModel];
50 | [self.navigationController pushViewController:vc animated:YES];
51 | }
52 |
53 | @end
54 |
--------------------------------------------------------------------------------
/RWFileTransferDemo/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // RWFileTransferDemo
4 | //
5 | // Created by RyanWong on 2018/1/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/RWFileTransferDemoTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/RWFileTransferDemoTests/RWFileTransferDemoTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWFileTransferDemoTests.m
3 | // RWFileTransferDemoTests
4 | //
5 | // Created by RyanWong on 2018/1/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface RWFileTransferDemoTests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation RWFileTransferDemoTests
16 |
17 | - (void)setUp {
18 | [super setUp];
19 | // Put setup code here. This method is called before the invocation of each test method in the class.
20 | }
21 |
22 | - (void)tearDown {
23 | // Put teardown code here. This method is called after the invocation of each test method in the class.
24 | [super tearDown];
25 | }
26 |
27 | - (void)testExample {
28 | // This is an example of a functional test case.
29 | // Use XCTAssert and related functions to verify your tests produce the correct results.
30 | }
31 |
32 | - (void)testPerformanceExample {
33 | // This is an example of a performance test case.
34 | [self measureBlock:^{
35 | // Put the code you want to measure the time of here.
36 | }];
37 | }
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/RWFileTransferDemoUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/RWFileTransferDemoUITests/RWFileTransferDemoUITests.m:
--------------------------------------------------------------------------------
1 | //
2 | // RWFileTransferDemoUITests.m
3 | // RWFileTransferDemoUITests
4 | //
5 | // Created by RyanWong on 2018/1/17.
6 | // Copyright © 2018年 RyanWong. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface RWFileTransferDemoUITests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation RWFileTransferDemoUITests
16 |
17 | - (void)setUp {
18 | [super setUp];
19 |
20 | // Put setup code here. This method is called before the invocation of each test method in the class.
21 |
22 | // In UI tests it is usually best to stop immediately when a failure occurs.
23 | self.continueAfterFailure = NO;
24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
25 | [[[XCUIApplication alloc] init] launch];
26 |
27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
28 | }
29 |
30 | - (void)tearDown {
31 | // Put teardown code here. This method is called after the invocation of each test method in the class.
32 | [super tearDown];
33 | }
34 |
35 | - (void)testExample {
36 | // Use recording to get started writing UI tests.
37 | // Use XCTAssert and related functions to verify your tests produce the correct results.
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------