Commit 89c5d1425d3781882e060e00ccd0927c716127d3
1 parent
f4202c95
提交解决问题
TODO: 1.bundle文件整理 2.宏定义改为系统方法编写
Showing
46 changed files
with
4707 additions
and
0 deletions
Too many changes to show.
To preserve performance only 46 of 62 files are displayed.
CNLiveImagePickerController.podspec
0 → 100644
| 1 | +# | ||
| 2 | +# Be sure to run `pod spec lint CNLiveImagePickerController.podspec' to ensure this is a | ||
| 3 | +# valid spec and to remove all comments including this before submitting the spec. | ||
| 4 | +# | ||
| 5 | +# To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html | ||
| 6 | +# To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ | ||
| 7 | +# | ||
| 8 | + | ||
| 9 | +Pod::Spec.new do |s| | ||
| 10 | + | ||
| 11 | + # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # | ||
| 12 | + # | ||
| 13 | + # These will help people to find your library, and whilst it | ||
| 14 | + # can feel like a chore to fill in it's definitely to your advantage. The | ||
| 15 | + # summary should be tweet-length, and the description more in depth. | ||
| 16 | + # | ||
| 17 | + | ||
| 18 | + s.name = "CNLiveImagePickerController" # 项目名称 | ||
| 19 | + s.version = "0.0.1" # 版本号 与 你仓库的 标签号 对应 | ||
| 20 | + s.summary = "相册选择基于TZImagePickerController" # 项目简介 | ||
| 21 | + | ||
| 22 | + # This description is used to generate tags and improve search results. | ||
| 23 | + # * Think: What does it do? Why did you write it? What is the focus? | ||
| 24 | + # * Try to keep it short, snappy and to the point. | ||
| 25 | + # * Write the description between the DESC delimiters below. | ||
| 26 | + # * Finally, don't worry about the indent, CocoaPods strips it! | ||
| 27 | + s.description = <<-DESC | ||
| 28 | + 1.相册选择图片,视频 | ||
| 29 | + DESC | ||
| 30 | + | ||
| 31 | + s.homepage = "http://bj.gitlab.cnlive.com/ios-team/CNLiveImagePickerController" | ||
| 32 | + | ||
| 33 | + # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" | ||
| 34 | + | ||
| 35 | + | ||
| 36 | + # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # | ||
| 37 | + # | ||
| 38 | + # Licensing your code is important. See http://choosealicense.com for more info. | ||
| 39 | + # CocoaPods will detect a license file if there is a named LICENSE* | ||
| 40 | + # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. | ||
| 41 | + # | ||
| 42 | + | ||
| 43 | + s.license = "MIT" # 开源证书 | ||
| 44 | + # s.license = { :type => "MIT", :file => "FILE_LICENSE" } | ||
| 45 | + | ||
| 46 | + | ||
| 47 | + # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # | ||
| 48 | + # | ||
| 49 | + # Specify the authors of the library, with email addresses. Email addresses | ||
| 50 | + # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also | ||
| 51 | + # accepts just a name if you'd rather not provide an email address. | ||
| 52 | + # | ||
| 53 | + # Specify a social_media_url where others can refer to, for example a twitter | ||
| 54 | + # profile URL. | ||
| 55 | + # | ||
| 56 | + | ||
| 57 | + s.author = { "梁星国" => "liangxingguo@cnlive.com" } | ||
| 58 | + # Or just: s.author = "梁星国" | ||
| 59 | + # s.authors = { "梁星国" => "jyyjkt@qq.com" } | ||
| 60 | + # s.social_media_url = "http://twitter.com/梁星国" | ||
| 61 | + | ||
| 62 | + # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # | ||
| 63 | + # | ||
| 64 | + # If this Pod runs only on iOS or OS X, then specify the platform and | ||
| 65 | + # the deployment target. You can optionally include the target after the platform. | ||
| 66 | + # | ||
| 67 | + | ||
| 68 | + # s.platform = :ios | ||
| 69 | + s.platform = :ios, "9.0" | ||
| 70 | + | ||
| 71 | + # When using multiple platforms | ||
| 72 | + # s.ios.deployment_target = "5.0" | ||
| 73 | + # s.osx.deployment_target = "10.7" | ||
| 74 | + # s.watchos.deployment_target = "2.0" | ||
| 75 | + # s.tvos.deployment_target = "9.0" | ||
| 76 | + | ||
| 77 | + | ||
| 78 | + # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # | ||
| 79 | + # | ||
| 80 | + # Specify the location from where the source should be retrieved. | ||
| 81 | + # Supports git, hg, bzr, svn and HTTP. | ||
| 82 | + # | ||
| 83 | + | ||
| 84 | + s.source = { :git => "http://bj.gitlab.cnlive.com/ios-team/CNLiveImagePickerController.git", :tag => "#{s.version}" } #你的仓库地址 | ||
| 85 | + | ||
| 86 | + | ||
| 87 | + # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # | ||
| 88 | + # | ||
| 89 | + # CocoaPods is smart about how it includes source code. For source files | ||
| 90 | + # giving a folder will include any swift, h, m, mm, c & cpp files. | ||
| 91 | + # For header files it will include any header in the folder. | ||
| 92 | + # Not including the public_header_files will make all headers public. | ||
| 93 | + # | ||
| 94 | + | ||
| 95 | + s.source_files = "CNLiveImagePickerController/*" | ||
| 96 | + s.exclude_files = "Classes/Exclude" | ||
| 97 | + | ||
| 98 | + # s.public_header_files = "Classes/**/*.h" | ||
| 99 | + | ||
| 100 | + | ||
| 101 | + # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # | ||
| 102 | + # | ||
| 103 | + # A list of resources included with the Pod. These are copied into the | ||
| 104 | + # target bundle with a build phase script. Anything else will be cleaned. | ||
| 105 | + # You can preserve files from being cleaned, please don't preserve | ||
| 106 | + # non-essential files like tests, examples and documentation. | ||
| 107 | + # | ||
| 108 | + | ||
| 109 | + # s.resource = "icon.png" | ||
| 110 | + # s.resources = "Resources/*.png" | ||
| 111 | + | ||
| 112 | + # s.preserve_paths = "FilesToSave", "MoreFilesToSave" | ||
| 113 | + | ||
| 114 | + | ||
| 115 | + # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # | ||
| 116 | + # | ||
| 117 | + # Link your library with frameworks, or libraries. Libraries do not include | ||
| 118 | + # the lib prefix of their name. | ||
| 119 | + # | ||
| 120 | + | ||
| 121 | + # s.framework = "SomeFramework" | ||
| 122 | + s.frameworks = "UIKit", "Foundation" #支持的框架 | ||
| 123 | + | ||
| 124 | + # s.library = "iconv" | ||
| 125 | + # s.libraries = "iconv", "xml2" | ||
| 126 | + | ||
| 127 | + | ||
| 128 | + # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # | ||
| 129 | + # | ||
| 130 | + # If your library depends on compiler flags you can set them in the xcconfig hash | ||
| 131 | + # where they will only apply to your library. If you depend on other Podspecs | ||
| 132 | + # you can include multiple dependencies to ensure it works. | ||
| 133 | + | ||
| 134 | + s.requires_arc = true | ||
| 135 | + | ||
| 136 | + # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } | ||
| 137 | + # s.dependency "JSONKit", "~> 1.4" | ||
| 138 | + | ||
| 139 | +end |
CNLiveImagePickerController/AppCommonDefine.h
0 → 100644
| 1 | +// | ||
| 2 | +// AppCommonDefine.h | ||
| 3 | +// CNLiveNetAdd | ||
| 4 | +// | ||
| 5 | +// Created by 张旭 on 2018/1/2. | ||
| 6 | +// Copyright © 2018年 cnlive. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#ifndef AppCommonDefine_h | ||
| 10 | +#define AppCommonDefine_h | ||
| 11 | + | ||
| 12 | +#ifndef CNLiveNetAddUrlSchems | ||
| 13 | +#define CNLiveNetAddUrlSchems @"CNLiveNetAddUrlSchems" // | ||
| 14 | +#endif | ||
| 15 | + | ||
| 16 | +#ifndef CNThirdPayBack | ||
| 17 | +#define CNThirdPayBack @"CNThirdPayBack" //三方支付返回标识 | ||
| 18 | +#endif | ||
| 19 | + | ||
| 20 | +#ifndef CNThirdPayOrderId | ||
| 21 | +#define CNThirdPayOrderId @"CNThirdPayOrderId" //三方支付订单Id | ||
| 22 | +#endif | ||
| 23 | + | ||
| 24 | +#ifndef CNDarenHomeSelectChangeNotification | ||
| 25 | +#define CNDarenHomeSelectChangeNotification @"CNDarenHomeSelectChangeNotification" //达人主页选择type 改变 | ||
| 26 | +#define CNAddGroupMemberNotification @"CNAddGroupMemberNotification" //添加群成员 | ||
| 27 | +#define CNDeleteGroupMemberNotification @"CNDeleteGroupMemberNotification" //删除群成功 | ||
| 28 | +#define CNMoviePlaybackStateChangeNotication @"CNMoviePlaybackStateChangeNotication" //播放状态发生改变 | ||
| 29 | +#define CNApplyRefundSuccessNotification @"CNApplyRefundSuccessNotification" //申请退款成功 | ||
| 30 | +#define CNEvaluateSuccessNotioncation @"CNEvaluateSuccessNotioncation" //评论成功 | ||
| 31 | +#define CNUpdateDefultAddressSuccessNotication @"CNUpdateDefultAddressSuccessNotication" //添加或修个收货地址成功 | ||
| 32 | +#define CNDeleteDefultAddressSuccessNotication @"CNDeleteDefultAddressSUccessNotication" //删除默认的收货地址 | ||
| 33 | +#define CNSendImageSuccessNotication @"CNSendImageSuccessNotication" //发送图片成功,更新UI | ||
| 34 | +#define CNRatatePortraitNotication @"CNRatatePortraitNotication" //全屏点击分享通知播放界面 由全屏变为竖屏 | ||
| 35 | +#endif | ||
| 36 | + | ||
| 37 | +//*** Native Classify | ||
| 38 | + | ||
| 39 | +//点击推荐页的栏目 跳转到对应栏目详细页 | ||
| 40 | +#define ChangeHomeChannel @"changeHomeChannel" | ||
| 41 | + | ||
| 42 | +//首页pagingView News类型的页面从屏幕上消失 news页面上的播放器同时消失 | ||
| 43 | +#define NewsPagingViewDisappear @"NewsPagingViewDisappear" | ||
| 44 | + | ||
| 45 | +#define OldChannelVersion @"OldChannelVersion" //首页频道列表版本号 | ||
| 46 | +#define NewChannelVersion @"NewChannelVersion" | ||
| 47 | + | ||
| 48 | +#define NormalChannelArray @"NormalChannelArray" //常用频道 | ||
| 49 | +#define OtherChannelArray @"OtherChannelArray" //其他频道 | ||
| 50 | + | ||
| 51 | +#define AppVersion [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] //CFBundleShortVersionString | ||
| 52 | + | ||
| 53 | +//*** | ||
| 54 | + | ||
| 55 | +// 分类板块h5地址存储 | ||
| 56 | +#ifndef CNLiveNetAddCategoryChannels | ||
| 57 | +#define CNLiveNetAddCategoryChannels @"CNLiveNetAddCategoryChannels" | ||
| 58 | +#endif | ||
| 59 | +// App Store版本号存储 | ||
| 60 | +#ifndef CNLiveNetAddAppStoreVersionNumber | ||
| 61 | +#define CNLiveNetAddAppStoreVersionNumber @"CNLiveNetAddAppStoreVersionNumber" | ||
| 62 | +#endif | ||
| 63 | +//////////////////////////////////////////////////////// | ||
| 64 | +//////////// AppDelegate | ||
| 65 | +//////////////////////////////////////////////////////// | ||
| 66 | +#ifndef AppKeyWindow | ||
| 67 | +#define AppKeyWindow ((AppDelegate *)[UIApplication sharedApplication].delegate).window | ||
| 68 | +#endif | ||
| 69 | + | ||
| 70 | +#ifndef CNAppDelegate | ||
| 71 | +#define CNAppDelegate ((AppDelegate *)[UIApplication sharedApplication].delegate) | ||
| 72 | +#endif | ||
| 73 | + | ||
| 74 | + | ||
| 75 | +//////////////////////////////////////////////////////// | ||
| 76 | +//////////// function | ||
| 77 | +//////////////////////////////////////////////////////// | ||
| 78 | +#define isAllowChangeTheme 0 | ||
| 79 | + | ||
| 80 | + | ||
| 81 | +//////////////////////////////////////////////////////// | ||
| 82 | +//////////// SCreen | ||
| 83 | +//////////////////////////////////////////////////////// | ||
| 84 | +#define KScreenWidth ([UIScreen mainScreen].bounds.size.width > [UIScreen mainScreen].bounds.size.height ? [UIScreen mainScreen].bounds.size.height : [UIScreen mainScreen].bounds.size.width) | ||
| 85 | +#define KScreenHeight ([UIScreen mainScreen].bounds.size.width > [UIScreen mainScreen].bounds.size.height ? [UIScreen mainScreen].bounds.size.width : [UIScreen mainScreen].bounds.size.height) | ||
| 86 | + | ||
| 87 | + | ||
| 88 | +//判断iPHoneXr | ||
| 89 | +#define Device_Is_iPhoneXS_XR ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(828, 1792), [[UIScreen mainScreen] currentMode].size) : NO) | ||
| 90 | + | ||
| 91 | +//判断iPHoneX、iPHoneXs | ||
| 92 | +#define Device_Is_iPhoneX ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) : NO) | ||
| 93 | +#define Device_Is_iPhoneXS ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1125, 2436), [[UIScreen mainScreen] currentMode].size) : NO) | ||
| 94 | + | ||
| 95 | +//判断iPhoneXs Max | ||
| 96 | +#define Device_Is_iPhoneXS_MAX ([UIScreen instancesRespondToSelector:@selector(currentMode)] ? CGSizeEqualToSize(CGSizeMake(1242, 2688), [[UIScreen mainScreen] currentMode].size) : NO) | ||
| 97 | + | ||
| 98 | +#define IS_IPhoneX_All (CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(375, 812)) ||CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(812, 375)) ||CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(414, 896)) ||CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(896, 414))) | ||
| 99 | + | ||
| 100 | + | ||
| 101 | +#define kStatusHeight (int)[UIApplication sharedApplication].statusBarFrame.size.height //x44 非x20 | ||
| 102 | +#define kNavigationBarHeight ((int)[UIApplication sharedApplication].statusBarFrame.size.height+44) //x88 非x64 | ||
| 103 | +#define kVerticalNaviBarHeight ((IS_IPhoneX_All > 0) ? 88 : 44) | ||
| 104 | +#define kVerticalTabBarTotalHeight ((IS_IPhoneX_All > 0) ? 83 : 49) | ||
| 105 | +#define kVerticalBottomSafeHeight ((IS_IPhoneX_All > 0) ? 34 : 0) | ||
| 106 | +#define kVerticalStatusHeight ((IS_IPhoneX_All > 0) ? 44 : 20) | ||
| 107 | +#define kVerticalTopHeight ((IS_IPhoneX_All > 0) ? 24 : 0) | ||
| 108 | +#define kHorizontalTopNaviBarHeight 32 | ||
| 109 | +#define kHorizontalBottomHeight ((IS_IPhoneX_All > 0) ? 21 : 0) | ||
| 110 | +#define kHorizontalLeftHeight ((IS_IPhoneX_All > 0) ? 44 : 0) | ||
| 111 | +#define kHorizontalRightHeight ((IS_IPhoneX_All > 0) ? 44 : 0) | ||
| 112 | +#define kHorizontalTopHeight ((IS_IPhoneX_All > 0) ? 24 : 0) | ||
| 113 | +//////////////////////分类中直播点播横屏特用//////////////////////////// | ||
| 114 | +#define kLiveHorizontalTopHeight ((Device_Is_iPhoneX > 0) ? 6 : 0) | ||
| 115 | +#define kLiveHorizontalBottomHeight ((Device_Is_iPhoneX > 0) ? 11 : 0) | ||
| 116 | + | ||
| 117 | +//////////////////////////////////////////////////////// | ||
| 118 | +//////////// Font | ||
| 119 | +//////////////////////////////////////////////////////// | ||
| 120 | +#define kAppLargeTextFont [UIFont systemFontOfSize:16] | ||
| 121 | +#define kAppMiddleTextFont [UIFont systemFontOfSize:14] | ||
| 122 | +#define kAppSmallTextFont [UIFont systemFontOfSize:12] | ||
| 123 | + | ||
| 124 | +#define TopToolBarHeight (64.0 + IPhoneXSafeAreaInsets.bottom) | ||
| 125 | + | ||
| 126 | +//////////////////////////////////////////////////////// | ||
| 127 | +//////////// 发送图片,视频限制 | ||
| 128 | +//////////////////////////////////////////////////////// | ||
| 129 | +#define kFriendsCircle_limit_photo 20*1024.0*1024.0 //朋友圈限制图片20M | ||
| 130 | +#define kFirendsCircle_limit_video 30*1024.0*1024.0 //朋友圈限制视频30M | ||
| 131 | +#define kTarentoComment_limit_photo 10*1024.0*1024.0 //达人评论10M | ||
| 132 | +#define kSweepCode_limit_phoho 10*1024.0*1024.0 //扫码10M限制 | ||
| 133 | +#define kChat_limit_photo 20*1024.0*1024.0 //聊天限制图片20M | ||
| 134 | +#define kChat_limit_video 20*1024.0*1024.0 //聊天限制视频20M | ||
| 135 | + | ||
| 136 | +#define kChat_limit_video_length 5*60 //聊天限制视频5分钟 | ||
| 137 | +#define kFriendsCircle_limit_video_maxEdit 5*60 //朋友圈限制视频5分钟 | ||
| 138 | +#define kFriendsCircle_limit_video_max 10*60 //朋友圈限制最大视频编辑10分钟 | ||
| 139 | + | ||
| 140 | +//////////////////////////////////////////////////////// | ||
| 141 | +//////////// Color | ||
| 142 | +//////////////////////////////////////////////////////// | ||
| 143 | + | ||
| 144 | +#define RGBOF(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \ | ||
| 145 | +green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \ | ||
| 146 | +blue:((float)(rgbValue & 0xFF))/255.0 \ | ||
| 147 | +alpha:1.0] | ||
| 148 | + | ||
| 149 | +#define RGBA_OF(rgbValue) [UIColor colorWithRed:((float)(((rgbValue) & 0xFF000000) >> 24))/255.0 \ | ||
| 150 | +green:((float)(((rgbValue) & 0x00FF0000) >> 16))/255.0 \ | ||
| 151 | +blue:((float)(rgbValue & 0x0000FF00) >> 8)/255.0 \ | ||
| 152 | +alpha:((float)(rgbValue & 0x000000FF))/255.0] | ||
| 153 | + | ||
| 154 | +#define RGBAOF(v, a) [UIColor colorWithRed:((float)(((v) & 0xFF0000) >> 16))/255.0 \ | ||
| 155 | +green:((float)(((v) & 0x00FF00) >> 8))/255.0 \ | ||
| 156 | +blue:((float)(v & 0x0000FF))/255.0 \ | ||
| 157 | +alpha:a] | ||
| 158 | + | ||
| 159 | + | ||
| 160 | + | ||
| 161 | +#define RGBA(r,g,b,a) [UIColor colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:a] | ||
| 162 | +#define RGB(r,g,b) RGBA(r,g,b,1.0f) | ||
| 163 | +#define CommonBgColor [UIColor colorWithHexString:@"eeeeee"]; | ||
| 164 | + | ||
| 165 | +#define kDarenBgColor [UIColor colorWithRed:0.36 green:0.36 blue:0.36 alpha:1.00] | ||
| 166 | + | ||
| 167 | +#define kDarenGradeColor [UIColor colorWithRed:0.85 green:0.36 blue:0.10 alpha:1.00] | ||
| 168 | + | ||
| 169 | +#define kDarenYellowColor [UIColor colorWithRed:0.97 green:0.75 blue:0.03 alpha:1.00] | ||
| 170 | + | ||
| 171 | +#define kDarenGreenColor [UIColor colorWithRed:0.08 green:0.86 blue:0.52 alpha:1.00] | ||
| 172 | + | ||
| 173 | +#define kToolGrayColor [UIColor colorWithRed:0.94 green:0.94 blue:0.94 alpha:1.00] | ||
| 174 | + | ||
| 175 | +#define kCameraNextBtnGrayColor [UIColor colorWithRed:0.56 green:0.56 blue:0.56 alpha:1.00] | ||
| 176 | + | ||
| 177 | +#define kViewCommonGrayColor [UIColor colorWithRed:0.97 green:0.97 blue:0.97 alpha:1.00] | ||
| 178 | + | ||
| 179 | +#define kLineGrayColor [UIColor colorWithRed:0.95 green:0.95 blue:0.95 alpha:1.00] | ||
| 180 | +#define kLineBlueColor [UIColor colorWithRed:0.00 green:0.36 blue:1.00 alpha:1.00] | ||
| 181 | + | ||
| 182 | +#define kPublishRedColor [UIColor colorWithRed:0.88 green:0.10 blue:0.13 alpha:1.00] | ||
| 183 | + | ||
| 184 | +#define kPublishNaviRedColor [UIColor colorWithRed:1.00 green:0.17 blue:0.29 alpha:1.00] | ||
| 185 | + | ||
| 186 | +#define kNaviTintColor [UIColor colorWithRed:0.00 green:0.58 blue:1.00 alpha:1.00] | ||
| 187 | + | ||
| 188 | +#define kNaviThemeTintColor [UIColor colorWithRed:0.35 green:0.35 blue:0.35 alpha:1.00] | ||
| 189 | + | ||
| 190 | +#define kPublishSuccessColor [UIColor colorWithRed:0.21 green:1.00 blue:0.75 alpha:1.00] | ||
| 191 | + | ||
| 192 | +#define kPublishFailureColor [UIColor colorWithRed:1.00 green:0.00 blue:0.00 alpha:1.00] | ||
| 193 | + | ||
| 194 | +#define kMomentOrgangeColor [UIColor colorWithRed:0.98 green:0.55 blue:0.11 alpha:1.00] | ||
| 195 | + | ||
| 196 | +#define kMomentLineColor [UIColor colorWithRed:0.79 green:0.79 blue:0.79 alpha:1.00] | ||
| 197 | + | ||
| 198 | +///////正式 | ||
| 199 | +#define kBackGrayColor [UIColor colorWithRed:0.53 green:0.53 blue:0.53 alpha:1.00] | ||
| 200 | + | ||
| 201 | +#define kGuangbiaoColor [UIColor colorWithRed:0.20 green:0.35 blue:0.95 alpha:1.00] | ||
| 202 | + | ||
| 203 | +//////////////////////////////////////////////////////// | ||
| 204 | +//////////// firendCricle | ||
| 205 | +//////////////////////////////////////////////////////// | ||
| 206 | +#define contentLabelFontSize 16 | ||
| 207 | + | ||
| 208 | + | ||
| 209 | +//////////////////////////////////////////////////////// | ||
| 210 | +//////////// SystemVersion | ||
| 211 | +//////////////////////////////////////////////////////// | ||
| 212 | +#define iOS7Later ([UIDevice currentDevice].systemVersion.floatValue >= 7.0f) | ||
| 213 | +#define iOS8Later ([UIDevice currentDevice].systemVersion.floatValue >= 8.0f) | ||
| 214 | +#define iOS9Later ([UIDevice currentDevice].systemVersion.floatValue >= 9.0f) | ||
| 215 | +#define iOS9_1Later ([UIDevice currentDevice].systemVersion.floatValue >= 9.1f) | ||
| 216 | +#define iOS10Later ([UIDevice currentDevice].systemVersion.floatValue >= 10.0f) | ||
| 217 | + | ||
| 218 | + | ||
| 219 | +//////////////////////////////////////////////////////// | ||
| 220 | +//////////// WeakSelf&StrongSelf | ||
| 221 | +//////////////////////////////////////////////////////// | ||
| 222 | +/** | ||
| 223 | + * weakSelf | ||
| 224 | + */ | ||
| 225 | +#ifndef weakself | ||
| 226 | +#define weakself __weak typeof(self)weakSelf = self; | ||
| 227 | +#endif | ||
| 228 | +/** | ||
| 229 | + * strongSelf | ||
| 230 | + */ | ||
| 231 | +#ifndef strongself | ||
| 232 | +#define strongself __strong typeof(weakSelf)self = weakSelf; | ||
| 233 | +#endif | ||
| 234 | + | ||
| 235 | +//////////////////////////////////////////////////////// | ||
| 236 | +//////////// 输出测试 | ||
| 237 | +//////////////////////////////////////////////////////// | ||
| 238 | +#ifdef DEBUG | ||
| 239 | +#define NSLog(s, ... ) NSLog( @"[%@ in line %d] ===============>%@", [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] ) | ||
| 240 | +#else | ||
| 241 | +#define NSLog(s, ... ) | ||
| 242 | +#endif | ||
| 243 | + | ||
| 244 | +//////////////////////////////////////////////////////// | ||
| 245 | +//////////// NSUserDefault | ||
| 246 | +//////////////////////////////////////////////////////// | ||
| 247 | +#ifndef CNUserIsLogin | ||
| 248 | +#define CNUserIsLogin @"CNUserIsLogin" | ||
| 249 | +#endif | ||
| 250 | + | ||
| 251 | +#ifndef CNUserInfo | ||
| 252 | +#define CNUserInfo @"CNUserInfo" | ||
| 253 | +#endif | ||
| 254 | + | ||
| 255 | +#ifndef kCNLiveUserDefaultsTingYunUIDKey | ||
| 256 | +#define kCNLiveUserDefaultsTingYunUIDKey @"kCNLiveUserDefaultsTingYunUIDKey" | ||
| 257 | +#endif | ||
| 258 | + | ||
| 259 | +#ifndef CNGroupListInfo // 某群组成员列表list | ||
| 260 | +#define CNGroupListInfo @"CNGroupListInfo" | ||
| 261 | +#endif | ||
| 262 | + | ||
| 263 | +#ifndef CNGroupsList // 群组列表list | ||
| 264 | +#define CNGroupsList @"CNGroupsList" | ||
| 265 | +#endif | ||
| 266 | + | ||
| 267 | +#ifndef CNRefundReason | ||
| 268 | +#define CNRefundReason @"CNRefundReason" | ||
| 269 | +#endif | ||
| 270 | + | ||
| 271 | +//////////////////////////////////////////////////////// | ||
| 272 | +//////////// Refresh | ||
| 273 | +//////////////////////////////////////////////////////// | ||
| 274 | +#define TableViewDefaultOffestHeight 64.f | ||
| 275 | + | ||
| 276 | +//////////////////////////IM//////////////////////////////// | ||
| 277 | +#pragma mark - IM | ||
| 278 | + | ||
| 279 | +#define kSaftyWordsCode 80001 | ||
| 280 | + | ||
| 281 | +// 是否支持富文本显示 | ||
| 282 | +#define kTestChatAttachment 1 | ||
| 283 | + | ||
| 284 | +//////////////////////////////////////////////////////// | ||
| 285 | +//////////// 动态跟随系统设置的字体大小改变字体和行高 | ||
| 286 | +//////////////////////////////////////////////////////// | ||
| 287 | +#define RATIO [CNSetUserDefaultTool getScaleCoefficient] | ||
| 288 | + | ||
| 289 | + | ||
| 290 | +//字体大小 | ||
| 291 | +#define Text_FONT(F) [UIFont systemFontOfSize:F] | ||
| 292 | + | ||
| 293 | +#define SINGLETON_FOR_HEADER(className) \ | ||
| 294 | +\ | ||
| 295 | ++ (className *)shared##className; | ||
| 296 | + | ||
| 297 | +#define SINGLETON_FOR_CLASS(className) \ | ||
| 298 | +\ | ||
| 299 | ++ (className *)shared##className { \ | ||
| 300 | +static className *shared##className = nil; \ | ||
| 301 | +static dispatch_once_t onceToken; \ | ||
| 302 | +dispatch_once(&onceToken, ^{ \ | ||
| 303 | +shared##className = [[self alloc] init]; \ | ||
| 304 | +}); \ | ||
| 305 | +return shared##className; \ | ||
| 306 | +} | ||
| 307 | + | ||
| 308 | +#endif /* AppCommonDefine_h */ |
CNLiveImagePickerController/CNEditVideoController.h
0 → 100755
| 1 | +// | ||
| 2 | +// ZLEditVideoController.h | ||
| 3 | +// ZLPhotoBrowser | ||
| 4 | +// | ||
| 5 | +// Created by long on 2017/9/15. | ||
| 6 | +// Copyright © 2017年 long. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import <UIKit/UIKit.h> | ||
| 10 | + | ||
| 11 | +@class TZAssetModel; | ||
| 12 | + | ||
| 13 | +@interface CNEditVideoController : UIViewController | ||
| 14 | + | ||
| 15 | +@property (nonatomic, strong) TZAssetModel *model; | ||
| 16 | + | ||
| 17 | +@property (nonatomic, strong) NSMutableArray *models; ///< All photo models / 所有图片模型数组 | ||
| 18 | +@property (nonatomic, strong) NSMutableArray *photos; ///< All photos / 所有图片数组 | ||
| 19 | +@property (nonatomic, assign) NSInteger currentIndex; | ||
| 20 | +@property (nonatomic, assign) BOOL isCropImage; | ||
| 21 | + | ||
| 22 | +/// Return the new selected photos / 返回最新的选中图片数组 | ||
| 23 | +@property (nonatomic, copy) void (^backButtonEditClickBlock)(BOOL isSelectOriginalPhoto); | ||
| 24 | +@property (nonatomic, copy) void (^doneButtonEditClickBlock)(BOOL isSelectOriginalPhoto); | ||
| 25 | + | ||
| 26 | + | ||
| 27 | +@end |
CNLiveImagePickerController/CNEditVideoController.m
0 → 100755
| 1 | +// | ||
| 2 | +// ZLEditVideoController.m | ||
| 3 | +// ZLPhotoBrowser | ||
| 4 | +// | ||
| 5 | +// Created by long on 2017/9/15. | ||
| 6 | +// Copyright © 2017年 long. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import "CNEditVideoController.h" | ||
| 10 | +#import <AVFoundation/AVFoundation.h> | ||
| 11 | +#import "TZAssetModel.h" | ||
| 12 | +#import <objc/runtime.h> | ||
| 13 | +#import <AssetsLibrary/AssetsLibrary.h> | ||
| 14 | +#import "TZImagePickerController.h" | ||
| 15 | +#import "TZPhotoPreviewCell.h" | ||
| 16 | +#import "TZImageCropManager.h" | ||
| 17 | + | ||
| 18 | +#define kItemWidth kItemHeight * 2/3 | ||
| 19 | +#define kItemHeight 50 | ||
| 20 | +#define kAPPName @"网++" | ||
| 21 | + | ||
| 22 | +#pragma mark - 显示帧数的CNEditFrameView | ||
| 23 | + | ||
| 24 | +@interface CNEditVideoCell : UICollectionViewCell | ||
| 25 | + | ||
| 26 | +@property (nonatomic, strong) UIImageView *imageView; | ||
| 27 | + | ||
| 28 | +@end | ||
| 29 | + | ||
| 30 | +@implementation CNEditVideoCell | ||
| 31 | + | ||
| 32 | +- (UIImageView *)imageView | ||
| 33 | +{ | ||
| 34 | + if (!_imageView) { | ||
| 35 | + _imageView = [[UIImageView alloc] init]; | ||
| 36 | + _imageView.frame = self.bounds; | ||
| 37 | + _imageView.contentMode = UIViewContentModeScaleAspectFill; | ||
| 38 | + _imageView.clipsToBounds = YES; | ||
| 39 | + [self.contentView addSubview:_imageView]; | ||
| 40 | + } | ||
| 41 | + return _imageView; | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | +@end | ||
| 45 | + | ||
| 46 | +#pragma mark - 协议编辑帧数协议CNEditFrameViewDelegate | ||
| 47 | +@protocol CNEditFrameViewDelegate <NSObject> | ||
| 48 | + | ||
| 49 | +- (void)editViewValidRectChanged;//有效范围更变 | ||
| 50 | + | ||
| 51 | +- (void)editViewValidRectEndChanged;//有效范围更变结束 | ||
| 52 | + | ||
| 53 | +@end | ||
| 54 | + | ||
| 55 | +#pragma mark - 编辑时视图CNEditFrameView | ||
| 56 | +@interface CNEditFrameView : UIView | ||
| 57 | +{ | ||
| 58 | + UIImageView *_leftView;//左视图 | ||
| 59 | + UIImageView *_rightView;//有视图 | ||
| 60 | +} | ||
| 61 | + | ||
| 62 | +@property (nonatomic, assign) CGRect validRect; | ||
| 63 | +@property (nonatomic, weak) id<CNEditFrameViewDelegate> delegate; | ||
| 64 | + | ||
| 65 | +@end | ||
| 66 | + | ||
| 67 | +@implementation CNEditFrameView | ||
| 68 | + | ||
| 69 | +- (instancetype)init | ||
| 70 | +{ | ||
| 71 | + self = [super init]; | ||
| 72 | + if (self) { | ||
| 73 | + [self setupUI]; | ||
| 74 | + } | ||
| 75 | + return self; | ||
| 76 | +} | ||
| 77 | + | ||
| 78 | +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event | ||
| 79 | +{ | ||
| 80 | + //扩大下有效范围 | ||
| 81 | + CGRect left = _leftView.frame; | ||
| 82 | + left.origin.x -= kItemWidth/2; | ||
| 83 | + left.size.width += kItemWidth/2; | ||
| 84 | + CGRect right = _rightView.frame; | ||
| 85 | + right.size.width += kItemWidth/2; | ||
| 86 | + | ||
| 87 | + if (CGRectContainsPoint(left, point)) { | ||
| 88 | + return _leftView; | ||
| 89 | + } | ||
| 90 | + if (CGRectContainsPoint(right, point)) { | ||
| 91 | + return _rightView; | ||
| 92 | + } | ||
| 93 | + return nil; | ||
| 94 | +} | ||
| 95 | + | ||
| 96 | +- (void)setupUI | ||
| 97 | +{ | ||
| 98 | + self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:.5]; | ||
| 99 | + self.layer.borderWidth = 2; | ||
| 100 | + self.layer.borderColor = [UIColor clearColor].CGColor; | ||
| 101 | + | ||
| 102 | + _leftView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"video_edit_left"]]; | ||
| 103 | + _leftView.userInteractionEnabled = YES; | ||
| 104 | + _leftView.tag = 0; | ||
| 105 | + UIPanGestureRecognizer *lg = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)]; | ||
| 106 | + [_leftView addGestureRecognizer:lg]; | ||
| 107 | + [self addSubview:_leftView]; | ||
| 108 | + | ||
| 109 | + _rightView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"video_edit_right"]]; | ||
| 110 | + _rightView.userInteractionEnabled = YES; | ||
| 111 | + _rightView.tag = 1; | ||
| 112 | + UIPanGestureRecognizer *rg = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)]; | ||
| 113 | + [_rightView addGestureRecognizer:rg]; | ||
| 114 | + [self addSubview:_rightView]; | ||
| 115 | +} | ||
| 116 | + | ||
| 117 | +- (void)panAction:(UIGestureRecognizer *)pan //滑动收拾实现 | ||
| 118 | +{ | ||
| 119 | + self.layer.borderColor = [[UIColor whiteColor] colorWithAlphaComponent:.4].CGColor; | ||
| 120 | + CGPoint point = [pan locationInView:self]; | ||
| 121 | + | ||
| 122 | + CGRect rct = self.validRect; | ||
| 123 | + | ||
| 124 | + const CGFloat W = self.frame.size.width; | ||
| 125 | + CGFloat minX = 0; | ||
| 126 | + CGFloat maxX = W; | ||
| 127 | + | ||
| 128 | + switch (pan.view.tag) { | ||
| 129 | + case 0: { | ||
| 130 | + //left视图响应 | ||
| 131 | + maxX = rct.origin.x + rct.size.width - kItemWidth; | ||
| 132 | + | ||
| 133 | + point.x = MAX(minX, MIN(point.x, maxX)); | ||
| 134 | + point.y = 0; | ||
| 135 | + | ||
| 136 | + rct.size.width -= (point.x - rct.origin.x); | ||
| 137 | + rct.origin.x = point.x; | ||
| 138 | + } | ||
| 139 | + break; | ||
| 140 | + | ||
| 141 | + case 1: | ||
| 142 | + { | ||
| 143 | + //right视图响应 | ||
| 144 | + minX = rct.origin.x + kItemWidth/2; | ||
| 145 | + maxX = W - kItemWidth/2; | ||
| 146 | + | ||
| 147 | + point.x = MAX(minX, MIN(point.x, maxX)); | ||
| 148 | + point.y = 0; | ||
| 149 | + | ||
| 150 | + rct.size.width = (point.x - rct.origin.x + kItemWidth/2); | ||
| 151 | + } | ||
| 152 | + break; | ||
| 153 | + } | ||
| 154 | + | ||
| 155 | + switch (pan.state) { | ||
| 156 | + case UIGestureRecognizerStateBegan: | ||
| 157 | + case UIGestureRecognizerStateChanged: | ||
| 158 | + if (self.delegate && [self.delegate respondsToSelector:@selector(editViewValidRectChanged)]) { | ||
| 159 | + [self.delegate editViewValidRectChanged];//滑动响应实现 | ||
| 160 | + } | ||
| 161 | + break; | ||
| 162 | + | ||
| 163 | + case UIGestureRecognizerStateEnded: | ||
| 164 | + case UIGestureRecognizerStateCancelled: | ||
| 165 | + self.layer.borderColor = [UIColor clearColor].CGColor; | ||
| 166 | + if (self.delegate && [self.delegate respondsToSelector:@selector(editViewValidRectEndChanged)]) { | ||
| 167 | + [self.delegate editViewValidRectEndChanged];//滑动结束响应实现 | ||
| 168 | + } | ||
| 169 | + break; | ||
| 170 | + | ||
| 171 | + default: | ||
| 172 | + break; | ||
| 173 | + } | ||
| 174 | + | ||
| 175 | + | ||
| 176 | + self.validRect = rct; | ||
| 177 | +} | ||
| 178 | + | ||
| 179 | +- (void)setValidRect:(CGRect)validRect//根据范围 | ||
| 180 | +{ | ||
| 181 | + _validRect = validRect; | ||
| 182 | + _leftView.frame = CGRectMake(validRect.origin.x - kItemWidth/4, 0, kItemWidth/2, kItemHeight); | ||
| 183 | + _rightView.frame = CGRectMake(validRect.origin.x+validRect.size.width-kItemWidth/4, 0, kItemWidth/2, kItemHeight); | ||
| 184 | + | ||
| 185 | + [self setNeedsDisplay]; | ||
| 186 | +} | ||
| 187 | + | ||
| 188 | +- (void)drawRect:(CGRect)rect | ||
| 189 | +{ | ||
| 190 | + CGContextRef context = UIGraphicsGetCurrentContext(); | ||
| 191 | + | ||
| 192 | + CGContextClearRect(context, self.validRect); | ||
| 193 | + | ||
| 194 | + CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor); | ||
| 195 | + CGContextSetLineWidth(context, 4.0); | ||
| 196 | + | ||
| 197 | + CGPoint topPoints[2]; | ||
| 198 | + topPoints[0] = CGPointMake(self.validRect.origin.x, 0); | ||
| 199 | + topPoints[1] = CGPointMake(self.validRect.origin.x+self.validRect.size.width, 0); | ||
| 200 | + | ||
| 201 | + CGPoint bottomPoints[2]; | ||
| 202 | + bottomPoints[0] = CGPointMake(self.validRect.origin.x, kItemHeight); | ||
| 203 | + bottomPoints[1] = CGPointMake(self.validRect.origin.x+self.validRect.size.width, kItemHeight); | ||
| 204 | + | ||
| 205 | + CGContextAddLines(context, topPoints, 2); | ||
| 206 | + CGContextAddLines(context, bottomPoints, 2); | ||
| 207 | + | ||
| 208 | + CGContextDrawPath(context, kCGPathStroke); | ||
| 209 | +} | ||
| 210 | + | ||
| 211 | +@end | ||
| 212 | + | ||
| 213 | + | ||
| 214 | +#pragma mark - 编辑视频控制器CNEditVideoController | ||
| 215 | +@interface CNEditVideoController () <UIScrollViewDelegate, UICollectionViewDataSource, UICollectionViewDelegate, CNEditFrameViewDelegate> | ||
| 216 | +{ | ||
| 217 | + UIView *_bottomView; | ||
| 218 | + UIButton *_cancelBtn; | ||
| 219 | + UIButton *_doneBtn; | ||
| 220 | + | ||
| 221 | + NSTimer *_timer; | ||
| 222 | + | ||
| 223 | + //下方collectionview偏移量 | ||
| 224 | + CGFloat _offsetX; | ||
| 225 | + BOOL _orientationChanged; | ||
| 226 | + | ||
| 227 | + UIView *_indicatorLine; | ||
| 228 | + | ||
| 229 | + AVAsset *_avAsset; | ||
| 230 | + | ||
| 231 | + NSTimeInterval _interval; | ||
| 232 | + | ||
| 233 | + NSInteger _measureCount; | ||
| 234 | + NSOperationQueue *_queue; | ||
| 235 | + NSMutableDictionary<NSString *, UIImage *> *_imageCache; | ||
| 236 | + NSMutableDictionary<NSString *, NSBlockOperation *> *_opCache; | ||
| 237 | +} | ||
| 238 | + | ||
| 239 | +@property (nonatomic, strong) AVPlayerLayer *playerLayer; | ||
| 240 | +@property (nonatomic, strong) UICollectionView *collectionView; | ||
| 241 | +@property (nonatomic, strong) CNEditFrameView *editView; | ||
| 242 | +@property (nonatomic, strong) AVAssetImageGenerator *generator; | ||
| 243 | + | ||
| 244 | +@end | ||
| 245 | + | ||
| 246 | +@implementation CNEditVideoController | ||
| 247 | + | ||
| 248 | +- (void)dealloc | ||
| 249 | +{ | ||
| 250 | + [_queue cancelAllOperations]; | ||
| 251 | + [self stopTimer]; | ||
| 252 | + [[NSNotificationCenter defaultCenter] removeObserver:self]; | ||
| 253 | +// NSLog(@"---- %s", __FUNCTION__); | ||
| 254 | +} | ||
| 255 | + | ||
| 256 | +- (AVAssetImageGenerator *)generator | ||
| 257 | +{ | ||
| 258 | + if (!_generator) { | ||
| 259 | + _generator = [[AVAssetImageGenerator alloc] initWithAsset:_avAsset]; | ||
| 260 | + _generator.maximumSize = CGSizeMake(kItemWidth*4, kItemHeight*4); | ||
| 261 | + _generator.appliesPreferredTrackTransform = YES; | ||
| 262 | + _generator.requestedTimeToleranceBefore = kCMTimeZero; | ||
| 263 | + _generator.requestedTimeToleranceAfter = kCMTimeZero; | ||
| 264 | + _generator.apertureMode = AVAssetImageGeneratorApertureModeProductionAperture; | ||
| 265 | + } | ||
| 266 | + return _generator; | ||
| 267 | +} | ||
| 268 | + | ||
| 269 | +- (void)viewDidLoad { | ||
| 270 | + [super viewDidLoad]; | ||
| 271 | + [self setupUI]; | ||
| 272 | + [self analysisAssetImages]; | ||
| 273 | + | ||
| 274 | + _queue = [[NSOperationQueue alloc] init]; | ||
| 275 | + _queue.maxConcurrentOperationCount = 3; | ||
| 276 | + | ||
| 277 | + _imageCache = [NSMutableDictionary dictionary]; | ||
| 278 | + _opCache = [NSMutableDictionary dictionary]; | ||
| 279 | + | ||
| 280 | + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationChanged:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil]; | ||
| 281 | + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appResignActive) name:UIApplicationWillResignActiveNotification object:nil]; | ||
| 282 | + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil]; | ||
| 283 | +} | ||
| 284 | + | ||
| 285 | +- (void)viewWillAppear:(BOOL)animated | ||
| 286 | +{ | ||
| 287 | + [super viewWillAppear:animated]; | ||
| 288 | + [UIApplication sharedApplication].statusBarHidden = YES; | ||
| 289 | + self.navigationController.navigationBar.hidden = YES; | ||
| 290 | +} | ||
| 291 | + | ||
| 292 | +- (void)viewWillDisappear:(BOOL)animated | ||
| 293 | +{ | ||
| 294 | + [super viewWillDisappear:animated]; | ||
| 295 | + self.navigationController.navigationBar.hidden = NO; | ||
| 296 | +} | ||
| 297 | + | ||
| 298 | +- (void)viewDidLayoutSubviews | ||
| 299 | +{ | ||
| 300 | + [super viewDidLayoutSubviews]; | ||
| 301 | + | ||
| 302 | + UIEdgeInsets inset = UIEdgeInsetsZero; | ||
| 303 | + if (@available(iOS 11, *)) { | ||
| 304 | + inset = self.view.safeAreaInsets; | ||
| 305 | + } | ||
| 306 | + | ||
| 307 | + self.playerLayer.frame = CGRectMake(15, inset.top>0?inset.top:30, [UIScreen mainScreen].bounds.size.width-30, [UIScreen mainScreen].bounds.size.height-160-inset.bottom); | ||
| 308 | + | ||
| 309 | + self.editView.frame = CGRectMake(([UIScreen mainScreen].bounds.size.width-kItemWidth*10)/2, [UIScreen mainScreen].bounds.size.height-100-inset.bottom, kItemWidth*10, kItemHeight); | ||
| 310 | + self.editView.validRect = self.editView.bounds; | ||
| 311 | + self.collectionView.frame = CGRectMake(inset.left, [UIScreen mainScreen].bounds.size.height-100-inset.bottom, [UIScreen mainScreen].bounds.size.width-inset.left-inset.right, kItemHeight); | ||
| 312 | + | ||
| 313 | + CGFloat leftOffset = (([UIScreen mainScreen].bounds.size.width-kItemWidth*10)/2-inset.left); | ||
| 314 | + CGFloat rightOffset = (([UIScreen mainScreen].bounds.size.width-kItemWidth*10)/2-inset.right); | ||
| 315 | + [self.collectionView setContentInset:UIEdgeInsetsMake(0, leftOffset, 0, rightOffset)]; | ||
| 316 | + [self.collectionView setContentOffset:CGPointMake(_offsetX-leftOffset, 0)]; | ||
| 317 | + | ||
| 318 | + CGFloat bottomViewH = 44; | ||
| 319 | + CGFloat bottomBtnH = 30; | ||
| 320 | + _bottomView.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height-bottomViewH-inset.bottom, [UIScreen mainScreen].bounds.size.width, kItemHeight); | ||
| 321 | + _cancelBtn.frame = CGRectMake(10+inset.left, 7, 60, bottomBtnH); | ||
| 322 | + _doneBtn.frame = CGRectMake([UIScreen mainScreen].bounds.size.width-70-inset.right, 7, 60, bottomBtnH); | ||
| 323 | + | ||
| 324 | + [self.playerLayer setBackgroundColor:[UIColor clearColor].CGColor]; | ||
| 325 | +} | ||
| 326 | + | ||
| 327 | +#pragma mark - notifies | ||
| 328 | +//设备旋转 | ||
| 329 | +- (void)deviceOrientationChanged:(NSNotification *)notify | ||
| 330 | +{ | ||
| 331 | + _offsetX = self.collectionView.contentOffset.x + self.collectionView.contentInset.left; | ||
| 332 | + _orientationChanged = YES; | ||
| 333 | +} | ||
| 334 | + | ||
| 335 | +- (void)appResignActive | ||
| 336 | +{ | ||
| 337 | + [self stopTimer]; | ||
| 338 | +} | ||
| 339 | + | ||
| 340 | +- (void)appBecomeActive | ||
| 341 | +{ | ||
| 342 | + [self startTimer]; | ||
| 343 | +} | ||
| 344 | + | ||
| 345 | +- (void)setupUI | ||
| 346 | +{ | ||
| 347 | + //禁用返回手势 | ||
| 348 | + if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { | ||
| 349 | + self.navigationController.interactivePopGestureRecognizer.delegate = nil; | ||
| 350 | + self.navigationController.interactivePopGestureRecognizer.enabled = NO; | ||
| 351 | + } | ||
| 352 | + | ||
| 353 | + self.view.backgroundColor = [UIColor blackColor]; | ||
| 354 | + | ||
| 355 | + self.playerLayer = [[AVPlayerLayer alloc] init]; | ||
| 356 | + [self.view.layer addSublayer:self.playerLayer]; | ||
| 357 | + | ||
| 358 | + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; | ||
| 359 | + layout.itemSize = CGSizeMake(kItemWidth, kItemHeight); | ||
| 360 | + layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; | ||
| 361 | + layout.minimumInteritemSpacing = 0; | ||
| 362 | + layout.minimumLineSpacing = 0; | ||
| 363 | + | ||
| 364 | + self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; | ||
| 365 | + self.collectionView.delegate = self; | ||
| 366 | + self.collectionView.dataSource = self; | ||
| 367 | + self.collectionView.backgroundColor = [UIColor clearColor]; | ||
| 368 | + self.collectionView.showsHorizontalScrollIndicator = NO; | ||
| 369 | + [self.collectionView registerClass:CNEditVideoCell.class forCellWithReuseIdentifier:@"CNEditVideoCell"]; | ||
| 370 | + | ||
| 371 | + [self.view addSubview:self.collectionView]; | ||
| 372 | + | ||
| 373 | + [self creatBottomView]; | ||
| 374 | + | ||
| 375 | + self.editView = [[CNEditFrameView alloc] init]; | ||
| 376 | + self.editView.delegate = self; | ||
| 377 | + [self.view addSubview:self.editView]; | ||
| 378 | + | ||
| 379 | + _indicatorLine = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 2, kItemHeight)]; | ||
| 380 | +// _indicatorLine.backgroundColor = [RGBOF(0x0BBE06) colorWithAlphaComponent:.7]; | ||
| 381 | +} | ||
| 382 | + | ||
| 383 | +- (void)creatBottomView | ||
| 384 | +{ | ||
| 385 | + //下方视图 | ||
| 386 | + _bottomView = [[UIView alloc] init]; | ||
| 387 | + _bottomView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:.7]; | ||
| 388 | + [self.view addSubview:_bottomView]; | ||
| 389 | + | ||
| 390 | + _cancelBtn = [UIButton buttonWithType:UIButtonTypeCustom]; | ||
| 391 | + _cancelBtn.titleLabel.font = [UIFont systemFontOfSize:15]; | ||
| 392 | + [_cancelBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; | ||
| 393 | + [_cancelBtn setTitle:@"取消" forState:UIControlStateNormal]; | ||
| 394 | + [_cancelBtn addTarget:self action:@selector(cancelBtn_click) forControlEvents:UIControlEventTouchUpInside]; | ||
| 395 | + [_bottomView addSubview:_cancelBtn]; | ||
| 396 | + | ||
| 397 | + _doneBtn = [UIButton buttonWithType:UIButtonTypeCustom]; | ||
| 398 | + [_doneBtn setTitle:@"完成" forState:UIControlStateNormal]; | ||
| 399 | + [_doneBtn setBackgroundColor:[UIColor clearColor]]; | ||
| 400 | +// [_doneBtn setTitleColor:RGBOF(0x0BBE06) forState:UIControlStateNormal]; | ||
| 401 | + _doneBtn.titleLabel.font = [UIFont systemFontOfSize:15]; | ||
| 402 | + _doneBtn.layer.masksToBounds = YES; | ||
| 403 | + _doneBtn.layer.cornerRadius = 3.0f; | ||
| 404 | + [_doneBtn addTarget:self action:@selector(btnDone_click) forControlEvents:UIControlEventTouchUpInside]; | ||
| 405 | + [_bottomView addSubview:_doneBtn]; | ||
| 406 | +} | ||
| 407 | + | ||
| 408 | +#pragma mark - 解析视频每一帧图片 | ||
| 409 | +- (void)analysisAssetImages | ||
| 410 | +{ | ||
| 411 | + NSTimeInterval duration = 0.0; | ||
| 412 | + if ([self.model.asset isKindOfClass:[PHAsset class]]) { | ||
| 413 | + PHAsset *asset = self.model.asset; | ||
| 414 | + duration = asset.duration; | ||
| 415 | + }else if([self.model.asset isKindOfClass:[ALAsset class]]){ | ||
| 416 | + duration = [[self.model.asset valueForProperty:ALAssetPropertyDuration] doubleValue]; | ||
| 417 | + } | ||
| 418 | + | ||
| 419 | + TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController; | ||
| 420 | + _interval = _tzImagePickerVc.maxEditVideoTime/10.0; | ||
| 421 | + _measureCount = (NSInteger)(duration / _interval); | ||
| 422 | + ///播放视频的 | ||
| 423 | + if ([self.model.asset isKindOfClass:[PHAsset class]]) {//PHAsset | ||
| 424 | + __weak typeof(self) weakSelf = self; | ||
| 425 | + [[PHCachingImageManager defaultManager] requestPlayerItemForVideo:self.model.asset options:nil resultHandler:^(AVPlayerItem * _Nullable playerItem, NSDictionary * _Nullable info) { | ||
| 426 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 427 | + __strong typeof(weakSelf) strongSelf = weakSelf; | ||
| 428 | + if (!playerItem) return; | ||
| 429 | + AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem]; | ||
| 430 | + strongSelf.playerLayer.player = player; | ||
| 431 | + [strongSelf startTimer]; | ||
| 432 | + }); | ||
| 433 | + | ||
| 434 | + }]; | ||
| 435 | + | ||
| 436 | + ///截取视频帧数 | ||
| 437 | + PHVideoRequestOptions* options = [[PHVideoRequestOptions alloc] init]; | ||
| 438 | + options.version = PHVideoRequestOptionsVersionOriginal; | ||
| 439 | + options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic; | ||
| 440 | + options.networkAccessAllowed = YES; | ||
| 441 | + [[PHImageManager defaultManager] requestAVAssetForVideo:self.model.asset options:options resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) { | ||
| 442 | + __strong typeof(weakSelf) strongSelf = weakSelf; | ||
| 443 | + strongSelf->_avAsset = asset; | ||
| 444 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 445 | + [strongSelf.collectionView reloadData]; | ||
| 446 | + }); | ||
| 447 | + }]; | ||
| 448 | + | ||
| 449 | + }else if([self.model.asset isKindOfClass:[ALAsset class]]){//ALAsset | ||
| 450 | + ///播放视频的 | ||
| 451 | + ALAsset *alAsset = (ALAsset *)self.model.asset; | ||
| 452 | + ALAssetRepresentation *defaultRepresentation = [alAsset defaultRepresentation]; | ||
| 453 | + NSString *uti = [defaultRepresentation UTI]; | ||
| 454 | + NSURL *videoURL = [[alAsset valueForProperty:ALAssetPropertyURLs] valueForKey:uti]; | ||
| 455 | + AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:videoURL]; | ||
| 456 | + AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem]; | ||
| 457 | + self.playerLayer.player = player; | ||
| 458 | + [self startTimer]; | ||
| 459 | + | ||
| 460 | + ///截取视频帧数 | ||
| 461 | + NSURL *videoURL_al =[self.model.asset valueForProperty:ALAssetPropertyAssetURL]; // ALAssetPropertyURLs | ||
| 462 | + AVAsset *videoAsset = [AVURLAsset assetWithURL:videoURL]; | ||
| 463 | + _avAsset = videoAsset ; | ||
| 464 | + [self.collectionView reloadData]; | ||
| 465 | + | ||
| 466 | + } | ||
| 467 | + | ||
| 468 | +} | ||
| 469 | + | ||
| 470 | +#pragma mark - action | ||
| 471 | +- (void)cancelBtn_click | ||
| 472 | +{ | ||
| 473 | + [self stopTimer]; | ||
| 474 | + | ||
| 475 | + if (self.backButtonEditClickBlock) { | ||
| 476 | + TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController; | ||
| 477 | + self.backButtonEditClickBlock(_tzImagePickerVc.isSelectOriginalPhoto); | ||
| 478 | + } | ||
| 479 | + UIViewController *vc = [self.navigationController popViewControllerAnimated:NO]; | ||
| 480 | + if (!vc) { | ||
| 481 | + [self dismissViewControllerAnimated:YES completion:nil]; | ||
| 482 | + } | ||
| 483 | +} | ||
| 484 | + | ||
| 485 | +- (void)btnDone_click | ||
| 486 | +{ | ||
| 487 | + [self stopTimer]; | ||
| 488 | + | ||
| 489 | + TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController; | ||
| 490 | + [_tzImagePickerVc showProgressHUD]; | ||
| 491 | + | ||
| 492 | + [self exportEditVideoForAsset:_avAsset range:[self getTimeRange] complete:^(BOOL isSuc, id asset) { | ||
| 493 | + [_tzImagePickerVc hideProgressHUD]; | ||
| 494 | + if (isSuc) { | ||
| 495 | + TZAssetModel *editModel = [TZAssetModel modelWithAsset:asset type:TZAssetModelMediaTypeVideo]; | ||
| 496 | + | ||
| 497 | + // 如果没有选中过照片 点击确定时选中当前预览的照片 | ||
| 498 | + if (_tzImagePickerVc.selectedModels.count == 0 && _tzImagePickerVc.minImagesCount <= 0) { | ||
| 499 | + [_tzImagePickerVc addSelectedModel:editModel]; | ||
| 500 | + } | ||
| 501 | + | ||
| 502 | + if (self.doneButtonEditClickBlock) { | ||
| 503 | + self.doneButtonEditClickBlock(_tzImagePickerVc.isSelectOriginalPhoto); | ||
| 504 | + } | ||
| 505 | + [_tzImagePickerVc hideProgressHUD]; | ||
| 506 | + }else{ | ||
| 507 | + [_tzImagePickerVc showAlertWithTitle:@"编辑失败"]; | ||
| 508 | + } | ||
| 509 | + | ||
| 510 | + }]; | ||
| 511 | +} | ||
| 512 | +#pragma mark - 截取视频 | ||
| 513 | + | ||
| 514 | +- (void)exportEditVideoForAsset:(AVAsset *)asset range:(CMTimeRange)range complete:(void (^)(BOOL, id))complete | ||
| 515 | +{ | ||
| 516 | + [self export:asset range:range presetName:AVAssetExportPresetPassthrough renderSize:CGSizeZero imageSize:CGSizeZero effectImage:nil birthRate:0 velocity:0 complete:^(NSString *exportFilePath, NSError *error) { | ||
| 517 | + if (!error) { | ||
| 518 | + [self saveVideoToAblum:[NSURL fileURLWithPath:exportFilePath] completion:^(BOOL isSuc, id asset) { | ||
| 519 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 520 | + if (complete) complete(isSuc, asset); | ||
| 521 | + }); | ||
| 522 | + }]; | ||
| 523 | + } else { | ||
| 524 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 525 | + if (complete) complete(NO, nil); | ||
| 526 | + }); | ||
| 527 | + } | ||
| 528 | + }]; | ||
| 529 | +} | ||
| 530 | + | ||
| 531 | +///保存视频到相册 | ||
| 532 | +- (void)saveVideoToAblum:(NSURL *)url completion:(void (^)(BOOL, id))completion | ||
| 533 | +{ | ||
| 534 | + if (iOS8Later) { | ||
| 535 | + PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus]; | ||
| 536 | + if (status == PHAuthorizationStatusDenied) { | ||
| 537 | + if (completion) completion(NO, nil); | ||
| 538 | + } else if (status == PHAuthorizationStatusRestricted) { | ||
| 539 | + if (completion) completion(NO, nil); | ||
| 540 | + } else { | ||
| 541 | + __block PHObjectPlaceholder *placeholderAsset=nil; | ||
| 542 | + [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ | ||
| 543 | + PHAssetChangeRequest *newAssetRequest = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:url]; | ||
| 544 | + placeholderAsset = newAssetRequest.placeholderForCreatedAsset; | ||
| 545 | + } completionHandler:^(BOOL success, NSError * _Nullable error) { | ||
| 546 | + if (!success) { | ||
| 547 | + if (completion) completion(NO, nil); | ||
| 548 | + return; | ||
| 549 | + } | ||
| 550 | + PHAsset *asset = [self getAssetFromlocalIdentifier:placeholderAsset.localIdentifier]; | ||
| 551 | + PHAssetCollection *desCollection = [self getDestinationCollection]; | ||
| 552 | + if (!desCollection) completion(NO, nil); | ||
| 553 | + | ||
| 554 | + [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ | ||
| 555 | + [[PHAssetCollectionChangeRequest changeRequestForAssetCollection:desCollection] addAssets:@[asset]]; | ||
| 556 | + } completionHandler:^(BOOL success, NSError * _Nullable error) { | ||
| 557 | + if (completion) completion(success, asset); | ||
| 558 | + }]; | ||
| 559 | + }]; | ||
| 560 | + } | ||
| 561 | + }else{ | ||
| 562 | + | ||
| 563 | + ALAssetsLibrary *assetLibrary = [[ALAssetsLibrary alloc] init]; | ||
| 564 | + [assetLibrary writeVideoAtPathToSavedPhotosAlbum:url completionBlock:^(NSURL *assetURL, NSError *error) { | ||
| 565 | + if (error) { | ||
| 566 | + NSLog(@"保存视频出错:%@",error.localizedDescription); | ||
| 567 | + if (completion) { | ||
| 568 | + completion(NO,nil); | ||
| 569 | + } | ||
| 570 | + } else { | ||
| 571 | + [assetLibrary assetForURL:assetURL resultBlock:^(ALAsset *asset) { | ||
| 572 | + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | ||
| 573 | + if (completion) { | ||
| 574 | + completion(YES,asset); | ||
| 575 | + } | ||
| 576 | + }); | ||
| 577 | + } failureBlock:^(NSError *error) { | ||
| 578 | + | ||
| 579 | + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | ||
| 580 | + if (completion) { | ||
| 581 | + completion(NO,nil); | ||
| 582 | + } | ||
| 583 | + }); | ||
| 584 | + | ||
| 585 | + }]; | ||
| 586 | + } | ||
| 587 | + }]; | ||
| 588 | + | ||
| 589 | + } | ||
| 590 | + | ||
| 591 | +} | ||
| 592 | + | ||
| 593 | +- (PHAsset *)getAssetFromlocalIdentifier:(NSString *)localIdentifier{ | ||
| 594 | + if(localIdentifier == nil){ | ||
| 595 | + NSLog(@"Cannot get asset from localID because it is nil"); | ||
| 596 | + return nil; | ||
| 597 | + } | ||
| 598 | + PHFetchResult *result = [PHAsset fetchAssetsWithLocalIdentifiers:@[localIdentifier] options:nil]; | ||
| 599 | + if(result.count){ | ||
| 600 | + return result[0]; | ||
| 601 | + } | ||
| 602 | + return nil; | ||
| 603 | +} | ||
| 604 | + | ||
| 605 | +//获取自定义相册 | ||
| 606 | +- (PHAssetCollection *)getDestinationCollection | ||
| 607 | +{ | ||
| 608 | + //找是否已经创建自定义相册 | ||
| 609 | + PHFetchResult<PHAssetCollection *> *collectionResult = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil]; | ||
| 610 | + for (PHAssetCollection *collection in collectionResult) { | ||
| 611 | + if ([collection.localizedTitle isEqualToString:kAPPName]) { | ||
| 612 | + return collection; | ||
| 613 | + } | ||
| 614 | + } | ||
| 615 | + //新建自定义相册 | ||
| 616 | + __block NSString *collectionId = nil; | ||
| 617 | + NSError *error = nil; | ||
| 618 | + [[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{ | ||
| 619 | + collectionId = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:kAPPName].placeholderForCreatedAssetCollection.localIdentifier; | ||
| 620 | + } error:&error]; | ||
| 621 | + if (error) { | ||
| 622 | + return nil; | ||
| 623 | + } | ||
| 624 | + return [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[collectionId] options:nil].lastObject; | ||
| 625 | +} | ||
| 626 | + | ||
| 627 | +- (void)export:(AVAsset *)asset range:(CMTimeRange)range presetName:(NSString *)presetName renderSize:(CGSize)renderSize imageSize:(CGSize)imageSize effectImage:(UIImage *)effectImage birthRate:(NSInteger)birthRate velocity:(CGFloat)velocity complete:(void (^)(NSString *exportFilePath, NSError *error))complete | ||
| 628 | +{ | ||
| 629 | + NSString *exportFilePath = [self getVideoExportFilePath]; | ||
| 630 | + | ||
| 631 | + AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:asset presetName:presetName]; | ||
| 632 | + | ||
| 633 | + NSURL *exportFileUrl = [NSURL fileURLWithPath:exportFilePath]; | ||
| 634 | + | ||
| 635 | + exportSession.outputURL = exportFileUrl; | ||
| 636 | + exportSession.outputFileType = AVFileTypeMPEG4; | ||
| 637 | + exportSession.timeRange = range; | ||
| 638 | + // exportSession.shouldOptimizeForNetworkUse = YES; | ||
| 639 | + if (!CGSizeEqualToSize(renderSize, CGSizeZero)) { | ||
| 640 | + AVMutableVideoComposition *com = [self getVideoComposition:asset renderSize:renderSize imageSize:imageSize effectImage:effectImage birthRate:birthRate velocity:velocity]; | ||
| 641 | + if (!com) { | ||
| 642 | + if (complete) { | ||
| 643 | + complete(nil, [NSError errorWithDomain:@"视频裁剪导出失败" code:-1 userInfo:@{@"message": @"视频对象格式可能有错误,没有检测到视频通道"}]); | ||
| 644 | + } | ||
| 645 | + return; | ||
| 646 | + } | ||
| 647 | + exportSession.videoComposition = com; | ||
| 648 | + } | ||
| 649 | + | ||
| 650 | + [exportSession exportAsynchronouslyWithCompletionHandler:^{ | ||
| 651 | + BOOL suc = NO; | ||
| 652 | + switch ([exportSession status]) { | ||
| 653 | + case AVAssetExportSessionStatusFailed: | ||
| 654 | + NSLog(@"Export failed: %@", [[exportSession error] localizedDescription]); | ||
| 655 | + break; | ||
| 656 | + case AVAssetExportSessionStatusCancelled: | ||
| 657 | + NSLog(@"Export canceled"); | ||
| 658 | + break; | ||
| 659 | + | ||
| 660 | + case AVAssetExportSessionStatusCompleted:{ | ||
| 661 | + NSLog(@"Export completed"); | ||
| 662 | + suc = YES; | ||
| 663 | + } | ||
| 664 | + break; | ||
| 665 | + | ||
| 666 | + default: | ||
| 667 | + NSLog(@"Export other"); | ||
| 668 | + break; | ||
| 669 | + } | ||
| 670 | + | ||
| 671 | + if (complete) { | ||
| 672 | + complete(suc?exportFilePath:nil, suc?nil:exportSession.error); | ||
| 673 | + if (!suc) { | ||
| 674 | + [exportSession cancelExport]; | ||
| 675 | + } | ||
| 676 | + } | ||
| 677 | + }]; | ||
| 678 | +} | ||
| 679 | + | ||
| 680 | +- (NSString *)getVideoExportFilePath | ||
| 681 | +{ | ||
| 682 | + NSString *format = @"mp4"; | ||
| 683 | + | ||
| 684 | + NSString *exportFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", [self getUniqueStrByUUID], format]]; | ||
| 685 | + | ||
| 686 | + return exportFilePath; | ||
| 687 | +} | ||
| 688 | + | ||
| 689 | +- (NSString *)getUniqueStrByUUID | ||
| 690 | +{ | ||
| 691 | + CFUUIDRef uuidObj = CFUUIDCreate(nil);//create a new UUID | ||
| 692 | + | ||
| 693 | + //get the string representation of the UUID | ||
| 694 | + CFStringRef uuidString = CFUUIDCreateString(nil, uuidObj); | ||
| 695 | + | ||
| 696 | + NSString *str = [NSString stringWithString:(__bridge NSString *)uuidString]; | ||
| 697 | + | ||
| 698 | + CFRelease(uuidObj); | ||
| 699 | + CFRelease(uuidString); | ||
| 700 | + | ||
| 701 | + return [str lowercaseString]; | ||
| 702 | +} | ||
| 703 | + | ||
| 704 | + | ||
| 705 | +- (AVMutableVideoComposition *)getVideoComposition:(AVAsset *)asset renderSize:(CGSize)renderSize imageSize:(CGSize)imageSize effectImage:(UIImage *)effectImage birthRate:(NSInteger)birthRate velocity:(CGFloat)velocity | ||
| 706 | +{ | ||
| 707 | + AVMutableComposition *composition = [AVMutableComposition composition]; | ||
| 708 | + //视频通道 | ||
| 709 | + AVMutableCompositionTrack *assetVideoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; | ||
| 710 | + //音频通道 | ||
| 711 | + AVMutableCompositionTrack *assetAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid]; | ||
| 712 | + | ||
| 713 | + NSError *error = nil; | ||
| 714 | + //裁剪时长 | ||
| 715 | + CMTimeRange timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration); | ||
| 716 | + | ||
| 717 | + AVAssetTrack *videoTrack = [asset tracksWithMediaType:AVMediaTypeVideo].firstObject;//视频 | ||
| 718 | + AVAssetTrack *audioTrack = [asset tracksWithMediaType:AVMediaTypeAudio].firstObject;//音频 | ||
| 719 | + | ||
| 720 | + if (!videoTrack) { | ||
| 721 | + return nil; | ||
| 722 | + } | ||
| 723 | + [assetVideoTrack insertTimeRange:timeRange ofTrack:videoTrack atTime:kCMTimeZero error:&error]; | ||
| 724 | + NSLog(@"%@", error); | ||
| 725 | + if (audioTrack) { | ||
| 726 | + [assetAudioTrack insertTimeRange:timeRange ofTrack:audioTrack atTime:kCMTimeZero error:&error]; | ||
| 727 | + NSLog(@"%@", error); | ||
| 728 | + } | ||
| 729 | + | ||
| 730 | + if (error) { | ||
| 731 | + return nil; | ||
| 732 | + } | ||
| 733 | + | ||
| 734 | + AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; | ||
| 735 | + instruction.timeRange = CMTimeRangeMake(kCMTimeZero, composition.duration); | ||
| 736 | + | ||
| 737 | + //处理视频旋转 | ||
| 738 | + AVMutableVideoCompositionLayerInstruction *layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:assetVideoTrack]; | ||
| 739 | + [layerInstruction setOpacity:0.0 atTime:assetVideoTrack.timeRange.duration]; | ||
| 740 | + //视频旋转,获取视频旋转角度,然后旋转对应角度,保持视频方向正确 | ||
| 741 | + CGFloat degree = [self getVideoDegree:videoTrack]; | ||
| 742 | + CGSize naturalSize = assetVideoTrack.naturalSize; | ||
| 743 | + | ||
| 744 | + CGAffineTransform mixedTransform = CGAffineTransformIdentity; | ||
| 745 | + //处理renderSize,不能大于视频宽高 | ||
| 746 | + CGFloat videoWidth = (degree==0 || degree==M_PI) ? naturalSize.width : naturalSize.height; | ||
| 747 | + CGFloat videoHeight = (degree==0 || degree==M_PI) ? naturalSize.height : naturalSize.width; | ||
| 748 | + CGSize cropSize = CGSizeMake(MIN(videoWidth, renderSize.width), MIN(videoHeight, renderSize.height)); | ||
| 749 | + CGFloat x, y; | ||
| 750 | + if (degree == M_PI_2) { | ||
| 751 | + //顺时针 90° | ||
| 752 | + CGAffineTransform t = CGAffineTransformMakeTranslation(naturalSize.height,.0); | ||
| 753 | + CGAffineTransform t1 = CGAffineTransformRotate(t, M_PI_2); | ||
| 754 | + //x为正向下 y为正向左 | ||
| 755 | + x = -(videoHeight-cropSize.height)/2; | ||
| 756 | + y = (videoWidth-cropSize.width)/2; | ||
| 757 | + mixedTransform = CGAffineTransformTranslate(t1, x, y); | ||
| 758 | + } else if (degree == M_PI) { | ||
| 759 | + //顺时针 180° | ||
| 760 | + CGAffineTransform t = CGAffineTransformMakeTranslation(naturalSize.width, naturalSize.height); | ||
| 761 | + CGAffineTransform t1 = CGAffineTransformRotate(t, M_PI); | ||
| 762 | + //x为正向左 y为正向上 | ||
| 763 | + x = (videoWidth-cropSize.width)/2; | ||
| 764 | + y = (videoHeight-cropSize.height)/2; | ||
| 765 | + mixedTransform = CGAffineTransformTranslate(t1, x, y); | ||
| 766 | + } else if (degree == (M_PI_2 * 3)) { | ||
| 767 | + //顺时针 270° | ||
| 768 | + CGAffineTransform t = CGAffineTransformMakeTranslation(.0, naturalSize.width); | ||
| 769 | + CGAffineTransform t1 = CGAffineTransformRotate(t, M_PI_2*3); | ||
| 770 | + //x为正向上 y为正向右 | ||
| 771 | + x = (videoHeight-cropSize.height)/2; | ||
| 772 | + y = -(videoWidth-cropSize.width)/2; | ||
| 773 | + mixedTransform = CGAffineTransformTranslate(t1, x, y); | ||
| 774 | + } else { | ||
| 775 | + //x为正向右 y为正向下 | ||
| 776 | + x = -(videoWidth-cropSize.width)/2; | ||
| 777 | + y = -(videoHeight-cropSize.height)/2; | ||
| 778 | + mixedTransform = CGAffineTransformMakeTranslation(x, y); | ||
| 779 | + } | ||
| 780 | + | ||
| 781 | + [layerInstruction setTransform:mixedTransform atTime:kCMTimeZero]; | ||
| 782 | + | ||
| 783 | + //管理所有需要处理的视频 | ||
| 784 | + AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition]; | ||
| 785 | + videoComposition.frameDuration = CMTimeMake(1, 30); | ||
| 786 | + videoComposition.renderScale = 1; | ||
| 787 | + videoComposition.renderSize = cropSize; | ||
| 788 | + | ||
| 789 | + instruction.layerInstructions = @[layerInstruction]; | ||
| 790 | + videoComposition.instructions = @[instruction]; | ||
| 791 | + | ||
| 792 | + return videoComposition; | ||
| 793 | +} | ||
| 794 | + | ||
| 795 | +- (CGFloat)getVideoDegree:(AVAssetTrack *)videoTrack | ||
| 796 | +{ | ||
| 797 | + CGAffineTransform tf = videoTrack.preferredTransform; | ||
| 798 | + | ||
| 799 | + CGFloat degree = 0; | ||
| 800 | + if (tf.b == 1.0 && tf.c == -1.0) { | ||
| 801 | + degree = M_PI_2; | ||
| 802 | + } else if (tf.a == -1.0 && tf.d == -1.0) { | ||
| 803 | + degree = M_PI; | ||
| 804 | + } else if (tf.b == -1.0 && tf.c == 1.0) { | ||
| 805 | + degree = M_PI_2 * 3; | ||
| 806 | + } | ||
| 807 | + return degree; | ||
| 808 | +} | ||
| 809 | + | ||
| 810 | + | ||
| 811 | + | ||
| 812 | +#pragma mark - timer | ||
| 813 | +- (void)startTimer | ||
| 814 | +{ | ||
| 815 | + [self stopTimer]; | ||
| 816 | + | ||
| 817 | + CGFloat duration = _interval * self.editView.validRect.size.width / (kItemWidth); | ||
| 818 | + _timer = [NSTimer scheduledTimerWithTimeInterval:duration target:self selector:@selector(playPartVideo:) userInfo:nil repeats:YES]; | ||
| 819 | + [_timer fire]; | ||
| 820 | + [[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes]; | ||
| 821 | + | ||
| 822 | + _indicatorLine.frame = CGRectMake(self.editView.validRect.origin.x, 0, 4, kItemHeight); | ||
| 823 | + [self.editView addSubview:_indicatorLine]; | ||
| 824 | + [UIView animateWithDuration:duration delay:.0 options:UIViewAnimationOptionRepeat|UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionCurveLinear animations:^{ | ||
| 825 | + _indicatorLine.frame = CGRectMake(CGRectGetMaxX(self.editView.validRect)-2, 0, 2, kItemHeight); | ||
| 826 | + } completion:nil]; | ||
| 827 | +} | ||
| 828 | + | ||
| 829 | +- (void)stopTimer | ||
| 830 | +{ | ||
| 831 | + [_timer invalidate]; | ||
| 832 | + _timer = nil; | ||
| 833 | + [_indicatorLine removeFromSuperview]; | ||
| 834 | + [self.playerLayer.player pause]; | ||
| 835 | +} | ||
| 836 | + | ||
| 837 | +- (CMTime)getStartTime | ||
| 838 | +{ | ||
| 839 | + CGRect rect = [self.collectionView convertRect:self.editView.validRect fromView:self.editView]; | ||
| 840 | + CGFloat s = MAX(0, _interval * rect.origin.x / (kItemWidth)); | ||
| 841 | + return CMTimeMakeWithSeconds(s, self.playerLayer.player.currentTime.timescale); | ||
| 842 | +} | ||
| 843 | + | ||
| 844 | +- (CMTimeRange)getTimeRange | ||
| 845 | +{ | ||
| 846 | + CMTime start = [self getStartTime]; | ||
| 847 | + CGFloat d = _interval * self.editView.validRect.size.width / (kItemWidth); | ||
| 848 | + CMTime duration = CMTimeMakeWithSeconds(d, self.playerLayer.player.currentTime.timescale); | ||
| 849 | + return CMTimeRangeMake(start, duration); | ||
| 850 | +} | ||
| 851 | + | ||
| 852 | +- (void)playPartVideo:(NSTimer *)timer | ||
| 853 | +{ | ||
| 854 | + [self.playerLayer.player play]; | ||
| 855 | + [self.playerLayer.player seekToTime:[self getStartTime] toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero]; | ||
| 856 | +} | ||
| 857 | + | ||
| 858 | +#pragma mark - edit view delegate | ||
| 859 | +- (void)editViewValidRectChanged | ||
| 860 | +{ | ||
| 861 | + [self stopTimer]; | ||
| 862 | + [self.playerLayer.player seekToTime:[self getStartTime] toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero]; | ||
| 863 | +} | ||
| 864 | + | ||
| 865 | +- (void)editViewValidRectEndChanged | ||
| 866 | +{ | ||
| 867 | + [self startTimer]; | ||
| 868 | +} | ||
| 869 | + | ||
| 870 | +#pragma mark - scroll view delegate | ||
| 871 | +- (void)scrollViewDidScroll:(UIScrollView *)scrollView | ||
| 872 | +{ | ||
| 873 | + if (!self.playerLayer.player || _orientationChanged) { | ||
| 874 | + _orientationChanged = NO; | ||
| 875 | + return; | ||
| 876 | + } | ||
| 877 | + [self stopTimer]; | ||
| 878 | + [self.playerLayer.player seekToTime:[self getStartTime] toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero]; | ||
| 879 | +} | ||
| 880 | + | ||
| 881 | +- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate | ||
| 882 | +{ | ||
| 883 | + if (!decelerate) { | ||
| 884 | + [self startTimer]; | ||
| 885 | + } | ||
| 886 | +} | ||
| 887 | + | ||
| 888 | +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView | ||
| 889 | +{ | ||
| 890 | + [self startTimer]; | ||
| 891 | +} | ||
| 892 | + | ||
| 893 | +#pragma mark - collection view data sources | ||
| 894 | +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section | ||
| 895 | +{ | ||
| 896 | + return _measureCount; | ||
| 897 | +} | ||
| 898 | + | ||
| 899 | +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath | ||
| 900 | +{ | ||
| 901 | + CNEditVideoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CNEditVideoCell" forIndexPath:indexPath]; | ||
| 902 | + | ||
| 903 | + UIImage *image = _imageCache[@(indexPath.row).stringValue]; | ||
| 904 | + if (image) { | ||
| 905 | + cell.imageView.image = image; | ||
| 906 | + } | ||
| 907 | + | ||
| 908 | + return cell; | ||
| 909 | +} | ||
| 910 | + | ||
| 911 | +static const char _CNOperationCellKey; | ||
| 912 | +- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath | ||
| 913 | +{ | ||
| 914 | + if (!_avAsset) return; | ||
| 915 | + | ||
| 916 | + if (_imageCache[@(indexPath.row).stringValue] || _opCache[@(indexPath.row).stringValue]) { | ||
| 917 | + return; | ||
| 918 | + } | ||
| 919 | + | ||
| 920 | + NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ | ||
| 921 | + NSInteger row = indexPath.row; | ||
| 922 | + NSInteger i = row * _interval; | ||
| 923 | + | ||
| 924 | + CMTime time = CMTimeMake((i+0.35) * _avAsset.duration.timescale, _avAsset.duration.timescale); | ||
| 925 | + | ||
| 926 | + NSError *error = nil; | ||
| 927 | + CGImageRef cgImg = [self.generator copyCGImageAtTime:time actualTime:NULL error:&error]; | ||
| 928 | + if (!error && cgImg) { | ||
| 929 | + UIImage *image = [UIImage imageWithCGImage:cgImg]; | ||
| 930 | + CGImageRelease(cgImg); | ||
| 931 | + | ||
| 932 | + [_imageCache setValue:image forKey:@(row).stringValue]; | ||
| 933 | + | ||
| 934 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 935 | + | ||
| 936 | + NSIndexPath *nowIndexPath = [collectionView indexPathForCell:cell]; | ||
| 937 | + if (row == nowIndexPath.row) { | ||
| 938 | + [(CNEditVideoCell *)cell imageView].image = image; | ||
| 939 | + } else { | ||
| 940 | + UIImage *cacheImage = _imageCache[@(nowIndexPath.row).stringValue]; | ||
| 941 | + if (cacheImage) { | ||
| 942 | + [(CNEditVideoCell *)cell imageView].image = cacheImage; | ||
| 943 | + } | ||
| 944 | + } | ||
| 945 | + }); | ||
| 946 | + [_opCache removeObjectForKey:@(row).stringValue]; | ||
| 947 | + } | ||
| 948 | + objc_removeAssociatedObjects(cell); | ||
| 949 | + }]; | ||
| 950 | + [_queue addOperation:op]; | ||
| 951 | + [_opCache setValue:op forKey:@(indexPath.row).stringValue]; | ||
| 952 | + | ||
| 953 | + objc_setAssociatedObject(cell, &_CNOperationCellKey, op, OBJC_ASSOCIATION_RETAIN_NONATOMIC); | ||
| 954 | +} | ||
| 955 | + | ||
| 956 | +- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath | ||
| 957 | +{ | ||
| 958 | + NSBlockOperation *op = objc_getAssociatedObject(cell, &_CNOperationCellKey); | ||
| 959 | + if (op) { | ||
| 960 | + [op cancel]; | ||
| 961 | + objc_removeAssociatedObjects(cell); | ||
| 962 | + [_opCache removeObjectForKey:@(indexPath.row).stringValue]; | ||
| 963 | + } | ||
| 964 | +} | ||
| 965 | + | ||
| 966 | +- (void)didReceiveMemoryWarning { | ||
| 967 | + [super didReceiveMemoryWarning]; | ||
| 968 | + // Dispose of any resources that can be recreated. | ||
| 969 | +} | ||
| 970 | + | ||
| 971 | +/* | ||
| 972 | +#pragma mark - Navigation | ||
| 973 | + | ||
| 974 | +// In a storyboard-based application, you will often want to do a little preparation before navigation | ||
| 975 | +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { | ||
| 976 | + // Get the new view controller using [segue destinationViewController]. | ||
| 977 | + // Pass the selected object to the new view controller. | ||
| 978 | +} | ||
| 979 | +*/ | ||
| 980 | + | ||
| 981 | +@end |
CNLiveImagePickerController/CNImagePickerController.bundle/Root.plist
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
| 3 | +<plist version="1.0"> | ||
| 4 | +<dict> | ||
| 5 | + <key>StringsTable</key> | ||
| 6 | + <string>Root</string> | ||
| 7 | + <key>PreferenceSpecifiers</key> | ||
| 8 | + <array> | ||
| 9 | + <dict> | ||
| 10 | + <key>Type</key> | ||
| 11 | + <string>PSGroupSpecifier</string> | ||
| 12 | + <key>Title</key> | ||
| 13 | + <string>Group</string> | ||
| 14 | + </dict> | ||
| 15 | + <dict> | ||
| 16 | + <key>Type</key> | ||
| 17 | + <string>PSTextFieldSpecifier</string> | ||
| 18 | + <key>Title</key> | ||
| 19 | + <string>Name</string> | ||
| 20 | + <key>Key</key> | ||
| 21 | + <string>name_preference</string> | ||
| 22 | + <key>DefaultValue</key> | ||
| 23 | + <string></string> | ||
| 24 | + <key>IsSecure</key> | ||
| 25 | + <false/> | ||
| 26 | + <key>KeyboardType</key> | ||
| 27 | + <string>Alphabet</string> | ||
| 28 | + <key>AutocapitalizationType</key> | ||
| 29 | + <string>None</string> | ||
| 30 | + <key>AutocorrectionType</key> | ||
| 31 | + <string>No</string> | ||
| 32 | + </dict> | ||
| 33 | + <dict> | ||
| 34 | + <key>Type</key> | ||
| 35 | + <string>PSToggleSwitchSpecifier</string> | ||
| 36 | + <key>Title</key> | ||
| 37 | + <string>Enabled</string> | ||
| 38 | + <key>Key</key> | ||
| 39 | + <string>enabled_preference</string> | ||
| 40 | + <key>DefaultValue</key> | ||
| 41 | + <true/> | ||
| 42 | + </dict> | ||
| 43 | + <dict> | ||
| 44 | + <key>Type</key> | ||
| 45 | + <string>PSSliderSpecifier</string> | ||
| 46 | + <key>Key</key> | ||
| 47 | + <string>slider_preference</string> | ||
| 48 | + <key>DefaultValue</key> | ||
| 49 | + <real>0.5</real> | ||
| 50 | + <key>MinimumValue</key> | ||
| 51 | + <integer>0</integer> | ||
| 52 | + <key>MaximumValue</key> | ||
| 53 | + <integer>1</integer> | ||
| 54 | + <key>MinimumValueImage</key> | ||
| 55 | + <string></string> | ||
| 56 | + <key>MaximumValueImage</key> | ||
| 57 | + <string></string> | ||
| 58 | + </dict> | ||
| 59 | + </array> | ||
| 60 | +</dict> | ||
| 61 | +</plist> |
CNLiveImagePickerController/CNImagePickerController.bundle/en.lproj/Root.strings
0 → 100644
CNLiveImagePickerController/CNImagePickerController.h
0 → 100644
| 1 | +// | ||
| 2 | +// CNTZImagePickerControllerViewController.h | ||
| 3 | +// CNLiveNetAdd | ||
| 4 | +// | ||
| 5 | +// Created by 梁星国 on 2018/8/22. | ||
| 6 | +// Copyright © 2018年 cnlive. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import "TZImagePickerController.h" | ||
| 10 | + | ||
| 11 | +@interface CNImagePickerController : TZImagePickerController | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + 配置 | ||
| 15 | + */ | ||
| 16 | +- (void)configTZImagePicker; | ||
| 17 | + | ||
| 18 | +@end |
CNLiveImagePickerController/CNImagePickerController.m
0 → 100644
| 1 | +// | ||
| 2 | +// CNTZImagePickerControllerViewController.m | ||
| 3 | +// CNLiveNetAdd | ||
| 4 | +// | ||
| 5 | +// Created by 梁星国 on 2018/8/22. | ||
| 6 | +// Copyright © 2018年 cnlive. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import "CNImagePickerController.h" | ||
| 10 | +#import "AppCommonDefine.h" | ||
| 11 | + | ||
| 12 | +@interface CNImagePickerController () | ||
| 13 | + | ||
| 14 | +@end | ||
| 15 | + | ||
| 16 | +@implementation CNImagePickerController | ||
| 17 | + | ||
| 18 | +- (void)viewDidLoad { | ||
| 19 | + [super viewDidLoad]; | ||
| 20 | + | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | +/** | ||
| 24 | + 配置 | ||
| 25 | + */ | ||
| 26 | +- (void)configTZImagePicker{ | ||
| 27 | + | ||
| 28 | + // imagePickerVc.navigationBar.translucent = NO; | ||
| 29 | + | ||
| 30 | +#pragma mark - 五类个性化设置,这些参数都可以不传,此时会走默认设置 | ||
| 31 | + // 1.设置目前已经选中的图片数组 | ||
| 32 | + self.allowTakePicture = NO; // 在内部显示拍照按钮 | ||
| 33 | + self.allowTakeVideo = NO;// 在内部拍摄按钮 | ||
| 34 | + self.videoMaximumDuration = 10; // 视频最大拍摄时间 | ||
| 35 | + [self setUiImagePickerControllerSettingBlock:^(UIImagePickerController *imagePickerController) { | ||
| 36 | + imagePickerController.videoQuality = UIImagePickerControllerQualityTypeHigh;//拍摄分辨率 | ||
| 37 | + }]; | ||
| 38 | + // imagePickerVc.photoWidth = 1000; | ||
| 39 | + | ||
| 40 | + // 2. Set the appearance | ||
| 41 | + // 2. 在这里设置imagePickerVc的外观 | ||
| 42 | + // if (iOS7Later) { | ||
| 43 | + // imagePickerVc.navigationBar.barTintColor = [UIColor greenColor]; | ||
| 44 | + // } | ||
| 45 | + // imagePickerVc.oKButtonTitleColorDisabled = [UIColor lightGrayColor]; | ||
| 46 | + // imagePickerVc.oKButtonTitleColorNormal = [UIColor greenColor]; | ||
| 47 | + // imagePickerVc.navigationBar.translucent = NO; | ||
| 48 | + self.iconThemeColor = RGBOF(0x00BBE06);// 主体颜色 | ||
| 49 | + self.showPhotoCannotSelectLayer = YES;//达到上限是否有浮层 | ||
| 50 | + self.cannotSelectLayerColor = [[UIColor whiteColor] colorWithAlphaComponent:0.8];//设置达到上限的浮层 | ||
| 51 | + self.photoDefImage = [UIImage imageNamed:@"photo_cell_normal"]; | ||
| 52 | +#pragma mark - TODO:RGBOF | ||
| 53 | +// self.photoSelImage = [UIImage qmui_imageWithColor:RGBOF(0x0BBE06) size:CGSizeMake(22, 22) cornerRadius:11]; | ||
| 54 | + self.photoOriginDefImage = [UIImage imageNamed:@"photo_original_nor"]; | ||
| 55 | + | ||
| 56 | + // 3. Set allow picking video & photo & originalPhoto or not | ||
| 57 | + // 3. 设置是否可以选择视频/图片/原图/gif(未开放) | ||
| 58 | + self.allowPickingVideo = YES;// 显示视频 | ||
| 59 | + self.allowPickingImage = YES;// 显示图片 | ||
| 60 | + self.allowPickingOriginalPhoto = YES; //显示原图 | ||
| 61 | + self.allowPickingGif = NO;// 显示GIF | ||
| 62 | + self.allowPickingMultipleVideo = YES; // 是否可以多选视频 | ||
| 63 | + | ||
| 64 | + // 4. 照片排列按修改时间升序 | ||
| 65 | + self.sortAscendingByModificationDate = YES; | ||
| 66 | + | ||
| 67 | + // imagePickerVc.minImagesCount = 3; | ||
| 68 | + // imagePickerVc.alwaysEnableDoneBtn = YES; | ||
| 69 | + | ||
| 70 | + // imagePickerVc.minPhotoWidthSelectable = 3000; | ||
| 71 | + // imagePickerVc.minPhotoHeightSelectable = 2000; | ||
| 72 | + | ||
| 73 | + /// 5. Single selection mode, valid when maxImagesCount = 1 | ||
| 74 | + /// 5. 单选模式,maxImagesCount为1时才生效 | ||
| 75 | + self.showSelectBtn = NO; | ||
| 76 | + self.allowCrop = NO; | ||
| 77 | + self.needCircleCrop = NO; | ||
| 78 | + // 设置横屏下的裁剪尺寸 | ||
| 79 | + // imagePickerVc.cropRectLandscape = CGRectMake((self.view.tz_height - widthHeight) / 2, left, widthHeight, widthHeight); | ||
| 80 | + /* | ||
| 81 | + [imagePickerVc setCropViewSettingBlock:^(UIView *cropView) { | ||
| 82 | + cropView.layer.borderColor = [UIColor redColor].CGColor; | ||
| 83 | + cropView.layer.borderWidth = 2.0; | ||
| 84 | + }];*/ | ||
| 85 | + | ||
| 86 | + //imagePickerVc.allowPreview = NO; | ||
| 87 | + // 自定义导航栏上的返回按钮 | ||
| 88 | + /* | ||
| 89 | + [imagePickerVc setNavLeftBarButtonSettingBlock:^(UIButton *leftButton){ | ||
| 90 | + [leftButton setImage:[UIImage imageNamed:@"back"] forState:UIControlStateNormal]; | ||
| 91 | + [leftButton setImageEdgeInsets:UIEdgeInsetsMake(0, -10, 0, 20)]; | ||
| 92 | + }]; | ||
| 93 | + imagePickerVc.delegate = self; | ||
| 94 | + */ | ||
| 95 | + | ||
| 96 | + self.statusBarStyle = UIStatusBarStyleDefault;//状态栏黑色 | ||
| 97 | + | ||
| 98 | + self.showSelectedIndex = YES; // 设置是否显示图片序号 | ||
| 99 | + | ||
| 100 | + self.naviBgColor = RGBOF(0xFFFFFF);//导航栏颜色 | ||
| 101 | + self.naviTitleColor = RGBOF(0x282828);//相册title | ||
| 102 | + self.naviTitleFont = [UIFont fontWithName:@"PingFangSC-Medium" size:18];//title文字大小 | ||
| 103 | + [self setNavLeftBarButtonSettingBlock:^(UIButton *leftButton) {//设置返回按钮图标 | ||
| 104 | + [leftButton setImage:[UIImage imageNamed:@"photo_back"] forState:UIControlStateNormal];//返回按钮图片 | ||
| 105 | + [leftButton setTitle:@"返回" forState:UIControlStateNormal];//设置文字大小 | ||
| 106 | + [leftButton setTitleColor:RGBOF(0x282828) forState:UIControlStateNormal];//设置文字颜色 | ||
| 107 | + leftButton.titleLabel.font = [UIFont systemFontOfSize:15];//设置文字大小 | ||
| 108 | + leftButton.imageEdgeInsets = UIEdgeInsetsMake(0, -7, 0, +7);//设置图片边缘 | ||
| 109 | + }]; | ||
| 110 | + self.barItemTextColor = RGBOF(0x282828);//设置barItem文字颜色 | ||
| 111 | + self.barItemTextFont = [UIFont systemFontOfSize:15]; | ||
| 112 | + | ||
| 113 | + // 设置首选语言 / Set preferred language | ||
| 114 | + // imagePickerVc.preferredLanguage = @"zh-Hans"; | ||
| 115 | + | ||
| 116 | + // 设置languageBundle以使用其它语言 / Set languageBundle to use other language | ||
| 117 | + // imagePickerVc.languageBundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"tz-ru" ofType:@"lproj"]]; | ||
| 118 | + | ||
| 119 | + /// 【自定义各页面/组件的样式】在界面初始化/组件setModel完成后调用,允许外界修改样式等 | ||
| 120 | + //相册 | ||
| 121 | + [self setPhotoPickerPageUIConfigBlock:^(UICollectionView *collectionView, UIView *bottomToolBar, UIButton *previewButton, UIButton *originalPhotoButton, UILabel *originalPhotoLabel, UIButton *doneButton, UIImageView *numberImageView, UILabel *numberLabel, UIView *divideLine) { | ||
| 122 | + | ||
| 123 | + //底部栏 | ||
| 124 | + [bottomToolBar setBackgroundColor:RGBOF(0xFFFFFF)]; | ||
| 125 | + | ||
| 126 | + //预览 | ||
| 127 | + previewButton.titleLabel.font = [UIFont systemFontOfSize:16]; | ||
| 128 | + [previewButton setTitleColor:RGBOF(0xB3B3B3) forState:UIControlStateDisabled];//禁用 | ||
| 129 | + [previewButton setTitleColor:RGBOF(0x000000) forState:UIControlStateNormal];//默认 | ||
| 130 | + | ||
| 131 | + //原图按钮 | ||
| 132 | + [originalPhotoButton setImage:[UIImage imageNamed:@"photo_original_normal"] forState:UIControlStateDisabled];//禁用 | ||
| 133 | + [originalPhotoButton setImage:[UIImage imageNamed:@"photo_original_normal"] forState:UIControlStateNormal];//默认 | ||
| 134 | + [originalPhotoButton setImage:[UIImage imageNamed:@"photo_original_select"] forState:UIControlStateSelected];//选中 | ||
| 135 | + originalPhotoButton.titleLabel.font = [UIFont systemFontOfSize:16]; | ||
| 136 | + [originalPhotoButton setTitleColor:RGBOF(0xB3B3B3) forState:UIControlStateDisabled]; | ||
| 137 | + [originalPhotoButton setTitleColor:RGBOF(0x000000) forState:UIControlStateNormal]; | ||
| 138 | + | ||
| 139 | + //原图文字提示 | ||
| 140 | + originalPhotoLabel.font = [UIFont fontWithName:@"PingFang-SC-Regular" size:16]; | ||
| 141 | + originalPhotoLabel.textColor = RGBOF(0xB3B3B3); | ||
| 142 | + originalPhotoLabel.hidden = YES; | ||
| 143 | + | ||
| 144 | + //选中提示 | ||
| 145 | + numberImageView = [UIImageView new]; | ||
| 146 | + numberLabel = [UILabel new]; | ||
| 147 | + | ||
| 148 | + //完成按钮 | ||
| 149 | + doneButton.titleLabel.font = [UIFont systemFontOfSize:13]; | ||
| 150 | + [doneButton setTitle:@"完成" forState:UIControlStateDisabled]; | ||
| 151 | + [doneButton setTitleColor:RGBAOF(0xFFFFFF, 0.5) forState:UIControlStateDisabled]; | ||
| 152 | + [doneButton setTitleColor:RGBOF(0xFFFFFF) forState:UIControlStateNormal]; | ||
| 153 | + | ||
| 154 | + //颜色绘制成图片 | ||
| 155 | +// UIImage *dis_image = [UIImage qmui_imageWithColor:RGBAOF(0x0BBE06, 0.5)]; | ||
| 156 | +// [doneButton setBackgroundImage:dis_image forState:UIControlStateDisabled]; | ||
| 157 | +// UIImage *nor_image = [UIImage qmui_imageWithColor:RGBOF(0x0BBE06)]; | ||
| 158 | +// [doneButton setBackgroundImage:nor_image forState:UIControlStateNormal]; | ||
| 159 | + doneButton.layer.masksToBounds = YES; | ||
| 160 | + doneButton.layer.cornerRadius = 5; | ||
| 161 | + | ||
| 162 | + //分割线 | ||
| 163 | + [divideLine setBackgroundColor:RGBOF(0xDEDEDE)]; | ||
| 164 | + | ||
| 165 | + }]; | ||
| 166 | + | ||
| 167 | + //图片预览 | ||
| 168 | + [self setPhotoPreviewPageUIConfigBlock:^(UICollectionView *collectionView, UIView *naviBar, UIButton *backButton, UIButton *selectButton, UILabel *indexLabel, UIView *toolBar, UIButton *originalPhotoButton, UILabel *originalPhotoLabel, UIButton *doneButton, UIImageView *numberImageView, UILabel *numberLabel) { | ||
| 169 | + | ||
| 170 | + //返回按钮 | ||
| 171 | + [backButton setImage:[UIImage imageNamed:@"photo_original_back"] forState:UIControlStateNormal]; | ||
| 172 | + //选择按钮 | ||
| 173 | + [selectButton setImage:[UIImage imageNamed:@"photo_original_nor"] forState:UIControlStateNormal]; | ||
| 174 | +// [selectButton setImage:[UIImage qmui_imageWithColor:RGBOF(0x0BBE06) size:CGSizeMake(28, 28) cornerRadius:14] forState:UIControlStateSelected]; | ||
| 175 | + [selectButton setTitle:@"" forState:UIControlStateDisabled]; | ||
| 176 | + | ||
| 177 | + //原图按钮 | ||
| 178 | + [originalPhotoButton setImage:[UIImage imageNamed:@"photo_original_normal"] forState:UIControlStateDisabled];//禁用 | ||
| 179 | + [originalPhotoButton setImage:[UIImage imageNamed:@"photo_original_normal"] forState:UIControlStateNormal];//默认 | ||
| 180 | + [originalPhotoButton setImage:[UIImage imageNamed:@"photo_original_select"] forState:UIControlStateSelected];//选中 | ||
| 181 | + originalPhotoButton.titleLabel.font = [UIFont systemFontOfSize:16]; | ||
| 182 | + [originalPhotoButton setTitleColor:RGBOF(0xB3B3B3) forState:UIControlStateDisabled]; | ||
| 183 | + [originalPhotoButton setTitleColor:RGBAOF(0xFFFFFF, 0.7) forState:UIControlStateNormal]; | ||
| 184 | + //原图内容隐藏 | ||
| 185 | + originalPhotoLabel.hidden = YES; | ||
| 186 | + | ||
| 187 | + //完成按钮 | ||
| 188 | + doneButton.titleLabel.font = [UIFont systemFontOfSize:13]; | ||
| 189 | + [doneButton setTitle:@"完成" forState:UIControlStateDisabled]; | ||
| 190 | + [doneButton setTitleColor:RGBOF(0xFFFFFF) forState:UIControlStateDisabled]; | ||
| 191 | + [doneButton setTitleColor:RGBOF(0xFFFFFF) forState:UIControlStateNormal]; | ||
| 192 | + | ||
| 193 | + //颜色绘制成图片 | ||
| 194 | +// UIImage *dis_image = [UIImage qmui_imageWithColor:RGBAOF(0x0BBE06, 0.5)]; | ||
| 195 | +// [doneButton setBackgroundImage:dis_image forState:UIControlStateSelected]; | ||
| 196 | +// UIImage *nor_image = [UIImage qmui_imageWithColor:RGBOF(0x0BBE06)]; | ||
| 197 | +// [doneButton setBackgroundImage:nor_image forState:UIControlStateNormal]; | ||
| 198 | + doneButton.layer.masksToBounds = YES; | ||
| 199 | + doneButton.layer.cornerRadius = 5; | ||
| 200 | + }]; | ||
| 201 | + | ||
| 202 | + //视频预览 | ||
| 203 | + [self setVideoPreviewPageUIConfigBlock:^(UIButton *playButton, UIView *toolBar, UIButton *doneButton) { | ||
| 204 | + | ||
| 205 | + //完成按钮 | ||
| 206 | + doneButton.titleLabel.font = [UIFont systemFontOfSize:13]; | ||
| 207 | + [doneButton setTitle:@"完成" forState:UIControlStateDisabled]; | ||
| 208 | + [doneButton setTitleColor:RGBOF(0xFFFFFF) forState:UIControlStateDisabled]; | ||
| 209 | + [doneButton setTitleColor:RGBOF(0xFFFFFF) forState:UIControlStateNormal]; | ||
| 210 | + | ||
| 211 | + //颜色绘制成图片 | ||
| 212 | +// UIImage *dis_image = [UIImage qmui_imageWithColor:RGBAOF(0x0BBE06, 0.5)]; | ||
| 213 | +// [doneButton setBackgroundImage:dis_image forState:UIControlStateSelected]; | ||
| 214 | +// UIImage *nor_image = [UIImage qmui_imageWithColor:RGBOF(0x0BBE06)]; | ||
| 215 | +// [doneButton setBackgroundImage:nor_image forState:UIControlStateNormal]; | ||
| 216 | + doneButton.layer.masksToBounds = YES; | ||
| 217 | + doneButton.layer.cornerRadius = 5; | ||
| 218 | + | ||
| 219 | + }]; | ||
| 220 | + | ||
| 221 | + //gif预览 | ||
| 222 | + [self setGifPreviewPageUIConfigBlock:^(UIView *toolBar, UIButton *doneButton) { | ||
| 223 | + | ||
| 224 | + //完成按钮 | ||
| 225 | + doneButton.titleLabel.font = [UIFont systemFontOfSize:13]; | ||
| 226 | + [doneButton setTitle:@"完成" forState:UIControlStateDisabled]; | ||
| 227 | + [doneButton setTitleColor:RGBOF(0xFFFFFF) forState:UIControlStateDisabled]; | ||
| 228 | + [doneButton setTitleColor:RGBOF(0xFFFFFF) forState:UIControlStateNormal]; | ||
| 229 | + | ||
| 230 | + //颜色绘制成图片 | ||
| 231 | +// UIImage *dis_image = [UIImage qmui_imageWithColor:RGBAOF(0x0BBE06, 0.5)]; | ||
| 232 | +// [doneButton setBackgroundImage:dis_image forState:UIControlStateSelected]; | ||
| 233 | +// UIImage *nor_image = [UIImage qmui_imageWithColor:RGBOF(0x0BBE06)]; | ||
| 234 | +// [doneButton setBackgroundImage:nor_image forState:UIControlStateNormal]; | ||
| 235 | + doneButton.layer.masksToBounds = YES; | ||
| 236 | + doneButton.layer.cornerRadius = 5; | ||
| 237 | + | ||
| 238 | + }]; | ||
| 239 | + //相册cell | ||
| 240 | + [self setAssetCellDidSetModelBlock:^(TZAssetCell *cell, UIImageView *imageView, UIImageView *selectImageView, UILabel *indexLabel, UIView *bottomView, UILabel *timeLength, UIImageView *videoImgView) { | ||
| 241 | + | ||
| 242 | + | ||
| 243 | + }]; | ||
| 244 | + //相簿 | ||
| 245 | + [self setAlbumCellDidSetModelBlock:^(TZAlbumCell *cell, UIImageView *posterImageView, UILabel *titleLabel) { | ||
| 246 | + [posterImageView setBackgroundColor:[UIColor blueColor]]; | ||
| 247 | + [posterImageView setBackgroundColor:[UIColor blueColor]]; | ||
| 248 | + | ||
| 249 | + }]; | ||
| 250 | + | ||
| 251 | + | ||
| 252 | + /// 【自定义各页面/组件的frame】在界面viewDidLayoutSubviews/组件layoutSubviews后调用,允许外界修改frame等 | ||
| 253 | + //相册 | ||
| 254 | + [self setPhotoPickerPageDidLayoutSubviewsBlock:^(UICollectionView *collectionView, UIView *bottomToolBar, UIButton *previewButton, UIButton *originalPhotoButton, UILabel *originalPhotoLabel, UIButton *doneButton, UIImageView *numberImageView, UILabel *numberLabel, UIView *divideLine) { | ||
| 255 | + | ||
| 256 | + CGRect frame = bottomToolBar.frame; | ||
| 257 | + doneButton.frame = CGRectMake(frame.size.width - 60 - 13, frame.size.height /2 - 15, 60, 30); | ||
| 258 | + | ||
| 259 | + CGRect originalPhotoLabelFrame = originalPhotoLabel.frame; | ||
| 260 | + CGFloat originalX = (bottomToolBar.frame.size.width - originalPhotoLabelFrame.size.width) /2; | ||
| 261 | + CGFloat originalY = originalPhotoLabelFrame.origin.y; | ||
| 262 | + originalPhotoButton.frame = CGRectMake(originalX, originalY, originalPhotoLabelFrame.size.width, originalPhotoLabelFrame.size.height); | ||
| 263 | + | ||
| 264 | + | ||
| 265 | + }]; | ||
| 266 | + //图片预览 | ||
| 267 | + [self setPhotoPreviewPageDidLayoutSubviewsBlock:^(UICollectionView *collectionView, UIView *naviBar, UIButton *backButton, UIButton *selectButton, UILabel *indexLabel, UIView *toolBar, UIButton *originalPhotoButton, UILabel *originalPhotoLabel, UIButton *doneButton, UIImageView *numberImageView, UILabel *numberLabel) { | ||
| 268 | + | ||
| 269 | + CGRect frame = toolBar.frame; | ||
| 270 | + doneButton.frame = CGRectMake(frame.size.width - 60 - 13, frame.size.height /2 - 15, 60, 30); | ||
| 271 | + | ||
| 272 | + }]; | ||
| 273 | + //视频预览 | ||
| 274 | + [self setVideoPreviewPageDidLayoutSubviewsBlock:^(UIButton *playButton, UIView *toolBar, UIButton *doneButton) { | ||
| 275 | + | ||
| 276 | + CGRect frame = toolBar.frame; | ||
| 277 | + doneButton.frame = CGRectMake(frame.size.width - 60 - 13, frame.size.height /2 - 15, 60, 30); | ||
| 278 | + | ||
| 279 | + }]; | ||
| 280 | + //gif预览 | ||
| 281 | + [self setGifPreviewPageDidLayoutSubviewsBlock:^(UIView *toolBar, UIButton *doneButton) { | ||
| 282 | + | ||
| 283 | + CGRect frame = toolBar.frame; | ||
| 284 | + doneButton.frame = CGRectMake(frame.size.width - 60 - 13, frame.size.height /2 - 15, 60, 30); | ||
| 285 | + | ||
| 286 | + }]; | ||
| 287 | + //相册cell | ||
| 288 | + | ||
| 289 | + [self setAssetCellDidLayoutSubviewsBlock:^(TZAssetCell *cell, UIImageView *imageView, UIImageView *selectImageView, UILabel *indexLabel, UIView *bottomView, UILabel *timeLength, UIImageView *videoImgView) { | ||
| 290 | + }]; | ||
| 291 | + //相簿 | ||
| 292 | + [self setAlbumCellDidLayoutSubviewsBlock:^(TZAlbumCell *cell, UIImageView *posterImageView, UILabel *titleLabel) { | ||
| 293 | + | ||
| 294 | + }]; | ||
| 295 | + | ||
| 296 | + //设置图片个数 | ||
| 297 | + self.photoNumberIconImage = [UIImage new]; | ||
| 298 | + | ||
| 299 | +} | ||
| 300 | + | ||
| 301 | +- (void)didReceiveMemoryWarning { | ||
| 302 | + [super didReceiveMemoryWarning]; | ||
| 303 | + // Dispose of any resources that can be recreated. | ||
| 304 | +} | ||
| 305 | + | ||
| 306 | + | ||
| 307 | +@end |
CNLiveImagePickerController/CNImagePickerManager.h
0 → 100644
| 1 | +// | ||
| 2 | +// CNImagePickerManager.h | ||
| 3 | +// CNLiveNetAdd | ||
| 4 | +// | ||
| 5 | +// Created by 梁星国 on 2018/10/8. | ||
| 6 | +// Copyright © 2018年 cnlive. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import <UIKit/UIKit.h> | ||
| 10 | +#import <Foundation/Foundation.h> | ||
| 11 | +#import "CNImagePickerController.h" | ||
| 12 | + | ||
| 13 | + | ||
| 14 | +typedef void (^didFinishPickingPhotosHandle)(NSArray<UIImage *> *photos,NSArray *assets,BOOL isSelectOriginalPhoto);//选着图片返回 | ||
| 15 | + | ||
| 16 | +//typedef void (^failureBlock)(NSMutableArray* errorArray, NSError *error);//失败数组,错误 | ||
| 17 | + | ||
| 18 | +typedef NS_ENUM(NSUInteger, CNImagePickerEnterType) { | ||
| 19 | + CNImagePickerEnterTypeFriendsCircle = 0,//朋友圈 | ||
| 20 | + CNImagePickerEnterTypeComment = 1,//评论 | ||
| 21 | + CNImagePickerEnterTypeSweepCode = 2,//扫码 | ||
| 22 | + CNImagePickerEnterTypeChat = 3,//聊天 | ||
| 23 | + CNImagePickerEnterTypeOther = 4,//其他 | ||
| 24 | + CNImagePickerEnterTypeWitness = 5, //目击者 | ||
| 25 | + | ||
| 26 | +}; | ||
| 27 | +@interface CNImagePickerManager : NSObject | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | +/** | ||
| 32 | + 打开相册选择图片 | ||
| 33 | + | ||
| 34 | + @param maxImagesCount 最大图片 | ||
| 35 | + @param columnNumber 列个数 | ||
| 36 | + @param delegate 代理(UIViewController<TZImagePickerControllerDelegate>) | ||
| 37 | + @param pushPhotoPickerVc 是否跳转到相册 | ||
| 38 | + @param isSelectOriginalPhoto 是否原图 | ||
| 39 | + @param selectedAssets 选择图片 | ||
| 40 | + @param enterType 进入方式 | ||
| 41 | + @param pickingPhotosHandle 选择结束回调 | ||
| 42 | + */ | ||
| 43 | ++ (void)ImagePickerWithMaxImagesCount:(NSInteger)maxImagesCount columnNumber:(NSInteger)columnNumber delegate:(id<TZImagePickerControllerDelegate>)delegate pushPhotoPickerVc:(BOOL)pushPhotoPickerVc isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto selectedAssets:(NSMutableArray *)selectedAssets enterType:(CNImagePickerEnterType )enterType pickingPhotosHandle:(didFinishPickingPhotosHandle) pickingPhotosHandle; | ||
| 44 | + | ||
| 45 | +/** | ||
| 46 | + 预览图片 | ||
| 47 | + | ||
| 48 | + @param maxImagesCount 最大限制 | ||
| 49 | + @param delegate 代理(UIViewController<TZImagePickerControllerDelegate>) | ||
| 50 | + @param selectedAssets 选中Assets | ||
| 51 | + @param selectedPhotos 选中Photos | ||
| 52 | + @param index 选中第几个 | ||
| 53 | + @param isSelectOriginalPhoto 是否原图 | ||
| 54 | + @param enterType 进入方式 | ||
| 55 | + @param pickingPhotosHandle 回调参数 | ||
| 56 | + */ | ||
| 57 | ++ (void)ImagePickerWithMaxImagesCount:(NSInteger)maxImagesCount delegate:(id<TZImagePickerControllerDelegate>)delegate selectedAssets:(NSMutableArray *)selectedAssets selectedPhotos:(NSMutableArray *)selectedPhotos index:(NSInteger)index isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto enterType:(CNImagePickerEnterType )enterType pickingPhotosHandle:(didFinishPickingPhotosHandle) pickingPhotosHandle; | ||
| 58 | + | ||
| 59 | + | ||
| 60 | +/** | ||
| 61 | + 预览单个视频 | ||
| 62 | + | ||
| 63 | + @param selectedAsset 选择视频 | ||
| 64 | + @param delegate 代理(UIViewController<TZImagePickerControllerDelegate>) | ||
| 65 | + */ | ||
| 66 | ++ (void)ImagePickerWithSelectedAsset:(id)selectedAsset delegate:(id<TZImagePickerControllerDelegate>)delegate; | ||
| 67 | + | ||
| 68 | + | ||
| 69 | +/** | ||
| 70 | + 保存图片 | ||
| 71 | + | ||
| 72 | + @param delegate 代理(UIViewController<TZImagePickerControllerDelegate>) | ||
| 73 | + @param image 图片 | ||
| 74 | + @param location 地点 | ||
| 75 | + @param success 成功 | ||
| 76 | + @param failure 失败 | ||
| 77 | + | ||
| 78 | + */ | ||
| 79 | ++(void)ImageManagerSavePhotoWithDelegate:(id<TZImagePickerControllerDelegate>)delegate image:(UIImage *)image location:(CLLocation *)location success:(void (^)(TZAssetModel *assetModel))success failure:(void (^)(NSError *error))failure; | ||
| 80 | + | ||
| 81 | + | ||
| 82 | +/** | ||
| 83 | + 判断Asset类型 | ||
| 84 | + | ||
| 85 | + @param asset asset | ||
| 86 | + @return 返回类型 | ||
| 87 | + */ | ||
| 88 | ++ (TZAssetModelMediaType)getAssetType:(id)asset; | ||
| 89 | + | ||
| 90 | + | ||
| 91 | +@end | ||
| 92 | + |
CNLiveImagePickerController/CNImagePickerManager.m
0 → 100644
| 1 | +// | ||
| 2 | +// CNImagePickerManager.m | ||
| 3 | +// CNLiveNetAdd | ||
| 4 | +// | ||
| 5 | +// Created by 梁星国 on 2018/10/8. | ||
| 6 | +// Copyright © 2018年 cnlive. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import "CNImagePickerManager.h" | ||
| 10 | +#import <AssetsLibrary/AssetsLibrary.h> | ||
| 11 | +#import "AppCommonDefine.h" | ||
| 12 | + | ||
| 13 | +@implementation CNImagePickerManager | ||
| 14 | + | ||
| 15 | +/// 打开相册选择图片 | ||
| 16 | ++ (void)ImagePickerWithMaxImagesCount:(NSInteger)maxImagesCount columnNumber:(NSInteger)columnNumber delegate:(id<TZImagePickerControllerDelegate>)delegate pushPhotoPickerVc:(BOOL)pushPhotoPickerVc isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto selectedAssets:(NSMutableArray *)selectedAssets enterType:(CNImagePickerEnterType )enterType pickingPhotosHandle:(didFinishPickingPhotosHandle) pickingPhotosHandle{ | ||
| 17 | + | ||
| 18 | + if (![delegate isKindOfClass:[UIViewController class]]) { | ||
| 19 | + return; | ||
| 20 | + } | ||
| 21 | + UIViewController *vc = (UIViewController *)delegate; | ||
| 22 | + | ||
| 23 | + CNImagePickerController *imagePickerVc = [[CNImagePickerController alloc] initWithMaxImagesCount:maxImagesCount columnNumber:columnNumber delegate:delegate pushPhotoPickerVc:pushPhotoPickerVc]; | ||
| 24 | + [imagePickerVc configTZImagePicker];//基本配置 | ||
| 25 | + //特殊配置 | ||
| 26 | + switch (enterType) { | ||
| 27 | + case CNImagePickerEnterTypeFriendsCircle: | ||
| 28 | + imagePickerVc.maxEditVideoTime = kFriendsCircle_limit_video_maxEdit;//编辑长度 | ||
| 29 | + imagePickerVc.maxVideoDuration = kFriendsCircle_limit_video_max;//编辑视频最大长度 | ||
| 30 | + // 3. 设置是否可以混选选择视频/图片/原图 | ||
| 31 | + imagePickerVc.allowPickingMultipleVideo = NO; | ||
| 32 | + break; | ||
| 33 | + case CNImagePickerEnterTypeComment: | ||
| 34 | + | ||
| 35 | + break; | ||
| 36 | + case CNImagePickerEnterTypeSweepCode: | ||
| 37 | + | ||
| 38 | + break; | ||
| 39 | + case CNImagePickerEnterTypeChat: | ||
| 40 | + | ||
| 41 | + break; | ||
| 42 | + case CNImagePickerEnterTypeWitness: | ||
| 43 | + imagePickerVc.allowPickingImage = NO;// 显示图片 | ||
| 44 | + imagePickerVc.allowPickingMultipleVideo = NO; | ||
| 45 | + imagePickerVc.maxEditVideoTime = kFriendsCircle_limit_video_maxEdit;//编辑长度 | ||
| 46 | + imagePickerVc.maxVideoDuration = kFriendsCircle_limit_video_max;//编辑视频最大长度 | ||
| 47 | + break; | ||
| 48 | + default://其他 | ||
| 49 | + break; | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + imagePickerVc.isSelectOriginalPhoto = isSelectOriginalPhoto;//是否选择原图 | ||
| 53 | + // 1.设置目前已经选中的图片数组 | ||
| 54 | + imagePickerVc.selectedAssets = selectedAssets; // 目前已经选中的图片数组 | ||
| 55 | + /// 5. Single selection mode, valid when maxImagesCount = 1 | ||
| 56 | + /// 5. 单选模式,maxImagesCount为1时才生效 | ||
| 57 | + // 设置竖屏下的裁剪尺寸 | ||
| 58 | + NSInteger left = 30; | ||
| 59 | + NSInteger widthHeight = vc.view.frame.size.width - 2 * left; | ||
| 60 | + NSInteger top = (vc.view.frame.size.height - widthHeight) / 2; | ||
| 61 | + imagePickerVc.cropRect = CGRectMake(left, top, widthHeight, widthHeight); | ||
| 62 | + // 你可以通过block或者代理,来得到用户选择的照片. | ||
| 63 | + [imagePickerVc setDidFinishPickingPhotosHandle:^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isSelectOriginalPhoto) { | ||
| 64 | + // 回调参数 | ||
| 65 | + !pickingPhotosHandle ? : pickingPhotosHandle(photos, assets, isSelectOriginalPhoto); | ||
| 66 | + | ||
| 67 | + }]; | ||
| 68 | + | ||
| 69 | + [vc presentViewController:imagePickerVc animated:YES completion:nil]; | ||
| 70 | +} | ||
| 71 | + | ||
| 72 | +/// 预览图片 | ||
| 73 | ++ (void)ImagePickerWithMaxImagesCount:(NSInteger)maxImagesCount delegate:(id<TZImagePickerControllerDelegate>)delegate selectedAssets:(NSMutableArray *)selectedAssets selectedPhotos:(NSMutableArray *)selectedPhotos index:(NSInteger)index isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto enterType:(CNImagePickerEnterType )enterType pickingPhotosHandle:(didFinishPickingPhotosHandle) pickingPhotosHandle { | ||
| 74 | + | ||
| 75 | + if (![delegate isKindOfClass:[UIViewController class]]) { | ||
| 76 | + return; | ||
| 77 | + } | ||
| 78 | + UIViewController *vc = (UIViewController *)delegate; | ||
| 79 | + | ||
| 80 | + CNImagePickerController *imagePickerVc = [[CNImagePickerController alloc] initWithSelectedAssets:selectedAssets selectedPhotos:selectedPhotos index:index]; | ||
| 81 | + [imagePickerVc configTZImagePicker];//基本配置 | ||
| 82 | + //特殊配置 | ||
| 83 | + switch (enterType) { | ||
| 84 | + case CNImagePickerEnterTypeFriendsCircle: | ||
| 85 | + imagePickerVc.allowPickingGif = NO; | ||
| 86 | + imagePickerVc.allowPickingOriginalPhoto = YES; | ||
| 87 | + imagePickerVc.allowPickingMultipleVideo = NO; | ||
| 88 | + break; | ||
| 89 | + case CNImagePickerEnterTypeComment: | ||
| 90 | +#pragma mark - TODO:根据不同设置 | ||
| 91 | + break; | ||
| 92 | + case CNImagePickerEnterTypeSweepCode: | ||
| 93 | + | ||
| 94 | + break; | ||
| 95 | + case CNImagePickerEnterTypeChat: | ||
| 96 | + | ||
| 97 | + break; | ||
| 98 | + case CNImagePickerEnterTypeOther: | ||
| 99 | + break; | ||
| 100 | + default://其他 | ||
| 101 | + break; | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + imagePickerVc.maxImagesCount = maxImagesCount; | ||
| 105 | + imagePickerVc.isSelectOriginalPhoto = isSelectOriginalPhoto; | ||
| 106 | + | ||
| 107 | + [imagePickerVc setDidFinishPickingPhotosHandle:^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isSelectOriginalPhoto) { | ||
| 108 | + // 回调参数 | ||
| 109 | + !pickingPhotosHandle ? : pickingPhotosHandle(photos, assets, isSelectOriginalPhoto); | ||
| 110 | + | ||
| 111 | + }]; | ||
| 112 | + [vc presentViewController:imagePickerVc animated:YES completion:nil]; | ||
| 113 | + | ||
| 114 | +} | ||
| 115 | + | ||
| 116 | +/// 预览单个视频 | ||
| 117 | ++ (void)ImagePickerWithSelectedAsset:(id)selectedAsset delegate:(id<TZImagePickerControllerDelegate>)delegate { | ||
| 118 | + | ||
| 119 | + if (![delegate isKindOfClass:[UIViewController class]]) { | ||
| 120 | + return; | ||
| 121 | + } | ||
| 122 | + UIViewController *vc = (UIViewController *)delegate; | ||
| 123 | + | ||
| 124 | + TZVideoPlayerController *videoVC = [[TZVideoPlayerController alloc] init]; | ||
| 125 | + TZAssetModel *model = [TZAssetModel modelWithAsset:selectedAsset type:TZAssetModelMediaTypeVideo timeLength:@""]; | ||
| 126 | + videoVC.isOther = YES;//(LXG)相册外进入 | ||
| 127 | + videoVC.model = model; | ||
| 128 | + [vc presentViewController:videoVC animated:YES completion:nil]; | ||
| 129 | + | ||
| 130 | +} | ||
| 131 | + | ||
| 132 | +/// 返回类型 | ||
| 133 | ++ (TZAssetModelMediaType)getAssetType:(id)asset { | ||
| 134 | + | ||
| 135 | + TZAssetModelMediaType type = TZAssetModelMediaTypePhoto; | ||
| 136 | + if ([asset isKindOfClass:[PHAsset class]]) { | ||
| 137 | + PHAsset *phAsset = (PHAsset *)asset; | ||
| 138 | + if (phAsset.mediaType == PHAssetMediaTypeVideo) type = TZAssetModelMediaTypeVideo; | ||
| 139 | + else if (phAsset.mediaType == PHAssetMediaTypeAudio) type = TZAssetModelMediaTypeAudio; | ||
| 140 | + else if (phAsset.mediaType == PHAssetMediaTypeImage) { | ||
| 141 | + if (iOS9_1Later) { | ||
| 142 | + if (@available(iOS 9.1, *)) { | ||
| 143 | + if (phAsset.mediaSubtypes == PHAssetMediaSubtypePhotoLive) | ||
| 144 | + { | ||
| 145 | + type = TZAssetModelMediaTypeLivePhoto; | ||
| 146 | + } | ||
| 147 | + } else { | ||
| 148 | + // Fallback on earlier versions | ||
| 149 | + } | ||
| 150 | + } | ||
| 151 | + // Gif | ||
| 152 | + if ([[phAsset valueForKey:@"filename"] hasSuffix:@"GIF"]) { | ||
| 153 | + type = TZAssetModelMediaTypePhotoGif; | ||
| 154 | + } | ||
| 155 | + } | ||
| 156 | + } else { | ||
| 157 | + if ([[asset valueForProperty:ALAssetPropertyType] isEqualToString:ALAssetTypeVideo]) { | ||
| 158 | + type = TZAssetModelMediaTypeVideo; | ||
| 159 | + } | ||
| 160 | + } | ||
| 161 | + return type; | ||
| 162 | +} | ||
| 163 | + | ||
| 164 | + | ||
| 165 | ++(void)ImageManagerSavePhotoWithDelegate:(id<TZImagePickerControllerDelegate>)delegate image:(UIImage *)image location:(CLLocation *)location success:(void (^)(TZAssetModel *assetModel))success failure:(void (^)(NSError *error))failure { | ||
| 166 | + | ||
| 167 | + TZImagePickerController *tzImagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:1 delegate:delegate]; | ||
| 168 | + tzImagePickerVc.sortAscendingByModificationDate = YES; | ||
| 169 | + | ||
| 170 | + [tzImagePickerVc showProgressHUD]; | ||
| 171 | + // save photo and get asset / 保存图片,获取到asset | ||
| 172 | + [[TZImageManager manager] savePhotoWithImage:image location:location completion:^(NSError *error){ | ||
| 173 | + | ||
| 174 | + if (error) { | ||
| 175 | + [tzImagePickerVc hideProgressHUD]; | ||
| 176 | + failure(error); | ||
| 177 | + NSLog(@"图片保存失败 %@",error); | ||
| 178 | + } else { | ||
| 179 | + | ||
| 180 | + [[TZImageManager manager] getCameraRollAlbum:NO allowPickingImage:YES needFetchAssets:YES completion:^(TZAlbumModel *model) { | ||
| 181 | + [[TZImageManager manager] getAssetsFromFetchResult:model.result allowPickingVideo:NO allowPickingImage:YES completion:^(NSArray<TZAssetModel *> *models) { | ||
| 182 | + [tzImagePickerVc hideProgressHUD]; | ||
| 183 | + TZAssetModel *assetModel = [models firstObject]; | ||
| 184 | + if (tzImagePickerVc.sortAscendingByModificationDate) { | ||
| 185 | + assetModel = [models lastObject]; | ||
| 186 | + } | ||
| 187 | + success(assetModel); | ||
| 188 | + }]; | ||
| 189 | + | ||
| 190 | + }]; | ||
| 191 | + } | ||
| 192 | + }]; | ||
| 193 | + | ||
| 194 | +} | ||
| 195 | + | ||
| 196 | + | ||
| 197 | + | ||
| 198 | + | ||
| 199 | + | ||
| 200 | + | ||
| 201 | + | ||
| 202 | + | ||
| 203 | + | ||
| 204 | +@end |
CNLiveImagePickerController/NSBundle+TZImagePicker.h
0 → 100755
| 1 | +// | ||
| 2 | +// NSBundle+TZImagePicker.h | ||
| 3 | +// TZImagePickerController | ||
| 4 | +// | ||
| 5 | +// Created by 谭真 on 16/08/18. | ||
| 6 | +// Copyright © 2016年 谭真. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import <UIKit/UIKit.h> | ||
| 10 | + | ||
| 11 | +@interface NSBundle (TZImagePicker) | ||
| 12 | + | ||
| 13 | ++ (NSBundle *)tz_imagePickerBundle; | ||
| 14 | + | ||
| 15 | ++ (NSString *)tz_localizedStringForKey:(NSString *)key value:(NSString *)value; | ||
| 16 | ++ (NSString *)tz_localizedStringForKey:(NSString *)key; | ||
| 17 | + | ||
| 18 | +@end | ||
| 19 | + |
CNLiveImagePickerController/NSBundle+TZImagePicker.m
0 → 100755
| 1 | +// | ||
| 2 | +// NSBundle+TZImagePicker.m | ||
| 3 | +// TZImagePickerController | ||
| 4 | +// | ||
| 5 | +// Created by 谭真 on 16/08/18. | ||
| 6 | +// Copyright © 2016年 谭真. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import "NSBundle+TZImagePicker.h" | ||
| 10 | +#import "TZImagePickerController.h" | ||
| 11 | + | ||
| 12 | +@implementation NSBundle (TZImagePicker) | ||
| 13 | + | ||
| 14 | ++ (NSBundle *)tz_imagePickerBundle { | ||
| 15 | + NSBundle *bundle = [NSBundle bundleForClass:[TZImagePickerController class]]; | ||
| 16 | + NSURL *url = [bundle URLForResource:@"TZImagePickerController" withExtension:@"bundle"]; | ||
| 17 | + bundle = [NSBundle bundleWithURL:url]; | ||
| 18 | + return bundle; | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | ++ (NSString *)tz_localizedStringForKey:(NSString *)key { | ||
| 22 | + return [self tz_localizedStringForKey:key value:@""]; | ||
| 23 | +} | ||
| 24 | + | ||
| 25 | ++ (NSString *)tz_localizedStringForKey:(NSString *)key value:(NSString *)value { | ||
| 26 | + NSBundle *bundle = [TZImagePickerConfig sharedInstance].languageBundle; | ||
| 27 | + NSString *value1 = [bundle localizedStringForKey:key value:value table:nil]; | ||
| 28 | + return value1; | ||
| 29 | +} | ||
| 30 | + | ||
| 31 | +@end |
CNLiveImagePickerController/TZAssetCell.h
0 → 100755
| 1 | +// | ||
| 2 | +// TZAssetCell.h | ||
| 3 | +// TZImagePickerController | ||
| 4 | +// | ||
| 5 | +// Created by 谭真 on 15/12/24. | ||
| 6 | +// Copyright © 2015年 谭真. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import <UIKit/UIKit.h> | ||
| 10 | +#import <Photos/Photos.h> | ||
| 11 | + | ||
| 12 | +typedef enum : NSUInteger { | ||
| 13 | + TZAssetCellTypePhoto = 0, | ||
| 14 | + TZAssetCellTypeLivePhoto, | ||
| 15 | + TZAssetCellTypePhotoGif, | ||
| 16 | + TZAssetCellTypeVideo, | ||
| 17 | + TZAssetCellTypeAudio, | ||
| 18 | +} TZAssetCellType; | ||
| 19 | + | ||
| 20 | +@class TZAssetModel; | ||
| 21 | +@interface TZAssetCell : UICollectionViewCell | ||
| 22 | +@property (weak, nonatomic) UIButton *selectPhotoButton; | ||
| 23 | +@property (weak, nonatomic) UIButton *cannotSelectLayerButton; | ||
| 24 | +@property (nonatomic, strong) TZAssetModel *model; | ||
| 25 | +@property (assign, nonatomic) NSInteger index; | ||
| 26 | +@property (nonatomic, copy) void (^didSelectPhotoBlock)(BOOL); | ||
| 27 | +@property (nonatomic, assign) TZAssetCellType type; | ||
| 28 | +@property (nonatomic, assign) BOOL allowPickingGif; | ||
| 29 | +@property (nonatomic, assign) BOOL allowPickingMultipleVideo; | ||
| 30 | +@property (nonatomic, copy) NSString *representedAssetIdentifier; | ||
| 31 | +@property (nonatomic, assign) int32_t imageRequestID; | ||
| 32 | + | ||
| 33 | +@property (nonatomic, strong) UIImage *photoSelImage; | ||
| 34 | +@property (nonatomic, strong) UIImage *photoDefImage; | ||
| 35 | + | ||
| 36 | +@property (nonatomic, assign) BOOL showSelectBtn; | ||
| 37 | +@property (assign, nonatomic) BOOL allowPreview; | ||
| 38 | +@property (assign, nonatomic) BOOL useCachedImage; | ||
| 39 | + | ||
| 40 | +@property (nonatomic, copy) void (^assetCellDidSetModelBlock)(TZAssetCell *cell, UIImageView *imageView, UIImageView *selectImageView, UILabel *indexLabel, UIView *bottomView, UILabel *timeLength, UIImageView *videoImgView); | ||
| 41 | +@property (nonatomic, copy) void (^assetCellDidLayoutSubviewsBlock)(TZAssetCell *cell, UIImageView *imageView, UIImageView *selectImageView, UILabel *indexLabel, UIView *bottomView, UILabel *timeLength, UIImageView *videoImgView); | ||
| 42 | +@end | ||
| 43 | + | ||
| 44 | + | ||
| 45 | +@class TZAlbumModel; | ||
| 46 | +@interface TZAlbumCell : UITableViewCell | ||
| 47 | +@property (nonatomic, strong) TZAlbumModel *model; | ||
| 48 | +@property (weak, nonatomic) UIButton *selectedCountButton; | ||
| 49 | + | ||
| 50 | +@property (nonatomic, copy) void (^albumCellDidSetModelBlock)(TZAlbumCell *cell, UIImageView *posterImageView, UILabel *titleLabel); | ||
| 51 | +@property (nonatomic, copy) void (^albumCellDidLayoutSubviewsBlock)(TZAlbumCell *cell, UIImageView *posterImageView, UILabel *titleLabel); | ||
| 52 | +@end | ||
| 53 | + | ||
| 54 | + | ||
| 55 | +@interface TZAssetCameraCell : UICollectionViewCell | ||
| 56 | +@property (nonatomic, strong) UIImageView *imageView; | ||
| 57 | +@end |
CNLiveImagePickerController/TZAssetCell.m
0 → 100755
| 1 | +// | ||
| 2 | +// TZAssetCell.m | ||
| 3 | +// TZImagePickerController | ||
| 4 | +// | ||
| 5 | +// Created by 谭真 on 15/12/24. | ||
| 6 | +// Copyright © 2015年 谭真. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import "TZAssetCell.h" | ||
| 10 | +#import "TZAssetModel.h" | ||
| 11 | +#import "UIView+Layout.h" | ||
| 12 | +#import "TZImageManager.h" | ||
| 13 | +#import "TZImagePickerController.h" | ||
| 14 | +#import "TZProgressView.h" | ||
| 15 | + | ||
| 16 | +@interface TZAssetCell () | ||
| 17 | +@property (weak, nonatomic) UIImageView *imageView; // The photo / 照片 | ||
| 18 | +@property (weak, nonatomic) UIImageView *selectImageView; | ||
| 19 | +@property (weak, nonatomic) UILabel *indexLabel; | ||
| 20 | +@property (weak, nonatomic) UIView *bottomView; | ||
| 21 | +@property (weak, nonatomic) UILabel *timeLength; | ||
| 22 | +@property (strong, nonatomic) UITapGestureRecognizer *tapGesture; | ||
| 23 | + | ||
| 24 | +@property (nonatomic, weak) UIImageView *videoImgView; | ||
| 25 | +@property (nonatomic, strong) TZProgressView *progressView; | ||
| 26 | +@property (nonatomic, assign) int32_t bigImageRequestID; | ||
| 27 | +@end | ||
| 28 | + | ||
| 29 | +@implementation TZAssetCell | ||
| 30 | + | ||
| 31 | +- (void)setModel:(TZAssetModel *)model { | ||
| 32 | + _model = model; | ||
| 33 | + if (iOS8Later) { | ||
| 34 | + self.representedAssetIdentifier = [[TZImageManager manager] getAssetIdentifier:model.asset]; | ||
| 35 | + } | ||
| 36 | + if (self.useCachedImage && model.cachedImage) { | ||
| 37 | + self.imageView.image = model.cachedImage; | ||
| 38 | + } else { | ||
| 39 | + self.model.cachedImage = nil; | ||
| 40 | + int32_t imageRequestID = [[TZImageManager manager] getPhotoWithAsset:model.asset photoWidth:self.tz_width completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) { | ||
| 41 | + // Set the cell's thumbnail image if it's still showing the same asset. | ||
| 42 | + if (!iOS8Later) { | ||
| 43 | + self.imageView.image = photo; | ||
| 44 | + self.model.cachedImage = photo; | ||
| 45 | + [self hideProgressView]; | ||
| 46 | + return; | ||
| 47 | + } | ||
| 48 | + if ([self.representedAssetIdentifier isEqualToString:[[TZImageManager manager] getAssetIdentifier:model.asset]]) { | ||
| 49 | + self.imageView.image = photo; | ||
| 50 | + self.model.cachedImage = photo; | ||
| 51 | + } else { | ||
| 52 | + // NSLog(@"this cell is showing other asset"); | ||
| 53 | + [[PHImageManager defaultManager] cancelImageRequest:self.imageRequestID]; | ||
| 54 | + } | ||
| 55 | + if (!isDegraded) { | ||
| 56 | + [self hideProgressView]; | ||
| 57 | + self.imageRequestID = 0; | ||
| 58 | + } | ||
| 59 | + } progressHandler:nil networkAccessAllowed:NO]; | ||
| 60 | + if (imageRequestID && self.imageRequestID && imageRequestID != self.imageRequestID) { | ||
| 61 | + [[PHImageManager defaultManager] cancelImageRequest:self.imageRequestID]; | ||
| 62 | + // NSLog(@"cancelImageRequest %d",self.imageRequestID); | ||
| 63 | + } | ||
| 64 | + self.imageRequestID = imageRequestID; | ||
| 65 | + } | ||
| 66 | + self.selectPhotoButton.selected = model.isSelected; | ||
| 67 | + self.selectImageView.image = self.selectPhotoButton.isSelected ? self.photoSelImage : self.photoDefImage; | ||
| 68 | + self.indexLabel.hidden = !self.selectPhotoButton.isSelected; | ||
| 69 | + | ||
| 70 | + self.type = (NSInteger)model.type; | ||
| 71 | + // 让宽度/高度小于 最小可选照片尺寸 的图片不能选中 | ||
| 72 | + if (![[TZImageManager manager] isPhotoSelectableWithAsset:model.asset]) { | ||
| 73 | + if (_selectImageView.hidden == NO) { | ||
| 74 | + self.selectPhotoButton.hidden = YES; | ||
| 75 | + _selectImageView.hidden = YES; | ||
| 76 | + } | ||
| 77 | + } | ||
| 78 | + // 如果用户选中了该图片,提前获取一下大图 | ||
| 79 | + if (model.isSelected) { | ||
| 80 | + [self requestBigImage]; | ||
| 81 | + } else { | ||
| 82 | + [self cancelBigImageRequest]; | ||
| 83 | + } | ||
| 84 | + if (model.needOscillatoryAnimation) { | ||
| 85 | + [UIView showOscillatoryAnimationWithLayer:self.selectImageView.layer type:TZOscillatoryAnimationToBigger]; | ||
| 86 | + } | ||
| 87 | + model.needOscillatoryAnimation = NO; | ||
| 88 | + [self setNeedsLayout]; | ||
| 89 | + | ||
| 90 | + if (self.assetCellDidSetModelBlock) { | ||
| 91 | + self.assetCellDidSetModelBlock(self, _imageView, _selectImageView, _indexLabel, _bottomView, _timeLength, _videoImgView); | ||
| 92 | + } | ||
| 93 | +} | ||
| 94 | + | ||
| 95 | +- (void)setIndex:(NSInteger)index { | ||
| 96 | + _index = index; | ||
| 97 | + self.indexLabel.text = [NSString stringWithFormat:@"%zd", index]; | ||
| 98 | + [self.contentView bringSubviewToFront:self.indexLabel]; | ||
| 99 | +} | ||
| 100 | + | ||
| 101 | +- (void)setShowSelectBtn:(BOOL)showSelectBtn { | ||
| 102 | + _showSelectBtn = showSelectBtn; | ||
| 103 | + BOOL selectable = [[TZImageManager manager] isPhotoSelectableWithAsset:self.model.asset]; | ||
| 104 | + if (!self.selectPhotoButton.hidden) { | ||
| 105 | + self.selectPhotoButton.hidden = !showSelectBtn || !selectable; | ||
| 106 | + } | ||
| 107 | + if (!self.selectImageView.hidden) { | ||
| 108 | + self.selectImageView.hidden = !showSelectBtn || !selectable; | ||
| 109 | + } | ||
| 110 | +} | ||
| 111 | + | ||
| 112 | +- (void)setType:(TZAssetCellType)type { | ||
| 113 | + _type = type; | ||
| 114 | + if (type == TZAssetCellTypePhoto || type == TZAssetCellTypeLivePhoto || (type == TZAssetCellTypePhotoGif && !self.allowPickingGif) || self.allowPickingMultipleVideo) { | ||
| 115 | + _selectImageView.hidden = NO; | ||
| 116 | + _selectPhotoButton.hidden = NO; | ||
| 117 | + _bottomView.hidden = YES; | ||
| 118 | + } else { // Video of Gif | ||
| 119 | + _selectImageView.hidden = YES; | ||
| 120 | + _selectPhotoButton.hidden = YES; | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + if (type == TZAssetCellTypeVideo) { | ||
| 124 | + self.bottomView.hidden = NO; | ||
| 125 | + self.timeLength.text = _model.timeLength; | ||
| 126 | + self.videoImgView.hidden = NO; | ||
| 127 | + _timeLength.tz_left = self.videoImgView.tz_right; | ||
| 128 | + _timeLength.textAlignment = NSTextAlignmentRight; | ||
| 129 | + } else if (type == TZAssetCellTypePhotoGif && self.allowPickingGif) { | ||
| 130 | + self.bottomView.hidden = NO; | ||
| 131 | + self.timeLength.text = @"GIF"; | ||
| 132 | + self.videoImgView.hidden = YES; | ||
| 133 | + _timeLength.tz_left = 5; | ||
| 134 | + _timeLength.textAlignment = NSTextAlignmentLeft; | ||
| 135 | + } | ||
| 136 | +} | ||
| 137 | + | ||
| 138 | +- (void)setAllowPreview:(BOOL)allowPreview { | ||
| 139 | + _allowPreview = allowPreview; | ||
| 140 | + if (allowPreview) { | ||
| 141 | + _imageView.userInteractionEnabled = NO; | ||
| 142 | + _tapGesture.enabled = NO; | ||
| 143 | + } else { | ||
| 144 | + _imageView.userInteractionEnabled = YES; | ||
| 145 | + _tapGesture.enabled = YES; | ||
| 146 | + } | ||
| 147 | +} | ||
| 148 | + | ||
| 149 | +- (void)selectPhotoButtonClick:(UIButton *)sender { | ||
| 150 | + if (self.didSelectPhotoBlock) { | ||
| 151 | + self.didSelectPhotoBlock(sender.isSelected); | ||
| 152 | + } | ||
| 153 | + self.selectImageView.image = sender.isSelected ? self.photoSelImage : self.photoDefImage; | ||
| 154 | + if (sender.isSelected) { | ||
| 155 | + if (![TZImagePickerConfig sharedInstance].showSelectedIndex && ![TZImagePickerConfig sharedInstance].showPhotoCannotSelectLayer) { | ||
| 156 | + [UIView showOscillatoryAnimationWithLayer:_selectImageView.layer type:TZOscillatoryAnimationToBigger]; | ||
| 157 | + } | ||
| 158 | + // 用户选中了该图片,提前获取一下大图 | ||
| 159 | + [self requestBigImage]; | ||
| 160 | + } else { // 取消选中,取消大图的获取 | ||
| 161 | + [self cancelBigImageRequest]; | ||
| 162 | + } | ||
| 163 | +} | ||
| 164 | + | ||
| 165 | +/// 只在单选状态且allowPreview为NO时会有该事件 | ||
| 166 | +- (void)didTapImageView { | ||
| 167 | + if (self.didSelectPhotoBlock) { | ||
| 168 | + self.didSelectPhotoBlock(NO); | ||
| 169 | + } | ||
| 170 | +} | ||
| 171 | + | ||
| 172 | +- (void)hideProgressView { | ||
| 173 | + if (_progressView) { | ||
| 174 | + self.progressView.hidden = YES; | ||
| 175 | + self.imageView.alpha = 1.0; | ||
| 176 | + } | ||
| 177 | +} | ||
| 178 | + | ||
| 179 | +- (void)requestBigImage { | ||
| 180 | + if (_bigImageRequestID) { | ||
| 181 | + [[PHImageManager defaultManager] cancelImageRequest:_bigImageRequestID]; | ||
| 182 | + } | ||
| 183 | + | ||
| 184 | + _bigImageRequestID = [[TZImageManager manager] requestImageDataForAsset:_model.asset completion:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { | ||
| 185 | + [self hideProgressView]; | ||
| 186 | + } progressHandler:^(double progress, NSError *error, BOOL *stop, NSDictionary *info) { | ||
| 187 | + if (self.model.isSelected) { | ||
| 188 | + progress = progress > 0.02 ? progress : 0.02;; | ||
| 189 | + self.progressView.progress = progress; | ||
| 190 | + self.progressView.hidden = NO; | ||
| 191 | + self.imageView.alpha = 0.4; | ||
| 192 | + if (progress >= 1) { | ||
| 193 | + [self hideProgressView]; | ||
| 194 | + } | ||
| 195 | + } else { | ||
| 196 | + *stop = YES; | ||
| 197 | + [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; | ||
| 198 | + [self cancelBigImageRequest]; | ||
| 199 | + } | ||
| 200 | + }]; | ||
| 201 | +} | ||
| 202 | + | ||
| 203 | +- (void)cancelBigImageRequest { | ||
| 204 | + if (_bigImageRequestID) { | ||
| 205 | + [[PHImageManager defaultManager] cancelImageRequest:_bigImageRequestID]; | ||
| 206 | + } | ||
| 207 | + [self hideProgressView]; | ||
| 208 | +} | ||
| 209 | + | ||
| 210 | +#pragma mark - Lazy load | ||
| 211 | + | ||
| 212 | +- (UIButton *)selectPhotoButton { | ||
| 213 | + if (_selectPhotoButton == nil) { | ||
| 214 | + UIButton *selectPhotoButton = [[UIButton alloc] init]; | ||
| 215 | + [selectPhotoButton addTarget:self action:@selector(selectPhotoButtonClick:) forControlEvents:UIControlEventTouchUpInside]; | ||
| 216 | + [self.contentView addSubview:selectPhotoButton]; | ||
| 217 | + _selectPhotoButton = selectPhotoButton; | ||
| 218 | + } | ||
| 219 | + return _selectPhotoButton; | ||
| 220 | +} | ||
| 221 | + | ||
| 222 | +- (UIImageView *)imageView { | ||
| 223 | + if (_imageView == nil) { | ||
| 224 | + UIImageView *imageView = [[UIImageView alloc] init]; | ||
| 225 | + imageView.contentMode = UIViewContentModeScaleAspectFill; | ||
| 226 | + imageView.clipsToBounds = YES; | ||
| 227 | + [self.contentView addSubview:imageView]; | ||
| 228 | + _imageView = imageView; | ||
| 229 | + | ||
| 230 | + _tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapImageView)]; | ||
| 231 | + [_imageView addGestureRecognizer:_tapGesture]; | ||
| 232 | + } | ||
| 233 | + return _imageView; | ||
| 234 | +} | ||
| 235 | + | ||
| 236 | +- (UIImageView *)selectImageView { | ||
| 237 | + if (_selectImageView == nil) { | ||
| 238 | + UIImageView *selectImageView = [[UIImageView alloc] init]; | ||
| 239 | + selectImageView.contentMode = UIViewContentModeCenter; | ||
| 240 | + selectImageView.clipsToBounds = YES; | ||
| 241 | + [self.contentView addSubview:selectImageView]; | ||
| 242 | + _selectImageView = selectImageView; | ||
| 243 | + } | ||
| 244 | + return _selectImageView; | ||
| 245 | +} | ||
| 246 | + | ||
| 247 | +- (UIView *)bottomView { | ||
| 248 | + if (_bottomView == nil) { | ||
| 249 | + UIView *bottomView = [[UIView alloc] init]; | ||
| 250 | + static NSInteger rgb = 0; | ||
| 251 | + bottomView.backgroundColor = [UIColor colorWithRed:rgb green:rgb blue:rgb alpha:0.8]; | ||
| 252 | + [self.contentView addSubview:bottomView]; | ||
| 253 | + _bottomView = bottomView; | ||
| 254 | + } | ||
| 255 | + return _bottomView; | ||
| 256 | +} | ||
| 257 | + | ||
| 258 | +- (UIButton *)cannotSelectLayerButton { | ||
| 259 | + if (_cannotSelectLayerButton == nil) { | ||
| 260 | + UIButton *cannotSelectLayerButton = [[UIButton alloc] init]; | ||
| 261 | + [self.contentView addSubview:cannotSelectLayerButton]; | ||
| 262 | + _cannotSelectLayerButton = cannotSelectLayerButton; | ||
| 263 | + } | ||
| 264 | + return _cannotSelectLayerButton; | ||
| 265 | +} | ||
| 266 | + | ||
| 267 | +- (UIImageView *)videoImgView { | ||
| 268 | + if (_videoImgView == nil) { | ||
| 269 | + UIImageView *videoImgView = [[UIImageView alloc] init]; | ||
| 270 | + [videoImgView setImage:[UIImage imageNamedFromMyBundle:@"VideoSendIcon"]]; | ||
| 271 | + [self.bottomView addSubview:videoImgView]; | ||
| 272 | + _videoImgView = videoImgView; | ||
| 273 | + } | ||
| 274 | + return _videoImgView; | ||
| 275 | +} | ||
| 276 | + | ||
| 277 | +- (UILabel *)timeLength { | ||
| 278 | + if (_timeLength == nil) { | ||
| 279 | + UILabel *timeLength = [[UILabel alloc] init]; | ||
| 280 | + timeLength.font = [UIFont boldSystemFontOfSize:11]; | ||
| 281 | + timeLength.textColor = [UIColor whiteColor]; | ||
| 282 | + timeLength.textAlignment = NSTextAlignmentRight; | ||
| 283 | + [self.bottomView addSubview:timeLength]; | ||
| 284 | + _timeLength = timeLength; | ||
| 285 | + } | ||
| 286 | + return _timeLength; | ||
| 287 | +} | ||
| 288 | + | ||
| 289 | +- (UILabel *)indexLabel { | ||
| 290 | + if (_indexLabel == nil) { | ||
| 291 | + UILabel *indexLabel = [[UILabel alloc] init]; | ||
| 292 | + indexLabel.font = [UIFont systemFontOfSize:14]; | ||
| 293 | + indexLabel.textColor = [UIColor whiteColor]; | ||
| 294 | + indexLabel.textAlignment = NSTextAlignmentCenter; | ||
| 295 | + [self.contentView addSubview:indexLabel]; | ||
| 296 | + _indexLabel = indexLabel; | ||
| 297 | + } | ||
| 298 | + return _indexLabel; | ||
| 299 | +} | ||
| 300 | + | ||
| 301 | +- (TZProgressView *)progressView { | ||
| 302 | + if (_progressView == nil) { | ||
| 303 | + _progressView = [[TZProgressView alloc] init]; | ||
| 304 | + _progressView.hidden = YES; | ||
| 305 | + [self addSubview:_progressView]; | ||
| 306 | + } | ||
| 307 | + return _progressView; | ||
| 308 | +} | ||
| 309 | + | ||
| 310 | +- (void)layoutSubviews { | ||
| 311 | + [super layoutSubviews]; | ||
| 312 | + _cannotSelectLayerButton.frame = self.bounds; | ||
| 313 | + if (self.allowPreview) { | ||
| 314 | + _selectPhotoButton.frame = CGRectMake(self.tz_width - 44, 0, 44, 44); | ||
| 315 | + } else { | ||
| 316 | + _selectPhotoButton.frame = self.bounds; | ||
| 317 | + } | ||
| 318 | + _selectImageView.frame = CGRectMake(self.tz_width - 27, 3, 24, 24); | ||
| 319 | + if (_selectImageView.image.size.width <= 27) { | ||
| 320 | + _selectImageView.contentMode = UIViewContentModeCenter; | ||
| 321 | + } else { | ||
| 322 | + _selectImageView.contentMode = UIViewContentModeScaleAspectFit; | ||
| 323 | + } | ||
| 324 | + _indexLabel.frame = _selectImageView.frame; | ||
| 325 | + _imageView.frame = CGRectMake(0, 0, self.tz_width, self.tz_height); | ||
| 326 | + | ||
| 327 | + static CGFloat progressWH = 20; | ||
| 328 | + CGFloat progressXY = (self.tz_width - progressWH) / 2; | ||
| 329 | + _progressView.frame = CGRectMake(progressXY, progressXY, progressWH, progressWH); | ||
| 330 | + | ||
| 331 | + _bottomView.frame = CGRectMake(0, self.tz_height - 17, self.tz_width, 17); | ||
| 332 | + _videoImgView.frame = CGRectMake(8, 0, 17, 17); | ||
| 333 | + _timeLength.frame = CGRectMake(self.videoImgView.tz_right, 0, self.tz_width - self.videoImgView.tz_right - 5, 17); | ||
| 334 | + | ||
| 335 | + self.type = (NSInteger)self.model.type; | ||
| 336 | + self.showSelectBtn = self.showSelectBtn; | ||
| 337 | + | ||
| 338 | + [self.contentView bringSubviewToFront:_bottomView]; | ||
| 339 | + [self.contentView bringSubviewToFront:_cannotSelectLayerButton]; | ||
| 340 | + [self.contentView bringSubviewToFront:_selectPhotoButton]; | ||
| 341 | + [self.contentView bringSubviewToFront:_selectImageView]; | ||
| 342 | + [self.contentView bringSubviewToFront:_indexLabel]; | ||
| 343 | + | ||
| 344 | + if (self.assetCellDidLayoutSubviewsBlock) { | ||
| 345 | + self.assetCellDidLayoutSubviewsBlock(self, _imageView, _selectImageView, _indexLabel, _bottomView, _timeLength, _videoImgView); | ||
| 346 | + } | ||
| 347 | +} | ||
| 348 | + | ||
| 349 | +@end | ||
| 350 | + | ||
| 351 | +@interface TZAlbumCell () | ||
| 352 | +@property (weak, nonatomic) UIImageView *posterImageView; | ||
| 353 | +@property (weak, nonatomic) UILabel *titleLabel; | ||
| 354 | +@end | ||
| 355 | + | ||
| 356 | +@implementation TZAlbumCell | ||
| 357 | + | ||
| 358 | +- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { | ||
| 359 | + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; | ||
| 360 | + self.accessoryType = UITableViewCellAccessoryDisclosureIndicator; | ||
| 361 | + return self; | ||
| 362 | +} | ||
| 363 | + | ||
| 364 | +- (void)setModel:(TZAlbumModel *)model { | ||
| 365 | + _model = model; | ||
| 366 | + | ||
| 367 | + NSMutableAttributedString *nameString = [[NSMutableAttributedString alloc] initWithString:model.name attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16],NSForegroundColorAttributeName:[UIColor blackColor]}]; | ||
| 368 | + NSAttributedString *countString = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@" (%zd)",model.count] attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16],NSForegroundColorAttributeName:[UIColor lightGrayColor]}]; | ||
| 369 | + [nameString appendAttributedString:countString]; | ||
| 370 | + self.titleLabel.attributedText = nameString; | ||
| 371 | + [[TZImageManager manager] getPostImageWithAlbumModel:model completion:^(UIImage *postImage) { | ||
| 372 | + self.posterImageView.image = postImage; | ||
| 373 | + }]; | ||
| 374 | + if (model.selectedCount) { | ||
| 375 | + self.selectedCountButton.hidden = NO; | ||
| 376 | + [self.selectedCountButton setTitle:[NSString stringWithFormat:@"%zd",model.selectedCount] forState:UIControlStateNormal]; | ||
| 377 | + } else { | ||
| 378 | + self.selectedCountButton.hidden = YES; | ||
| 379 | + } | ||
| 380 | + | ||
| 381 | + if (self.albumCellDidSetModelBlock) { | ||
| 382 | + self.albumCellDidSetModelBlock(self, _posterImageView, _titleLabel); | ||
| 383 | + } | ||
| 384 | +} | ||
| 385 | + | ||
| 386 | +/// For fitting iOS6 | ||
| 387 | +- (void)layoutSubviews { | ||
| 388 | + if (iOS7Later) [super layoutSubviews]; | ||
| 389 | + _selectedCountButton.frame = CGRectMake(self.tz_width - 24 - 30, 23, 24, 24); | ||
| 390 | + NSInteger titleHeight = ceil(self.titleLabel.font.lineHeight); | ||
| 391 | + self.titleLabel.frame = CGRectMake(80, (self.tz_height - titleHeight) / 2, self.tz_width - 80 - 50, titleHeight); | ||
| 392 | + self.posterImageView.frame = CGRectMake(0, 0, 70, 70); | ||
| 393 | + | ||
| 394 | + if (self.albumCellDidLayoutSubviewsBlock) { | ||
| 395 | + self.albumCellDidLayoutSubviewsBlock(self, _posterImageView, _titleLabel); | ||
| 396 | + } | ||
| 397 | +} | ||
| 398 | + | ||
| 399 | +- (void)layoutSublayersOfLayer:(CALayer *)layer { | ||
| 400 | + if (iOS7Later) [super layoutSublayersOfLayer:layer]; | ||
| 401 | +} | ||
| 402 | + | ||
| 403 | +#pragma mark - Lazy load | ||
| 404 | + | ||
| 405 | +- (UIImageView *)posterImageView { | ||
| 406 | + if (_posterImageView == nil) { | ||
| 407 | + UIImageView *posterImageView = [[UIImageView alloc] init]; | ||
| 408 | + posterImageView.contentMode = UIViewContentModeScaleAspectFill; | ||
| 409 | + posterImageView.clipsToBounds = YES; | ||
| 410 | + [self.contentView addSubview:posterImageView]; | ||
| 411 | + _posterImageView = posterImageView; | ||
| 412 | + } | ||
| 413 | + return _posterImageView; | ||
| 414 | +} | ||
| 415 | + | ||
| 416 | +- (UILabel *)titleLabel { | ||
| 417 | + if (_titleLabel == nil) { | ||
| 418 | + UILabel *titleLabel = [[UILabel alloc] init]; | ||
| 419 | + titleLabel.font = [UIFont boldSystemFontOfSize:17]; | ||
| 420 | + titleLabel.textColor = [UIColor blackColor]; | ||
| 421 | + titleLabel.textAlignment = NSTextAlignmentLeft; | ||
| 422 | + [self.contentView addSubview:titleLabel]; | ||
| 423 | + _titleLabel = titleLabel; | ||
| 424 | + } | ||
| 425 | + return _titleLabel; | ||
| 426 | +} | ||
| 427 | + | ||
| 428 | +- (UIButton *)selectedCountButton { | ||
| 429 | + if (_selectedCountButton == nil) { | ||
| 430 | + UIButton *selectedCountButton = [[UIButton alloc] init]; | ||
| 431 | + selectedCountButton.layer.cornerRadius = 12; | ||
| 432 | + selectedCountButton.clipsToBounds = YES; | ||
| 433 | + selectedCountButton.backgroundColor = [UIColor redColor]; | ||
| 434 | + [selectedCountButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; | ||
| 435 | + selectedCountButton.titleLabel.font = [UIFont systemFontOfSize:15]; | ||
| 436 | + [self.contentView addSubview:selectedCountButton]; | ||
| 437 | + _selectedCountButton = selectedCountButton; | ||
| 438 | + } | ||
| 439 | + return _selectedCountButton; | ||
| 440 | +} | ||
| 441 | + | ||
| 442 | +@end | ||
| 443 | + | ||
| 444 | + | ||
| 445 | + | ||
| 446 | +@implementation TZAssetCameraCell | ||
| 447 | + | ||
| 448 | +- (instancetype)initWithFrame:(CGRect)frame { | ||
| 449 | + self = [super initWithFrame:frame]; | ||
| 450 | + if (self) { | ||
| 451 | + self.backgroundColor = [UIColor whiteColor]; | ||
| 452 | + _imageView = [[UIImageView alloc] init]; | ||
| 453 | + _imageView.backgroundColor = [UIColor colorWithWhite:1.000 alpha:0.500]; | ||
| 454 | + _imageView.contentMode = UIViewContentModeScaleAspectFill; | ||
| 455 | + [self.contentView addSubview:_imageView]; | ||
| 456 | + self.clipsToBounds = YES; | ||
| 457 | + } | ||
| 458 | + return self; | ||
| 459 | +} | ||
| 460 | + | ||
| 461 | +- (void)layoutSubviews { | ||
| 462 | + [super layoutSubviews]; | ||
| 463 | + _imageView.frame = self.bounds; | ||
| 464 | +} | ||
| 465 | + | ||
| 466 | +@end |
CNLiveImagePickerController/TZAssetModel.h
0 → 100755
| 1 | +// | ||
| 2 | +// TZAssetModel.h | ||
| 3 | +// TZImagePickerController | ||
| 4 | +// | ||
| 5 | +// Created by 谭真 on 15/12/24. | ||
| 6 | +// Copyright © 2015年 谭真. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import <Foundation/Foundation.h> | ||
| 10 | +#import <UIKit/UIKit.h> | ||
| 11 | + | ||
| 12 | +typedef enum : NSUInteger { | ||
| 13 | + TZAssetModelMediaTypePhoto = 0, | ||
| 14 | + TZAssetModelMediaTypeLivePhoto, | ||
| 15 | + TZAssetModelMediaTypePhotoGif, | ||
| 16 | + TZAssetModelMediaTypeVideo, | ||
| 17 | + TZAssetModelMediaTypeAudio | ||
| 18 | +} TZAssetModelMediaType; | ||
| 19 | + | ||
| 20 | +@class PHAsset; | ||
| 21 | +@interface TZAssetModel : NSObject | ||
| 22 | + | ||
| 23 | +@property (nonatomic, strong) id asset; ///< PHAsset or ALAsset | ||
| 24 | +@property (nonatomic, assign) BOOL isSelected; ///< The select status of a photo, default is No | ||
| 25 | +@property (nonatomic, assign) TZAssetModelMediaType type; | ||
| 26 | +@property (assign, nonatomic) BOOL needOscillatoryAnimation; | ||
| 27 | +@property (nonatomic, copy) NSString *timeLength; | ||
| 28 | +@property (strong, nonatomic) UIImage *cachedImage; | ||
| 29 | + | ||
| 30 | +/// Init a photo dataModel With a asset | ||
| 31 | +/// 用一个PHAsset/ALAsset实例,初始化一个照片模型 | ||
| 32 | ++ (instancetype)modelWithAsset:(id)asset type:(TZAssetModelMediaType)type; | ||
| 33 | ++ (instancetype)modelWithAsset:(id)asset type:(TZAssetModelMediaType)type timeLength:(NSString *)timeLength; | ||
| 34 | + | ||
| 35 | +@end | ||
| 36 | + | ||
| 37 | + | ||
| 38 | +@class PHFetchResult; | ||
| 39 | +@interface TZAlbumModel : NSObject | ||
| 40 | + | ||
| 41 | +@property (nonatomic, strong) NSString *name; ///< The album name | ||
| 42 | +@property (nonatomic, assign) NSInteger count; ///< Count of photos the album contain | ||
| 43 | +@property (nonatomic, strong) id result; ///< PHFetchResult<PHAsset> or ALAssetsGroup<ALAsset> | ||
| 44 | + | ||
| 45 | +@property (nonatomic, strong) NSArray *models; | ||
| 46 | +@property (nonatomic, strong) NSArray *selectedModels; | ||
| 47 | +@property (nonatomic, assign) NSUInteger selectedCount; | ||
| 48 | + | ||
| 49 | +@property (nonatomic, assign) BOOL isCameraRoll; | ||
| 50 | + | ||
| 51 | +- (void)setResult:(id)result needFetchAssets:(BOOL)needFetchAssets; | ||
| 52 | + | ||
| 53 | +@end |
CNLiveImagePickerController/TZAssetModel.m
0 → 100755
| 1 | +// | ||
| 2 | +// TZAssetModel.m | ||
| 3 | +// TZImagePickerController | ||
| 4 | +// | ||
| 5 | +// Created by 谭真 on 15/12/24. | ||
| 6 | +// Copyright © 2015年 谭真. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import "TZAssetModel.h" | ||
| 10 | +#import "TZImageManager.h" | ||
| 11 | + | ||
| 12 | +@implementation TZAssetModel | ||
| 13 | + | ||
| 14 | ++ (instancetype)modelWithAsset:(id)asset type:(TZAssetModelMediaType)type{ | ||
| 15 | + TZAssetModel *model = [[TZAssetModel alloc] init]; | ||
| 16 | + model.asset = asset; | ||
| 17 | + model.isSelected = NO; | ||
| 18 | + model.type = type; | ||
| 19 | + return model; | ||
| 20 | +} | ||
| 21 | + | ||
| 22 | ++ (instancetype)modelWithAsset:(id)asset type:(TZAssetModelMediaType)type timeLength:(NSString *)timeLength { | ||
| 23 | + TZAssetModel *model = [self modelWithAsset:asset type:type]; | ||
| 24 | + model.timeLength = timeLength; | ||
| 25 | + return model; | ||
| 26 | +} | ||
| 27 | + | ||
| 28 | +@end | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | +@implementation TZAlbumModel | ||
| 33 | + | ||
| 34 | +- (void)setResult:(id)result needFetchAssets:(BOOL)needFetchAssets { | ||
| 35 | + _result = result; | ||
| 36 | + if (needFetchAssets) { | ||
| 37 | + [[TZImageManager manager] getAssetsFromFetchResult:result completion:^(NSArray<TZAssetModel *> *models) { | ||
| 38 | + self->_models = models; | ||
| 39 | + if (self->_selectedModels) { | ||
| 40 | + [self checkSelectedModels]; | ||
| 41 | + } | ||
| 42 | + }]; | ||
| 43 | + } | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +- (void)setSelectedModels:(NSArray *)selectedModels { | ||
| 47 | + _selectedModels = selectedModels; | ||
| 48 | + if (_models) { | ||
| 49 | + [self checkSelectedModels]; | ||
| 50 | + } | ||
| 51 | +} | ||
| 52 | + | ||
| 53 | +- (void)checkSelectedModels { | ||
| 54 | + self.selectedCount = 0; | ||
| 55 | + NSMutableArray *selectedAssets = [NSMutableArray array]; | ||
| 56 | + for (TZAssetModel *model in _selectedModels) { | ||
| 57 | + [selectedAssets addObject:model.asset]; | ||
| 58 | + } | ||
| 59 | + for (TZAssetModel *model in _models) { | ||
| 60 | + if ([[TZImageManager manager] isAssetsArray:selectedAssets containAsset:model.asset]) { | ||
| 61 | + self.selectedCount ++; | ||
| 62 | + } | ||
| 63 | + } | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +- (NSString *)name { | ||
| 67 | + if (_name) { | ||
| 68 | + return _name; | ||
| 69 | + } | ||
| 70 | + return @""; | ||
| 71 | +} | ||
| 72 | + | ||
| 73 | +@end |
CNLiveImagePickerController/TZGifPhotoPreviewController.h
0 → 100755
| 1 | +// | ||
| 2 | +// TZGifPhotoPreviewController.h | ||
| 3 | +// TZImagePickerController | ||
| 4 | +// | ||
| 5 | +// Created by ttouch on 2016/12/13. | ||
| 6 | +// Copyright © 2016年 谭真. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import <UIKit/UIKit.h> | ||
| 10 | + | ||
| 11 | +@class TZAssetModel; | ||
| 12 | +@interface TZGifPhotoPreviewController : UIViewController | ||
| 13 | + | ||
| 14 | +@property (nonatomic, strong) TZAssetModel *model; | ||
| 15 | + | ||
| 16 | +@end |
CNLiveImagePickerController/TZGifPhotoPreviewController.m
0 → 100755
| 1 | +// | ||
| 2 | +// TZGifPhotoPreviewController.m | ||
| 3 | +// TZImagePickerController | ||
| 4 | +// | ||
| 5 | +// Created by ttouch on 2016/12/13. | ||
| 6 | +// Copyright © 2016年 谭真. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import "TZGifPhotoPreviewController.h" | ||
| 10 | +#import "TZImagePickerController.h" | ||
| 11 | +#import "TZAssetModel.h" | ||
| 12 | +#import "UIView+Layout.h" | ||
| 13 | +#import "TZPhotoPreviewCell.h" | ||
| 14 | +#import "TZImageManager.h" | ||
| 15 | + | ||
| 16 | +#pragma clang diagnostic push | ||
| 17 | +#pragma clang diagnostic ignored "-Wdeprecated-declarations" | ||
| 18 | + | ||
| 19 | +@interface TZGifPhotoPreviewController () { | ||
| 20 | + UIView *_toolBar; | ||
| 21 | + UIButton *_doneButton; | ||
| 22 | + UIProgressView *_progress; | ||
| 23 | + | ||
| 24 | + TZPhotoPreviewView *_previewView; | ||
| 25 | + | ||
| 26 | + UIStatusBarStyle _originStatusBarStyle; | ||
| 27 | +} | ||
| 28 | +@end | ||
| 29 | + | ||
| 30 | +@implementation TZGifPhotoPreviewController | ||
| 31 | + | ||
| 32 | +- (void)viewDidLoad { | ||
| 33 | + [super viewDidLoad]; | ||
| 34 | + self.view.backgroundColor = [UIColor blackColor]; | ||
| 35 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | ||
| 36 | + if (tzImagePickerVc) { | ||
| 37 | + self.navigationItem.title = [NSString stringWithFormat:@"GIF %@",tzImagePickerVc.previewBtnTitleStr]; | ||
| 38 | + } | ||
| 39 | + [self configPreviewView]; | ||
| 40 | + [self configBottomToolBar]; | ||
| 41 | +} | ||
| 42 | + | ||
| 43 | +- (void)viewWillAppear:(BOOL)animated { | ||
| 44 | + [super viewWillAppear:animated]; | ||
| 45 | + _originStatusBarStyle = [UIApplication sharedApplication].statusBarStyle; | ||
| 46 | + [UIApplication sharedApplication].statusBarStyle = iOS7Later ? UIStatusBarStyleLightContent : UIStatusBarStyleBlackOpaque; | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | +- (void)viewWillDisappear:(BOOL)animated { | ||
| 50 | + [super viewWillDisappear:animated]; | ||
| 51 | + [UIApplication sharedApplication].statusBarStyle = _originStatusBarStyle; | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +- (void)configPreviewView { | ||
| 55 | + _previewView = [[TZPhotoPreviewView alloc] initWithFrame:CGRectZero]; | ||
| 56 | + _previewView.model = self.model; | ||
| 57 | + __weak typeof(self) weakSelf = self; | ||
| 58 | + [_previewView setSingleTapGestureBlock:^{ | ||
| 59 | + __strong typeof(weakSelf) strongSelf = weakSelf; | ||
| 60 | + [strongSelf signleTapAction]; | ||
| 61 | + }]; | ||
| 62 | + [self.view addSubview:_previewView]; | ||
| 63 | +} | ||
| 64 | + | ||
| 65 | +- (void)configBottomToolBar { | ||
| 66 | + _toolBar = [[UIView alloc] initWithFrame:CGRectZero]; | ||
| 67 | + CGFloat rgb = 34 / 255.0; | ||
| 68 | + _toolBar.backgroundColor = [UIColor colorWithRed:rgb green:rgb blue:rgb alpha:0.7]; | ||
| 69 | + | ||
| 70 | + _doneButton = [UIButton buttonWithType:UIButtonTypeCustom]; | ||
| 71 | + _doneButton.titleLabel.font = [UIFont systemFontOfSize:16]; | ||
| 72 | + [_doneButton addTarget:self action:@selector(doneButtonClick) forControlEvents:UIControlEventTouchUpInside]; | ||
| 73 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | ||
| 74 | + if (tzImagePickerVc) { | ||
| 75 | + [_doneButton setTitle:tzImagePickerVc.doneBtnTitleStr forState:UIControlStateNormal]; | ||
| 76 | + [_doneButton setTitleColor:tzImagePickerVc.oKButtonTitleColorNormal forState:UIControlStateNormal]; | ||
| 77 | + } else { | ||
| 78 | + [_doneButton setTitle:[NSBundle tz_localizedStringForKey:@"Done"] forState:UIControlStateNormal]; | ||
| 79 | + [_doneButton setTitleColor:[UIColor colorWithRed:(83/255.0) green:(179/255.0) blue:(17/255.0) alpha:1.0] forState:UIControlStateNormal]; | ||
| 80 | + } | ||
| 81 | + [_toolBar addSubview:_doneButton]; | ||
| 82 | + | ||
| 83 | + UILabel *byteLabel = [[UILabel alloc] init]; | ||
| 84 | + byteLabel.textColor = [UIColor whiteColor]; | ||
| 85 | + byteLabel.font = [UIFont systemFontOfSize:13]; | ||
| 86 | + byteLabel.frame = CGRectMake(10, 0, 100, 44); | ||
| 87 | + [[TZImageManager manager] getPhotosBytesWithArray:@[_model] completion:^(NSString *totalBytes) { | ||
| 88 | + byteLabel.text = totalBytes; | ||
| 89 | + }]; | ||
| 90 | + [_toolBar addSubview:byteLabel]; | ||
| 91 | + | ||
| 92 | + [self.view addSubview:_toolBar]; | ||
| 93 | + | ||
| 94 | + if (tzImagePickerVc.gifPreviewPageUIConfigBlock) { | ||
| 95 | + tzImagePickerVc.gifPreviewPageUIConfigBlock(_toolBar, _doneButton); | ||
| 96 | + } | ||
| 97 | +} | ||
| 98 | + | ||
| 99 | +#pragma mark - Layout | ||
| 100 | + | ||
| 101 | +- (void)viewDidLayoutSubviews { | ||
| 102 | + [super viewDidLayoutSubviews]; | ||
| 103 | + | ||
| 104 | + _previewView.frame = self.view.bounds; | ||
| 105 | + _previewView.scrollView.frame = self.view.bounds; | ||
| 106 | + CGFloat toolBarHeight = [TZCommonTools tz_isIPhoneX] ? 44 + (83 - 49) : 44; | ||
| 107 | + _toolBar.frame = CGRectMake(0, self.view.tz_height - toolBarHeight, self.view.tz_width, toolBarHeight); | ||
| 108 | + _doneButton.frame = CGRectMake(self.view.tz_width - 44 - 12, 0, 44, 44); | ||
| 109 | + | ||
| 110 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | ||
| 111 | + if (tzImagePickerVc.gifPreviewPageDidLayoutSubviewsBlock) { | ||
| 112 | + tzImagePickerVc.gifPreviewPageDidLayoutSubviewsBlock(_toolBar, _doneButton); | ||
| 113 | + } | ||
| 114 | +} | ||
| 115 | + | ||
| 116 | +#pragma mark - Click Event | ||
| 117 | + | ||
| 118 | +- (void)signleTapAction { | ||
| 119 | + _toolBar.hidden = !_toolBar.isHidden; | ||
| 120 | + [self.navigationController setNavigationBarHidden:_toolBar.isHidden]; | ||
| 121 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | ||
| 122 | + if (iOS7Later) { | ||
| 123 | + if (_toolBar.isHidden) { | ||
| 124 | + [UIApplication sharedApplication].statusBarHidden = YES; | ||
| 125 | + } else if (tzImagePickerVc.needShowStatusBar) { | ||
| 126 | + [UIApplication sharedApplication].statusBarHidden = NO; | ||
| 127 | + } | ||
| 128 | + } | ||
| 129 | +} | ||
| 130 | + | ||
| 131 | +- (void)doneButtonClick { | ||
| 132 | + if (self.navigationController) { | ||
| 133 | + TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; | ||
| 134 | + if (imagePickerVc.autoDismiss) { | ||
| 135 | + [self.navigationController dismissViewControllerAnimated:YES completion:^{ | ||
| 136 | + [self callDelegateMethod]; | ||
| 137 | + }]; | ||
| 138 | + } else { | ||
| 139 | + [self callDelegateMethod]; | ||
| 140 | + } | ||
| 141 | + } else { | ||
| 142 | + [self dismissViewControllerAnimated:YES completion:^{ | ||
| 143 | + [self callDelegateMethod]; | ||
| 144 | + }]; | ||
| 145 | + } | ||
| 146 | +} | ||
| 147 | + | ||
| 148 | +- (void)callDelegateMethod { | ||
| 149 | + TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; | ||
| 150 | + UIImage *animatedImage = _previewView.imageView.image; | ||
| 151 | + if ([imagePickerVc.pickerDelegate respondsToSelector:@selector(imagePickerController:didFinishPickingGifImage:sourceAssets:)]) { | ||
| 152 | + [imagePickerVc.pickerDelegate imagePickerController:imagePickerVc didFinishPickingGifImage:animatedImage sourceAssets:_model.asset]; | ||
| 153 | + } | ||
| 154 | + if (imagePickerVc.didFinishPickingGifImageHandle) { | ||
| 155 | + imagePickerVc.didFinishPickingGifImageHandle(animatedImage,_model.asset); | ||
| 156 | + } | ||
| 157 | +} | ||
| 158 | + | ||
| 159 | +#pragma clang diagnostic pop | ||
| 160 | + | ||
| 161 | +@end |
CNLiveImagePickerController/TZImageCropManager.h
0 → 100755
| 1 | +// | ||
| 2 | +// TZImageCropManager.h | ||
| 3 | +// TZImagePickerController | ||
| 4 | +// | ||
| 5 | +// Created by 谭真 on 2016/12/5. | ||
| 6 | +// Copyright © 2016年 谭真. All rights reserved. | ||
| 7 | +// 图片裁剪管理类 | ||
| 8 | + | ||
| 9 | +#import <Foundation/Foundation.h> | ||
| 10 | +#import <UIKit/UIKit.h> | ||
| 11 | + | ||
| 12 | +@interface TZImageCropManager : NSObject | ||
| 13 | + | ||
| 14 | +/// 裁剪框背景的处理 | ||
| 15 | ++ (void)overlayClippingWithView:(UIView *)view cropRect:(CGRect)cropRect containerView:(UIView *)containerView needCircleCrop:(BOOL)needCircleCrop; | ||
| 16 | + | ||
| 17 | +/* | ||
| 18 | + 1.7.2 为了解决多位同学对于图片裁剪的需求,我这两天有空便在研究图片裁剪 | ||
| 19 | + 幸好有tuyou的PhotoTweaks库做参考,裁剪的功能实现起来简单许多 | ||
| 20 | + 该方法和其内部引用的方法基本来自于tuyou的PhotoTweaks库,我做了稍许删减和修改 | ||
| 21 | + 感谢tuyou同学在github开源了优秀的裁剪库PhotoTweaks,表示感谢 | ||
| 22 | + PhotoTweaks库的github链接:https://github.com/itouch2/PhotoTweaks | ||
| 23 | + */ | ||
| 24 | +/// 获得裁剪后的图片 | ||
| 25 | ++ (UIImage *)cropImageView:(UIImageView *)imageView toRect:(CGRect)rect zoomScale:(double)zoomScale containerView:(UIView *)containerView; | ||
| 26 | + | ||
| 27 | +/// 获取圆形图片 | ||
| 28 | ++ (UIImage *)circularClipImage:(UIImage *)image; | ||
| 29 | + | ||
| 30 | +@end | ||
| 31 | + | ||
| 32 | + | ||
| 33 | +/// 该分类的代码来自SDWebImage:https://github.com/rs/SDWebImage | ||
| 34 | +/// 为了防止冲突,我将分类名字和方法名字做了修改 | ||
| 35 | +@interface UIImage (TZGif) | ||
| 36 | + | ||
| 37 | ++ (UIImage *)sd_tz_animatedGIFWithData:(NSData *)data; | ||
| 38 | + | ||
| 39 | +@end |
CNLiveImagePickerController/TZImageCropManager.m
0 → 100755
| 1 | +// | ||
| 2 | +// TZImageCropManager.m | ||
| 3 | +// TZImagePickerController | ||
| 4 | +// | ||
| 5 | +// Created by 谭真 on 2016/12/5. | ||
| 6 | +// Copyright © 2016年 谭真. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import "TZImageCropManager.h" | ||
| 10 | +#import "UIView+Layout.h" | ||
| 11 | +#import <ImageIO/ImageIO.h> | ||
| 12 | +#import "TZImageManager.h" | ||
| 13 | +#import "TZImagePickerController.h" | ||
| 14 | + | ||
| 15 | +@implementation TZImageCropManager | ||
| 16 | + | ||
| 17 | +/// 裁剪框背景的处理 | ||
| 18 | ++ (void)overlayClippingWithView:(UIView *)view cropRect:(CGRect)cropRect containerView:(UIView *)containerView needCircleCrop:(BOOL)needCircleCrop { | ||
| 19 | + UIBezierPath *path= [UIBezierPath bezierPathWithRect:[UIScreen mainScreen].bounds]; | ||
| 20 | + CAShapeLayer *layer = [CAShapeLayer layer]; | ||
| 21 | + if (needCircleCrop) { // 圆形裁剪框 | ||
| 22 | + [path appendPath:[UIBezierPath bezierPathWithArcCenter:containerView.center radius:cropRect.size.width / 2 startAngle:0 endAngle: 2 * M_PI clockwise:NO]]; | ||
| 23 | + } else { // 矩形裁剪框 | ||
| 24 | + [path appendPath:[UIBezierPath bezierPathWithRect:cropRect]]; | ||
| 25 | + } | ||
| 26 | + layer.path = path.CGPath; | ||
| 27 | + layer.fillRule = kCAFillRuleEvenOdd; | ||
| 28 | + layer.fillColor = [[UIColor blackColor] CGColor]; | ||
| 29 | + layer.opacity = 0.5; | ||
| 30 | + [view.layer addSublayer:layer]; | ||
| 31 | +} | ||
| 32 | + | ||
| 33 | +/// 获得裁剪后的图片 | ||
| 34 | ++ (UIImage *)cropImageView:(UIImageView *)imageView toRect:(CGRect)rect zoomScale:(double)zoomScale containerView:(UIView *)containerView { | ||
| 35 | + CGAffineTransform transform = CGAffineTransformIdentity; | ||
| 36 | + // 平移的处理 | ||
| 37 | + CGRect imageViewRect = [imageView convertRect:imageView.bounds toView:containerView]; | ||
| 38 | + CGPoint point = CGPointMake(imageViewRect.origin.x + imageViewRect.size.width / 2, imageViewRect.origin.y + imageViewRect.size.height / 2); | ||
| 39 | + CGFloat xMargin = containerView.tz_width - CGRectGetMaxX(rect) - rect.origin.x; | ||
| 40 | + CGPoint zeroPoint = CGPointMake((CGRectGetWidth(containerView.frame) - xMargin) / 2, containerView.center.y); | ||
| 41 | + CGPoint translation = CGPointMake(point.x - zeroPoint.x, point.y - zeroPoint.y); | ||
| 42 | + transform = CGAffineTransformTranslate(transform, translation.x, translation.y); | ||
| 43 | + // 缩放的处理 | ||
| 44 | + transform = CGAffineTransformScale(transform, zoomScale, zoomScale); | ||
| 45 | + | ||
| 46 | + CGImageRef imageRef = [self newTransformedImage:transform | ||
| 47 | + sourceImage:imageView.image.CGImage | ||
| 48 | + sourceSize:imageView.image.size | ||
| 49 | + outputWidth:rect.size.width * [UIScreen mainScreen].scale | ||
| 50 | + cropSize:rect.size | ||
| 51 | + imageViewSize:imageView.frame.size]; | ||
| 52 | + UIImage *cropedImage = [UIImage imageWithCGImage:imageRef]; | ||
| 53 | + cropedImage = [[TZImageManager manager] fixOrientation:cropedImage]; | ||
| 54 | + CGImageRelease(imageRef); | ||
| 55 | + return cropedImage; | ||
| 56 | +} | ||
| 57 | + | ||
| 58 | ++ (CGImageRef)newTransformedImage:(CGAffineTransform)transform sourceImage:(CGImageRef)sourceImage sourceSize:(CGSize)sourceSize outputWidth:(CGFloat)outputWidth cropSize:(CGSize)cropSize imageViewSize:(CGSize)imageViewSize { | ||
| 59 | + CGImageRef source = [self newScaledImage:sourceImage toSize:sourceSize]; | ||
| 60 | + | ||
| 61 | + CGFloat aspect = cropSize.height/cropSize.width; | ||
| 62 | + CGSize outputSize = CGSizeMake(outputWidth, outputWidth*aspect); | ||
| 63 | + | ||
| 64 | + CGContextRef context = CGBitmapContextCreate(NULL, outputSize.width, outputSize.height, CGImageGetBitsPerComponent(source), 0, CGImageGetColorSpace(source), CGImageGetBitmapInfo(source)); | ||
| 65 | + CGContextSetFillColorWithColor(context, [[UIColor clearColor] CGColor]); | ||
| 66 | + CGContextFillRect(context, CGRectMake(0, 0, outputSize.width, outputSize.height)); | ||
| 67 | + | ||
| 68 | + CGAffineTransform uiCoords = CGAffineTransformMakeScale(outputSize.width / cropSize.width, outputSize.height / cropSize.height); | ||
| 69 | + uiCoords = CGAffineTransformTranslate(uiCoords, cropSize.width/2.0, cropSize.height / 2.0); | ||
| 70 | + uiCoords = CGAffineTransformScale(uiCoords, 1.0, -1.0); | ||
| 71 | + CGContextConcatCTM(context, uiCoords); | ||
| 72 | + | ||
| 73 | + CGContextConcatCTM(context, transform); | ||
| 74 | + CGContextScaleCTM(context, 1.0, -1.0); | ||
| 75 | + | ||
| 76 | + CGContextDrawImage(context, CGRectMake(-imageViewSize.width/2, -imageViewSize.height/2.0, imageViewSize.width, imageViewSize.height), source); | ||
| 77 | + CGImageRef resultRef = CGBitmapContextCreateImage(context); | ||
| 78 | + CGContextRelease(context); | ||
| 79 | + CGImageRelease(source); | ||
| 80 | + return resultRef; | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | ++ (CGImageRef)newScaledImage:(CGImageRef)source toSize:(CGSize)size { | ||
| 84 | + CGSize srcSize = size; | ||
| 85 | + CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); | ||
| 86 | + CGContextRef context = CGBitmapContextCreate(NULL, size.width, size.height, 8, 0, rgbColorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); | ||
| 87 | + CGColorSpaceRelease(rgbColorSpace); | ||
| 88 | + | ||
| 89 | + CGContextSetInterpolationQuality(context, kCGInterpolationNone); | ||
| 90 | + CGContextTranslateCTM(context, size.width/2, size.height/2); | ||
| 91 | + | ||
| 92 | + CGContextDrawImage(context, CGRectMake(-srcSize.width/2, -srcSize.height/2, srcSize.width, srcSize.height), source); | ||
| 93 | + | ||
| 94 | + CGImageRef resultRef = CGBitmapContextCreateImage(context); | ||
| 95 | + CGContextRelease(context); | ||
| 96 | + return resultRef; | ||
| 97 | +} | ||
| 98 | + | ||
| 99 | +/// 获取圆形图片 | ||
| 100 | ++ (UIImage *)circularClipImage:(UIImage *)image { | ||
| 101 | + UIGraphicsBeginImageContextWithOptions(image.size, NO, [UIScreen mainScreen].scale); | ||
| 102 | + | ||
| 103 | + CGContextRef ctx = UIGraphicsGetCurrentContext(); | ||
| 104 | + CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height); | ||
| 105 | + CGContextAddEllipseInRect(ctx, rect); | ||
| 106 | + CGContextClip(ctx); | ||
| 107 | + | ||
| 108 | + [image drawInRect:rect]; | ||
| 109 | + UIImage *circleImage = UIGraphicsGetImageFromCurrentImageContext(); | ||
| 110 | + | ||
| 111 | + UIGraphicsEndImageContext(); | ||
| 112 | + return circleImage; | ||
| 113 | +} | ||
| 114 | + | ||
| 115 | +@end | ||
| 116 | + | ||
| 117 | + | ||
| 118 | +@implementation UIImage (TZGif) | ||
| 119 | + | ||
| 120 | ++ (UIImage *)sd_tz_animatedGIFWithData:(NSData *)data { | ||
| 121 | + if (!data) { | ||
| 122 | + return nil; | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); | ||
| 126 | + | ||
| 127 | + size_t count = CGImageSourceGetCount(source); | ||
| 128 | + | ||
| 129 | + UIImage *animatedImage; | ||
| 130 | + | ||
| 131 | + if (count <= 1) { | ||
| 132 | + animatedImage = [[UIImage alloc] initWithData:data]; | ||
| 133 | + } | ||
| 134 | + else { | ||
| 135 | + // images数组过大时内存会飙升,在这里限制下最大count | ||
| 136 | + NSInteger maxCount = [TZImagePickerConfig sharedInstance].gifPreviewMaxImagesCount ?: 200; | ||
| 137 | + NSInteger interval = MAX((count + maxCount / 2) / maxCount, 1); | ||
| 138 | + | ||
| 139 | + NSMutableArray *images = [NSMutableArray array]; | ||
| 140 | + | ||
| 141 | + NSTimeInterval duration = 0.0f; | ||
| 142 | + | ||
| 143 | + for (size_t i = 0; i < count; i+=interval) { | ||
| 144 | + CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL); | ||
| 145 | + if (!image) { | ||
| 146 | + continue; | ||
| 147 | + } | ||
| 148 | + | ||
| 149 | + duration += [self sd_frameDurationAtIndex:i source:source] * MIN(interval, 3); | ||
| 150 | + | ||
| 151 | + [images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]]; | ||
| 152 | + | ||
| 153 | + CGImageRelease(image); | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + if (!duration) { | ||
| 157 | + duration = (1.0f / 10.0f) * count; | ||
| 158 | + } | ||
| 159 | + | ||
| 160 | + animatedImage = [UIImage animatedImageWithImages:images duration:duration]; | ||
| 161 | + } | ||
| 162 | + | ||
| 163 | + CFRelease(source); | ||
| 164 | + | ||
| 165 | + return animatedImage; | ||
| 166 | +} | ||
| 167 | + | ||
| 168 | ++ (float)sd_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source { | ||
| 169 | + float frameDuration = 0.1f; | ||
| 170 | + CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil); | ||
| 171 | + NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties; | ||
| 172 | + NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary]; | ||
| 173 | + | ||
| 174 | + NSNumber *delayTimeUnclampedProp = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime]; | ||
| 175 | + if (delayTimeUnclampedProp) { | ||
| 176 | + frameDuration = [delayTimeUnclampedProp floatValue]; | ||
| 177 | + } | ||
| 178 | + else { | ||
| 179 | + | ||
| 180 | + NSNumber *delayTimeProp = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime]; | ||
| 181 | + if (delayTimeProp) { | ||
| 182 | + frameDuration = [delayTimeProp floatValue]; | ||
| 183 | + } | ||
| 184 | + } | ||
| 185 | + | ||
| 186 | + // Many annoying ads specify a 0 duration to make an image flash as quickly as possible. | ||
| 187 | + // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify | ||
| 188 | + // a duration of <= 10 ms. See <rdar://problem/7689300> and <http://webkit.org/b/36082> | ||
| 189 | + // for more information. | ||
| 190 | + | ||
| 191 | + if (frameDuration < 0.011f) { | ||
| 192 | + frameDuration = 0.100f; | ||
| 193 | + } | ||
| 194 | + | ||
| 195 | + CFRelease(cfFrameProperties); | ||
| 196 | + return frameDuration; | ||
| 197 | +} | ||
| 198 | + | ||
| 199 | +@end |
CNLiveImagePickerController/TZImageManager.h
0 → 100755
| 1 | +// | ||
| 2 | +// TZImageManager.h | ||
| 3 | +// TZImagePickerController | ||
| 4 | +// | ||
| 5 | +// Created by 谭真 on 16/1/4. | ||
| 6 | +// Copyright © 2016年 谭真. All rights reserved. | ||
| 7 | +// 图片资源获取管理类 | ||
| 8 | + | ||
| 9 | +#import <Foundation/Foundation.h> | ||
| 10 | +#import <UIKit/UIKit.h> | ||
| 11 | +#import <AVFoundation/AVFoundation.h> | ||
| 12 | +#import <Photos/Photos.h> | ||
| 13 | +#import "TZAssetModel.h" | ||
| 14 | + | ||
| 15 | +@class TZAlbumModel,TZAssetModel; | ||
| 16 | +@protocol TZImagePickerControllerDelegate; | ||
| 17 | +@interface TZImageManager : NSObject | ||
| 18 | + | ||
| 19 | +@property (nonatomic, strong) PHCachingImageManager *cachingImageManager; | ||
| 20 | + | ||
| 21 | ++ (instancetype)manager NS_SWIFT_NAME(default()); | ||
| 22 | ++ (void)deallocManager; | ||
| 23 | + | ||
| 24 | +@property (weak, nonatomic) id<TZImagePickerControllerDelegate> pickerDelegate; | ||
| 25 | + | ||
| 26 | +#pragma mark - 添加: 原有逻辑-会话id(LXG) | ||
| 27 | +//zl__会话Id用于视频导出路径 | ||
| 28 | +@property (nonatomic, strong) NSString *conversationId; | ||
| 29 | + | ||
| 30 | +@property (nonatomic, assign) BOOL shouldFixOrientation; | ||
| 31 | + | ||
| 32 | +/// Default is 600px / 默认600像素宽 | ||
| 33 | +@property (nonatomic, assign) CGFloat photoPreviewMaxWidth; | ||
| 34 | +/// The pixel width of output image, Default is 828px / 导出图片的宽度,默认828像素宽 | ||
| 35 | +@property (nonatomic, assign) CGFloat photoWidth; | ||
| 36 | + | ||
| 37 | +/// Default is 4, Use in photos collectionView in TZPhotoPickerController | ||
| 38 | +/// 默认4列, TZPhotoPickerController中的照片collectionView | ||
| 39 | +@property (nonatomic, assign) NSInteger columnNumber; | ||
| 40 | + | ||
| 41 | +/// Sort photos ascending by modificationDate,Default is YES | ||
| 42 | +/// 对照片排序,按修改时间升序,默认是YES。如果设置为NO,最新的照片会显示在最前面,内部的拍照按钮会排在第一个 | ||
| 43 | +@property (nonatomic, assign) BOOL sortAscendingByModificationDate; | ||
| 44 | + | ||
| 45 | +/// Minimum selectable photo width, Default is 0 | ||
| 46 | +/// 最小可选中的图片宽度,默认是0,小于这个宽度的图片不可选中 | ||
| 47 | +@property (nonatomic, assign) NSInteger minPhotoWidthSelectable; | ||
| 48 | +@property (nonatomic, assign) NSInteger minPhotoHeightSelectable; | ||
| 49 | +@property (nonatomic, assign) BOOL hideWhenCanNotSelect; | ||
| 50 | + | ||
| 51 | +/// Return YES if Authorized 返回YES如果得到了授权 | ||
| 52 | +- (BOOL)authorizationStatusAuthorized; | ||
| 53 | ++ (NSInteger)authorizationStatus; | ||
| 54 | +- (void)requestAuthorizationWithCompletion:(void (^)(void))completion; | ||
| 55 | + | ||
| 56 | +/// Get Album 获得相册/相册数组 | ||
| 57 | +- (void)getCameraRollAlbum:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage needFetchAssets:(BOOL)needFetchAssets completion:(void (^)(TZAlbumModel *model))completion; | ||
| 58 | +- (void)getAllAlbums:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage needFetchAssets:(BOOL)needFetchAssets completion:(void (^)(NSArray<TZAlbumModel *> *models))completion; | ||
| 59 | + | ||
| 60 | +/// Get Assets 获得Asset数组 | ||
| 61 | +- (void)getAssetsFromFetchResult:(id)result completion:(void (^)(NSArray<TZAssetModel *> *models))completion; | ||
| 62 | +- (void)getAssetsFromFetchResult:(id)result allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage completion:(void (^)(NSArray<TZAssetModel *> *models))completion; | ||
| 63 | +- (void)getAssetFromFetchResult:(id)result atIndex:(NSInteger)index allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage completion:(void (^)(TZAssetModel *model))completion; | ||
| 64 | + | ||
| 65 | +/// Get photo 获得照片 | ||
| 66 | +- (void)getPostImageWithAlbumModel:(TZAlbumModel *)model completion:(void (^)(UIImage *postImage))completion; | ||
| 67 | + | ||
| 68 | +- (int32_t)getPhotoWithAsset:(id)asset completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion; | ||
| 69 | +- (int32_t)getPhotoWithAsset:(id)asset photoWidth:(CGFloat)photoWidth completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion; | ||
| 70 | +- (int32_t)getPhotoWithAsset:(id)asset completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler networkAccessAllowed:(BOOL)networkAccessAllowed; | ||
| 71 | +- (int32_t)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; | ||
| 72 | +- (int32_t)requestImageDataForAsset:(id)asset completion:(void (^)(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info))completion progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler; | ||
| 73 | + | ||
| 74 | +/// Get full Image 获取原图 | ||
| 75 | +/// 如下两个方法completion一般会调多次,一般会先返回缩略图,再返回原图(详见方法内部使用的系统API的说明),如果info[PHImageResultIsDegradedKey] 为 YES,则表明当前返回的是缩略图,否则是原图。 | ||
| 76 | +- (void)getOriginalPhotoWithAsset:(id)asset completion:(void (^)(UIImage *photo,NSDictionary *info))completion; | ||
| 77 | +- (void)getOriginalPhotoWithAsset:(id)asset newCompletion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion; | ||
| 78 | +// 该方法中,completion只会走一次 | ||
| 79 | +- (void)getOriginalPhotoDataWithAsset:(id)asset completion:(void (^)(NSData *data,NSDictionary *info,BOOL isDegraded))completion; | ||
| 80 | +- (void)getOriginalPhotoDataWithAsset:(id)asset progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler completion:(void (^)(NSData *data,NSDictionary *info,BOOL isDegraded))completion; | ||
| 81 | + | ||
| 82 | +/// Save photo 保存照片 | ||
| 83 | +- (void)savePhotoWithImage:(UIImage *)image completion:(void (^)(NSError *error))completion; | ||
| 84 | +- (void)savePhotoWithImage:(UIImage *)image location:(CLLocation *)location completion:(void (^)(NSError *error))completion; | ||
| 85 | + | ||
| 86 | +/// Save video 保存视频 | ||
| 87 | +- (void)saveVideoWithUrl:(NSURL *)url completion:(void (^)(NSError *error))completion; | ||
| 88 | +- (void)saveVideoWithUrl:(NSURL *)url location:(CLLocation *)location completion:(void (^)(NSError *error))completion; | ||
| 89 | + | ||
| 90 | +/// Get video 获得视频 | ||
| 91 | +- (void)getVideoWithAsset:(id)asset completion:(void (^)(AVPlayerItem * playerItem, NSDictionary * info))completion; | ||
| 92 | +- (void)getVideoWithAsset:(id)asset progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler completion:(void (^)(AVPlayerItem *, NSDictionary *))completion; | ||
| 93 | + | ||
| 94 | +/// Export video 导出视频 presetName: 预设名字,默认值是AVAssetExportPreset640x480 | ||
| 95 | +- (void)getVideoOutputPathWithAsset:(id)asset success:(void (^)(NSString *outputPath))success failure:(void (^)(NSString *errorMessage, NSError *error))failure; | ||
| 96 | +- (void)getVideoOutputPathWithAsset:(id)asset presetName:(NSString *)presetName success:(void (^)(NSString *outputPath))success failure:(void (^)(NSString *errorMessage, NSError *error))failure; | ||
| 97 | +/// Deprecated, Use -getVideoOutputPathWithAsset:failure:success: | ||
| 98 | +- (void)getVideoOutputPathWithAsset:(id)asset completion:(void (^)(NSString *outputPath))completion __attribute__((deprecated("Use -getVideoOutputPathWithAsset:failure:success:"))); | ||
| 99 | + | ||
| 100 | +/// Get photo bytes 获得一组照片的大小 | ||
| 101 | +- (void)getPhotosBytesWithArray:(NSArray *)photos completion:(void (^)(NSString *totalBytes))completion; | ||
| 102 | + | ||
| 103 | +/// Judge is a assets array contain the asset 判断一个assets数组是否包含这个asset | ||
| 104 | +- (BOOL)isAssetsArray:(NSArray *)assets containAsset:(id)asset; | ||
| 105 | + | ||
| 106 | +- (NSString *)getAssetIdentifier:(id)asset; | ||
| 107 | +- (BOOL)isCameraRollAlbum:(id)metadata; | ||
| 108 | + | ||
| 109 | +/// 检查照片大小是否满足最小要求 | ||
| 110 | +- (BOOL)isPhotoSelectableWithAsset:(id)asset; | ||
| 111 | +- (CGSize)photoSizeWithAsset:(id)asset; | ||
| 112 | + | ||
| 113 | +/// 修正图片转向 | ||
| 114 | +- (UIImage *)fixOrientation:(UIImage *)aImage; | ||
| 115 | + | ||
| 116 | +/// 获取asset的资源类型 | ||
| 117 | +- (TZAssetModelMediaType)getAssetType:(id)asset; | ||
| 118 | +/// 缩放图片至新尺寸 | ||
| 119 | +- (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)size; | ||
| 120 | + | ||
| 121 | +/// 判断asset是否是视频 | ||
| 122 | +- (BOOL)isVideo:(id)asset; | ||
| 123 | + | ||
| 124 | +@end | ||
| 125 | + | ||
| 126 | +//@interface TZSortDescriptor : NSSortDescriptor | ||
| 127 | +// | ||
| 128 | +//@end |
CNLiveImagePickerController/TZImageManager.m
0 → 100755
| 1 | +// | ||
| 2 | +// TZImageManager.m | ||
| 3 | +// TZImagePickerController | ||
| 4 | +// | ||
| 5 | +// Created by 谭真 on 16/1/4. | ||
| 6 | +// Copyright © 2016年 谭真. All rights reserved. | ||
| 7 | +// | ||
| 8 | + | ||
| 9 | +#import "TZImageManager.h" | ||
| 10 | +#import <AssetsLibrary/AssetsLibrary.h> | ||
| 11 | +#import "TZAssetModel.h" | ||
| 12 | +#import "TZImagePickerController.h" | ||
| 13 | + | ||
| 14 | +@interface TZImageManager () | ||
| 15 | +#pragma clang diagnostic push | ||
| 16 | +#pragma clang diagnostic ignored "-Wdeprecated-declarations" | ||
| 17 | +@property (nonatomic, strong) ALAssetsLibrary *assetLibrary; | ||
| 18 | +@end | ||
| 19 | + | ||
| 20 | +@implementation TZImageManager | ||
| 21 | + | ||
| 22 | +CGSize AssetGridThumbnailSize; | ||
| 23 | +CGFloat TZScreenWidth; | ||
| 24 | +CGFloat TZScreenScale; | ||
| 25 | + | ||
| 26 | +static TZImageManager *manager; | ||
| 27 | +static dispatch_once_t onceToken; | ||
| 28 | + | ||
| 29 | ++ (instancetype)manager { | ||
| 30 | + dispatch_once(&onceToken, ^{ | ||
| 31 | + manager = [[self alloc] init]; | ||
| 32 | + if (iOS8Later) { | ||
| 33 | + // manager.cachingImageManager = [[PHCachingImageManager alloc] init]; | ||
| 34 | + // manager.cachingImageManager.allowsCachingHighQualityImages = YES; | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + [manager configTZScreenWidth]; | ||
| 38 | + }); | ||
| 39 | + return manager; | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | ++ (void)deallocManager { | ||
| 43 | + onceToken = 0; | ||
| 44 | + manager = nil; | ||
| 45 | +} | ||
| 46 | + | ||
| 47 | +- (void)setPhotoWidth:(CGFloat)photoWidth { | ||
| 48 | + _photoWidth = photoWidth; | ||
| 49 | + TZScreenWidth = photoWidth / 2; | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +- (void)setColumnNumber:(NSInteger)columnNumber { | ||
| 53 | + [self configTZScreenWidth]; | ||
| 54 | + | ||
| 55 | + _columnNumber = columnNumber; | ||
| 56 | + CGFloat margin = 4; | ||
| 57 | + CGFloat itemWH = (TZScreenWidth - 2 * margin - 4) / columnNumber - margin; | ||
| 58 | + AssetGridThumbnailSize = CGSizeMake(itemWH * TZScreenScale, itemWH * TZScreenScale); | ||
| 59 | +} | ||
| 60 | + | ||
| 61 | +- (void)configTZScreenWidth { | ||
| 62 | + TZScreenWidth = [UIScreen mainScreen].bounds.size.width; | ||
| 63 | + // 测试发现,如果scale在plus真机上取到3.0,内存会增大特别多。故这里写死成2.0 | ||
| 64 | + TZScreenScale = 2.0; | ||
| 65 | + if (TZScreenWidth > 700) { | ||
| 66 | + TZScreenScale = 1.5; | ||
| 67 | + } | ||
| 68 | +} | ||
| 69 | + | ||
| 70 | +- (ALAssetsLibrary *)assetLibrary { | ||
| 71 | + if (_assetLibrary == nil) _assetLibrary = [[ALAssetsLibrary alloc] init]; | ||
| 72 | + return _assetLibrary; | ||
| 73 | +} | ||
| 74 | + | ||
| 75 | +/// Return YES if Authorized 返回YES如果得到了授权 | ||
| 76 | +- (BOOL)authorizationStatusAuthorized { | ||
| 77 | + NSInteger status = [self.class authorizationStatus]; | ||
| 78 | + if (status == 0) { | ||
| 79 | + /** | ||
| 80 | + * 当某些情况下AuthorizationStatus == AuthorizationStatusNotDetermined时,无法弹出系统首次使用的授权alertView,系统应用设置里亦没有相册的设置,此时将无法使用,故作以下操作,弹出系统首次使用的授权alertView | ||
| 81 | + */ | ||
| 82 | + [self requestAuthorizationWithCompletion:nil]; | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + return status == 3; | ||
| 86 | +} | ||
| 87 | + | ||
| 88 | ++ (NSInteger)authorizationStatus { | ||
| 89 | + if (iOS8Later) { | ||
| 90 | + return [PHPhotoLibrary authorizationStatus]; | ||
| 91 | + } else { | ||
| 92 | + return [ALAssetsLibrary authorizationStatus]; | ||
| 93 | + } | ||
| 94 | + return NO; | ||
| 95 | +} | ||
| 96 | + | ||
| 97 | +- (void)requestAuthorizationWithCompletion:(void (^)(void))completion { | ||
| 98 | + void (^callCompletionBlock)(void) = ^(){ | ||
| 99 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 100 | + if (completion) { | ||
| 101 | + completion(); | ||
| 102 | + } | ||
| 103 | + }); | ||
| 104 | + }; | ||
| 105 | + | ||
| 106 | + if (iOS8Later) { | ||
| 107 | + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ | ||
| 108 | + [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) { | ||
| 109 | + callCompletionBlock(); | ||
| 110 | + }]; | ||
| 111 | + }); | ||
| 112 | + } else { | ||
| 113 | + [self.assetLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) { | ||
| 114 | + callCompletionBlock(); | ||
| 115 | + } failureBlock:^(NSError *error) { | ||
| 116 | + callCompletionBlock(); | ||
| 117 | + }]; | ||
| 118 | + } | ||
| 119 | +} | ||
| 120 | + | ||
| 121 | +#pragma mark - Get Album | ||
| 122 | + | ||
| 123 | +/// Get Album 获得相册/相册数组 | ||
| 124 | +- (void)getCameraRollAlbum:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage needFetchAssets:(BOOL)needFetchAssets completion:(void (^)(TZAlbumModel *model))completion { | ||
| 125 | + __block TZAlbumModel *model; | ||
| 126 | + if (iOS8Later) { | ||
| 127 | + PHFetchOptions *option = [[PHFetchOptions alloc] init]; | ||
| 128 | + if (!allowPickingVideo) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", PHAssetMediaTypeImage]; | ||
| 129 | + if (!allowPickingImage) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", | ||
| 130 | + PHAssetMediaTypeVideo]; | ||
| 131 | + // option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"modificationDate" ascending:self.sortAscendingByModificationDate]]; | ||
| 132 | + if (!self.sortAscendingByModificationDate) { | ||
| 133 | + option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:self.sortAscendingByModificationDate]]; | ||
| 134 | + } | ||
| 135 | + PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil]; | ||
| 136 | + for (PHAssetCollection *collection in smartAlbums) { | ||
| 137 | + // 有可能是PHCollectionList类的的对象,过滤掉 | ||
| 138 | + if (![collection isKindOfClass:[PHAssetCollection class]]) continue; | ||
| 139 | + // 过滤空相册 | ||
| 140 | + if (collection.estimatedAssetCount <= 0) continue; | ||
| 141 | + if ([self isCameraRollAlbum:collection]) { | ||
| 142 | + PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:collection options:option]; | ||
| 143 | + model = [self modelWithResult:fetchResult name:collection.localizedTitle isCameraRoll:YES needFetchAssets:needFetchAssets]; | ||
| 144 | + if (completion) completion(model); | ||
| 145 | + break; | ||
| 146 | + } | ||
| 147 | + } | ||
| 148 | + } else { | ||
| 149 | + [self.assetLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) { | ||
| 150 | + if ([group numberOfAssets] < 1) return; | ||
| 151 | + if ([self isCameraRollAlbum:group]) { | ||
| 152 | + NSString *name = [group valueForProperty:ALAssetsGroupPropertyName]; | ||
| 153 | + model = [self modelWithResult:group name:name isCameraRoll:YES needFetchAssets:needFetchAssets]; | ||
| 154 | + if (completion) completion(model); | ||
| 155 | + *stop = YES; | ||
| 156 | + } | ||
| 157 | + } failureBlock:nil]; | ||
| 158 | + } | ||
| 159 | +} | ||
| 160 | + | ||
| 161 | +- (void)getAllAlbums:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage needFetchAssets:(BOOL)needFetchAssets completion:(void (^)(NSArray<TZAlbumModel *> *))completion{ | ||
| 162 | + NSMutableArray *albumArr = [NSMutableArray array]; | ||
| 163 | + if (iOS8Later) { | ||
| 164 | + PHFetchOptions *option = [[PHFetchOptions alloc] init]; | ||
| 165 | + if (!allowPickingVideo) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", PHAssetMediaTypeImage]; | ||
| 166 | + if (!allowPickingImage) option.predicate = [NSPredicate predicateWithFormat:@"mediaType == %ld", | ||
| 167 | + PHAssetMediaTypeVideo]; | ||
| 168 | + // option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"modificationDate" ascending:self.sortAscendingByModificationDate]]; | ||
| 169 | + if (!self.sortAscendingByModificationDate) { | ||
| 170 | + option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:self.sortAscendingByModificationDate]]; | ||
| 171 | + } | ||
| 172 | + // 我的照片流 1.6.10重新加入.. | ||
| 173 | + PHFetchResult *myPhotoStreamAlbum = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumMyPhotoStream options:nil]; | ||
| 174 | + PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil]; | ||
| 175 | + PHFetchResult *topLevelUserCollections = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil]; | ||
| 176 | + PHFetchResult *syncedAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumSyncedAlbum options:nil]; | ||
| 177 | + PHFetchResult *sharedAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumCloudShared options:nil]; | ||
| 178 | + NSArray *allAlbums = @[myPhotoStreamAlbum,smartAlbums,topLevelUserCollections,syncedAlbums,sharedAlbums]; | ||
| 179 | + for (PHFetchResult *fetchResult in allAlbums) { | ||
| 180 | + for (PHAssetCollection *collection in fetchResult) { | ||
| 181 | + // 有可能是PHCollectionList类的的对象,过滤掉 | ||
| 182 | + if (![collection isKindOfClass:[PHAssetCollection class]]) continue; | ||
| 183 | + // 过滤空相册 | ||
| 184 | + if (collection.estimatedAssetCount <= 0) continue; | ||
| 185 | + PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:collection options:option]; | ||
| 186 | + if (fetchResult.count < 1) continue; | ||
| 187 | + | ||
| 188 | + if ([self.pickerDelegate respondsToSelector:@selector(isAlbumCanSelect:result:)]) { | ||
| 189 | + if (![self.pickerDelegate isAlbumCanSelect:collection.localizedTitle result:fetchResult]) { | ||
| 190 | + continue; | ||
| 191 | + } | ||
| 192 | + } | ||
| 193 | + | ||
| 194 | + if (collection.assetCollectionSubtype == PHAssetCollectionSubtypeSmartAlbumAllHidden) continue; | ||
| 195 | + if (collection.assetCollectionSubtype == 1000000201) continue; //『最近删除』相册 | ||
| 196 | + if ([self isCameraRollAlbum:collection]) { | ||
| 197 | + [albumArr insertObject:[self modelWithResult:fetchResult name:collection.localizedTitle isCameraRoll:YES needFetchAssets:needFetchAssets] atIndex:0]; | ||
| 198 | + } else { | ||
| 199 | + [albumArr addObject:[self modelWithResult:fetchResult name:collection.localizedTitle isCameraRoll:NO needFetchAssets:needFetchAssets]]; | ||
| 200 | + } | ||
| 201 | + } | ||
| 202 | + } | ||
| 203 | + if (completion && albumArr.count > 0) completion(albumArr); | ||
| 204 | + } else { | ||
| 205 | + [self.assetLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) { | ||
| 206 | + if (group == nil) { | ||
| 207 | + if (completion && albumArr.count > 0) completion(albumArr); | ||
| 208 | + } | ||
| 209 | + if ([group numberOfAssets] < 1) return; | ||
| 210 | + NSString *name = [group valueForProperty:ALAssetsGroupPropertyName]; | ||
| 211 | + | ||
| 212 | + if ([self.pickerDelegate respondsToSelector:@selector(isAlbumCanSelect:result:)]) { | ||
| 213 | + if (![self.pickerDelegate isAlbumCanSelect:name result:group]) { | ||
| 214 | + return; | ||
| 215 | + } | ||
| 216 | + } | ||
| 217 | + | ||
| 218 | + if ([self isCameraRollAlbum:group]) { | ||
| 219 | + [albumArr insertObject:[self modelWithResult:group name:name isCameraRoll:YES needFetchAssets:needFetchAssets] atIndex:0]; | ||
| 220 | + } else if ([[group valueForProperty:ALAssetsGroupPropertyType] intValue] == ALAssetsGroupPhotoStream) { | ||
| 221 | + if (albumArr.count) { | ||
| 222 | + [albumArr insertObject:[self modelWithResult:group name:name isCameraRoll:NO needFetchAssets:needFetchAssets] atIndex:1]; | ||
| 223 | + } else { | ||
| 224 | + [albumArr addObject:[self modelWithResult:group name:name isCameraRoll:NO needFetchAssets:needFetchAssets]]; | ||
| 225 | + } | ||
| 226 | + } else { | ||
| 227 | + [albumArr addObject:[self modelWithResult:group name:name isCameraRoll:NO needFetchAssets:needFetchAssets]]; | ||
| 228 | + } | ||
| 229 | + } failureBlock:nil]; | ||
| 230 | + } | ||
| 231 | +} | ||
| 232 | + | ||
| 233 | +#pragma mark - Get Assets | ||
| 234 | + | ||
| 235 | +/// Get Assets 获得照片数组 | ||
| 236 | +- (void)getAssetsFromFetchResult:(id)result completion:(void (^)(NSArray<TZAssetModel *> *))completion { | ||
| 237 | + TZImagePickerConfig *config = [TZImagePickerConfig sharedInstance]; | ||
| 238 | + return [self getAssetsFromFetchResult:result allowPickingVideo:config.allowPickingVideo allowPickingImage:config.allowPickingImage completion:completion]; | ||
| 239 | +} | ||
| 240 | + | ||
| 241 | +- (void)getAssetsFromFetchResult:(id)result allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage completion:(void (^)(NSArray<TZAssetModel *> *))completion { | ||
| 242 | + NSMutableArray *photoArr = [NSMutableArray array]; | ||
| 243 | + if ([result isKindOfClass:[PHFetchResult class]]) { | ||
| 244 | + PHFetchResult *fetchResult = (PHFetchResult *)result; | ||
| 245 | + [fetchResult enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { | ||
| 246 | + TZAssetModel *model = [self assetModelWithAsset:obj allowPickingVideo:allowPickingVideo allowPickingImage:allowPickingImage]; | ||
| 247 | + if (model) { | ||
| 248 | + [photoArr addObject:model]; | ||
| 249 | + } | ||
| 250 | + }]; | ||
| 251 | + if (completion) completion(photoArr); | ||
| 252 | + } else if ([result isKindOfClass:[ALAssetsGroup class]]) { | ||
| 253 | + ALAssetsGroup *group = (ALAssetsGroup *)result; | ||
| 254 | + if (allowPickingImage && allowPickingVideo) { | ||
| 255 | + [group setAssetsFilter:[ALAssetsFilter allAssets]]; | ||
| 256 | + } else if (allowPickingVideo) { | ||
| 257 | + [group setAssetsFilter:[ALAssetsFilter allVideos]]; | ||
| 258 | + } else if (allowPickingImage) { | ||
| 259 | + [group setAssetsFilter:[ALAssetsFilter allPhotos]]; | ||
| 260 | + } | ||
| 261 | + ALAssetsGroupEnumerationResultsBlock resultBlock = ^(ALAsset *result, NSUInteger index, BOOL *stop) { | ||
| 262 | + if (result == nil) { | ||
| 263 | + if (completion) completion(photoArr); | ||
| 264 | + } | ||
| 265 | + TZAssetModel *model = [self assetModelWithAsset:result allowPickingVideo:allowPickingVideo allowPickingImage:allowPickingImage]; | ||
| 266 | + if (model) { | ||
| 267 | + [photoArr addObject:model]; | ||
| 268 | + } | ||
| 269 | + }; | ||
| 270 | + if (self.sortAscendingByModificationDate) { | ||
| 271 | + [group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) { | ||
| 272 | + if (resultBlock) { resultBlock(result,index,stop); } | ||
| 273 | + }]; | ||
| 274 | + } else { | ||
| 275 | + [group enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) { | ||
| 276 | + if (resultBlock) {resultBlock(result,index,stop); } | ||
| 277 | + }]; | ||
| 278 | + } | ||
| 279 | + } | ||
| 280 | +} | ||
| 281 | + | ||
| 282 | +/// Get asset at index 获得下标为index的单个照片 | ||
| 283 | +/// if index beyond bounds, return nil in callback 如果索引越界, 在回调中返回 nil | ||
| 284 | +- (void)getAssetFromFetchResult:(id)result atIndex:(NSInteger)index allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage completion:(void (^)(TZAssetModel *))completion { | ||
| 285 | + if ([result isKindOfClass:[PHFetchResult class]]) { | ||
| 286 | + PHFetchResult *fetchResult = (PHFetchResult *)result; | ||
| 287 | + PHAsset *asset; | ||
| 288 | + @try { | ||
| 289 | + asset = fetchResult[index]; | ||
| 290 | + } | ||
| 291 | + @catch (NSException* e) { | ||
| 292 | + if (completion) completion(nil); | ||
| 293 | + return; | ||
| 294 | + } | ||
| 295 | + TZAssetModel *model = [self assetModelWithAsset:asset allowPickingVideo:allowPickingVideo allowPickingImage:allowPickingImage]; | ||
| 296 | + if (completion) completion(model); | ||
| 297 | + } else if ([result isKindOfClass:[ALAssetsGroup class]]) { | ||
| 298 | + ALAssetsGroup *group = (ALAssetsGroup *)result; | ||
| 299 | + if (allowPickingImage && allowPickingVideo) { | ||
| 300 | + [group setAssetsFilter:[ALAssetsFilter allAssets]]; | ||
| 301 | + } else if (allowPickingVideo) { | ||
| 302 | + [group setAssetsFilter:[ALAssetsFilter allVideos]]; | ||
| 303 | + } else if (allowPickingImage) { | ||
| 304 | + [group setAssetsFilter:[ALAssetsFilter allPhotos]]; | ||
| 305 | + } | ||
| 306 | + NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:index]; | ||
| 307 | + @try { | ||
| 308 | + [group enumerateAssetsAtIndexes:indexSet options:NSEnumerationConcurrent usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) { | ||
| 309 | + if (!result) return; | ||
| 310 | + TZAssetModel *model = [self assetModelWithAsset:result allowPickingVideo:allowPickingVideo allowPickingImage:allowPickingImage]; | ||
| 311 | + if (completion) completion(model); | ||
| 312 | + }]; | ||
| 313 | + } | ||
| 314 | + @catch (NSException* e) { | ||
| 315 | + if (completion) completion(nil); | ||
| 316 | + } | ||
| 317 | + } | ||
| 318 | +} | ||
| 319 | + | ||
| 320 | +- (TZAssetModel *)assetModelWithAsset:(id)asset allowPickingVideo:(BOOL)allowPickingVideo allowPickingImage:(BOOL)allowPickingImage { | ||
| 321 | + BOOL canSelect = YES; | ||
| 322 | + if ([self.pickerDelegate respondsToSelector:@selector(isAssetCanSelect:)]) { | ||
| 323 | + canSelect = [self.pickerDelegate isAssetCanSelect:asset]; | ||
| 324 | + } | ||
| 325 | + if (!canSelect) return nil; | ||
| 326 | + | ||
| 327 | + TZAssetModel *model; | ||
| 328 | + TZAssetModelMediaType type = [self getAssetType:asset]; | ||
| 329 | + if ([asset isKindOfClass:[PHAsset class]]) { | ||
| 330 | + if (!allowPickingVideo && type == TZAssetModelMediaTypeVideo) return nil; | ||
| 331 | + if (!allowPickingImage && type == TZAssetModelMediaTypePhoto) return nil; | ||
| 332 | + if (!allowPickingImage && type == TZAssetModelMediaTypePhotoGif) return nil; | ||
| 333 | + | ||
| 334 | + PHAsset *phAsset = (PHAsset *)asset; | ||
| 335 | + if (self.hideWhenCanNotSelect) { | ||
| 336 | + // 过滤掉尺寸不满足要求的图片 | ||
| 337 | + if (![self isPhotoSelectableWithAsset:phAsset]) { | ||
| 338 | + return nil; | ||
| 339 | + } | ||
| 340 | + } | ||
| 341 | + NSString *timeLength = type == TZAssetModelMediaTypeVideo ? [NSString stringWithFormat:@"%0.0f",phAsset.duration] : @""; | ||
| 342 | + timeLength = [self getNewTimeFromDurationSecond:timeLength.integerValue]; | ||
| 343 | + model = [TZAssetModel modelWithAsset:asset type:type timeLength:timeLength]; | ||
| 344 | + } else { | ||
| 345 | + if (!allowPickingVideo){ | ||
| 346 | + model = [TZAssetModel modelWithAsset:asset type:type]; | ||
| 347 | + return model; | ||
| 348 | + } | ||
| 349 | + /// Allow picking video | ||
| 350 | + if (type == TZAssetModelMediaTypeVideo) { | ||
| 351 | + NSTimeInterval duration = [[asset valueForProperty:ALAssetPropertyDuration] doubleValue]; | ||
| 352 | + NSString *timeLength = [NSString stringWithFormat:@"%0.0f",duration]; | ||
| 353 | + timeLength = [self getNewTimeFromDurationSecond:timeLength.integerValue]; | ||
| 354 | + model = [TZAssetModel modelWithAsset:asset type:type timeLength:timeLength]; | ||
| 355 | + } else { | ||
| 356 | + if (self.hideWhenCanNotSelect) { | ||
| 357 | + // 过滤掉尺寸不满足要求的图片 | ||
| 358 | + if (![self isPhotoSelectableWithAsset:asset]) { | ||
| 359 | + return nil; | ||
| 360 | + } | ||
| 361 | + } | ||
| 362 | + model = [TZAssetModel modelWithAsset:asset type:type]; | ||
| 363 | + } | ||
| 364 | + } | ||
| 365 | + return model; | ||
| 366 | +} | ||
| 367 | + | ||
| 368 | +- (TZAssetModelMediaType)getAssetType:(id)asset { | ||
| 369 | + TZAssetModelMediaType type = TZAssetModelMediaTypePhoto; | ||
| 370 | + if ([asset isKindOfClass:[PHAsset class]]) { | ||
| 371 | + PHAsset *phAsset = (PHAsset *)asset; | ||
| 372 | + if (phAsset.mediaType == PHAssetMediaTypeVideo) type = TZAssetModelMediaTypeVideo; | ||
| 373 | + else if (phAsset.mediaType == PHAssetMediaTypeAudio) type = TZAssetModelMediaTypeAudio; | ||
| 374 | + else if (phAsset.mediaType == PHAssetMediaTypeImage) { | ||
| 375 | + if (@available(iOS 9.1, *)) { | ||
| 376 | + // if (asset.mediaSubtypes == PHAssetMediaSubtypePhotoLive) type = TZAssetModelMediaTypeLivePhoto; | ||
| 377 | + } | ||
| 378 | + // Gif | ||
| 379 | + if ([[phAsset valueForKey:@"filename"] hasSuffix:@"GIF"]) { | ||
| 380 | + type = TZAssetModelMediaTypePhotoGif; | ||
| 381 | + } | ||
| 382 | + } | ||
| 383 | + } else { | ||
| 384 | + if ([[asset valueForProperty:ALAssetPropertyType] isEqualToString:ALAssetTypeVideo]) { | ||
| 385 | + type = TZAssetModelMediaTypeVideo; | ||
| 386 | + } | ||
| 387 | + } | ||
| 388 | + return type; | ||
| 389 | +} | ||
| 390 | + | ||
| 391 | +- (NSString *)getNewTimeFromDurationSecond:(NSInteger)duration { | ||
| 392 | + NSString *newTime; | ||
| 393 | + if (duration < 10) { | ||
| 394 | + newTime = [NSString stringWithFormat:@"0:0%zd",duration]; | ||
| 395 | + } else if (duration < 60) { | ||
| 396 | + newTime = [NSString stringWithFormat:@"0:%zd",duration]; | ||
| 397 | + } else { | ||
| 398 | + NSInteger min = duration / 60; | ||
| 399 | + NSInteger sec = duration - (min * 60); | ||
| 400 | + if (sec < 10) { | ||
| 401 | + newTime = [NSString stringWithFormat:@"%zd:0%zd",min,sec]; | ||
| 402 | + } else { | ||
| 403 | + newTime = [NSString stringWithFormat:@"%zd:%zd",min,sec]; | ||
| 404 | + } | ||
| 405 | + } | ||
| 406 | + return newTime; | ||
| 407 | +} | ||
| 408 | + | ||
| 409 | +/// Get photo bytes 获得一组照片的大小 | ||
| 410 | +- (void)getPhotosBytesWithArray:(NSArray *)photos completion:(void (^)(NSString *totalBytes))completion { | ||
| 411 | + if (!photos || !photos.count) { | ||
| 412 | + if (completion) completion(@"0B"); | ||
| 413 | + return; | ||
| 414 | + } | ||
| 415 | + __block NSInteger dataLength = 0; | ||
| 416 | + __block NSInteger assetCount = 0; | ||
| 417 | + for (NSInteger i = 0; i < photos.count; i++) { | ||
| 418 | + TZAssetModel *model = photos[i]; | ||
| 419 | + if ([model.asset isKindOfClass:[PHAsset class]]) { | ||
| 420 | + PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init]; | ||
| 421 | + options.resizeMode = PHImageRequestOptionsResizeModeFast; | ||
| 422 | + options.networkAccessAllowed = YES; | ||
| 423 | + if (model.type == TZAssetModelMediaTypePhotoGif) { | ||
| 424 | + options.version = PHImageRequestOptionsVersionOriginal; | ||
| 425 | + } | ||
| 426 | + [[PHImageManager defaultManager] requestImageDataForAsset:model.asset options:options resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { | ||
| 427 | + if (model.type != TZAssetModelMediaTypeVideo) dataLength += imageData.length; | ||
| 428 | + assetCount ++; | ||
| 429 | + if (assetCount >= photos.count) { | ||
| 430 | + NSString *bytes = [self getBytesFromDataLength:dataLength]; | ||
| 431 | + if (completion) completion(bytes); | ||
| 432 | + } | ||
| 433 | + }]; | ||
| 434 | + } else if ([model.asset isKindOfClass:[ALAsset class]]) { | ||
| 435 | + ALAssetRepresentation *representation = [model.asset defaultRepresentation]; | ||
| 436 | + if (model.type != TZAssetModelMediaTypeVideo) dataLength += (NSInteger)representation.size; | ||
| 437 | + if (i >= photos.count - 1) { | ||
| 438 | + NSString *bytes = [self getBytesFromDataLength:dataLength]; | ||
| 439 | + if (completion) completion(bytes); | ||
| 440 | + } | ||
| 441 | + } | ||
| 442 | + } | ||
| 443 | +} | ||
| 444 | + | ||
| 445 | +- (NSString *)getBytesFromDataLength:(NSInteger)dataLength { | ||
| 446 | + NSString *bytes; | ||
| 447 | + if (dataLength >= 0.1 * (1024 * 1024)) { | ||
| 448 | + bytes = [NSString stringWithFormat:@"%0.1fM",dataLength/1024/1024.0]; | ||
| 449 | + } else if (dataLength >= 1024) { | ||
| 450 | + bytes = [NSString stringWithFormat:@"%0.0fK",dataLength/1024.0]; | ||
| 451 | + } else { | ||
| 452 | + bytes = [NSString stringWithFormat:@"%zdB",dataLength]; | ||
| 453 | + } | ||
| 454 | + return bytes; | ||
| 455 | +} | ||
| 456 | + | ||
| 457 | +#pragma mark - Get Photo | ||
| 458 | + | ||
| 459 | +/// Get photo 获得照片本身 | ||
| 460 | +- (int32_t)getPhotoWithAsset:(id)asset completion:(void (^)(UIImage *, NSDictionary *, BOOL isDegraded))completion { | ||
| 461 | + CGFloat fullScreenWidth = TZScreenWidth; | ||
| 462 | + if (fullScreenWidth > _photoPreviewMaxWidth) { | ||
| 463 | + fullScreenWidth = _photoPreviewMaxWidth; | ||
| 464 | + } | ||
| 465 | + return [self getPhotoWithAsset:asset photoWidth:fullScreenWidth completion:completion progressHandler:nil networkAccessAllowed:YES]; | ||
| 466 | +} | ||
| 467 | + | ||
| 468 | +- (int32_t)getPhotoWithAsset:(id)asset photoWidth:(CGFloat)photoWidth completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion { | ||
| 469 | + return [self getPhotoWithAsset:asset photoWidth:photoWidth completion:completion progressHandler:nil networkAccessAllowed:YES]; | ||
| 470 | +} | ||
| 471 | + | ||
| 472 | +- (int32_t)getPhotoWithAsset:(id)asset completion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler networkAccessAllowed:(BOOL)networkAccessAllowed { | ||
| 473 | + CGFloat fullScreenWidth = TZScreenWidth; | ||
| 474 | + if (fullScreenWidth > _photoPreviewMaxWidth) { | ||
| 475 | + fullScreenWidth = _photoPreviewMaxWidth; | ||
| 476 | + } | ||
| 477 | + return [self getPhotoWithAsset:asset photoWidth:fullScreenWidth completion:completion progressHandler:progressHandler networkAccessAllowed:networkAccessAllowed]; | ||
| 478 | +} | ||
| 479 | + | ||
| 480 | +- (int32_t)requestImageDataForAsset:(id)asset completion:(void (^)(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info))completion progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler { | ||
| 481 | + if ([asset isKindOfClass:[PHAsset class]]) { | ||
| 482 | + PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init]; | ||
| 483 | + options.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info) { | ||
| 484 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 485 | + if (progressHandler) { | ||
| 486 | + progressHandler(progress, error, stop, info); | ||
| 487 | + } | ||
| 488 | + }); | ||
| 489 | + }; | ||
| 490 | + options.networkAccessAllowed = YES; | ||
| 491 | + options.resizeMode = PHImageRequestOptionsResizeModeFast; | ||
| 492 | + int32_t imageRequestID = [[PHImageManager defaultManager] requestImageDataForAsset:asset options:options resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { | ||
| 493 | + if (completion) completion(imageData,dataUTI,orientation,info); | ||
| 494 | + }]; | ||
| 495 | + return imageRequestID; | ||
| 496 | + } else if ([asset isKindOfClass:[ALAsset class]]) { | ||
| 497 | + ALAsset *alAsset = (ALAsset *)asset; | ||
| 498 | + dispatch_async(dispatch_get_global_queue(0,0), ^{ | ||
| 499 | + ALAssetRepresentation *assetRep = [alAsset defaultRepresentation]; | ||
| 500 | + CGImageRef fullScrennImageRef = [assetRep fullScreenImage]; | ||
| 501 | + UIImage *fullScrennImage = [UIImage imageWithCGImage:fullScrennImageRef scale:2.0 orientation:UIImageOrientationUp]; | ||
| 502 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 503 | + if (completion) completion(UIImageJPEGRepresentation(fullScrennImage, 0.83), nil, UIImageOrientationUp, nil); | ||
| 504 | + }); | ||
| 505 | + }); | ||
| 506 | + } | ||
| 507 | + return 0; | ||
| 508 | +} | ||
| 509 | + | ||
| 510 | +- (int32_t)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 { | ||
| 511 | + if ([asset isKindOfClass:[PHAsset class]]) { | ||
| 512 | + CGSize imageSize; | ||
| 513 | + if (photoWidth < TZScreenWidth && photoWidth < _photoPreviewMaxWidth) { | ||
| 514 | + imageSize = AssetGridThumbnailSize; | ||
| 515 | + } else { | ||
| 516 | + PHAsset *phAsset = (PHAsset *)asset; | ||
| 517 | + CGFloat aspectRatio = phAsset.pixelWidth / (CGFloat)phAsset.pixelHeight; | ||
| 518 | + CGFloat pixelWidth = photoWidth * TZScreenScale * 1.5; | ||
| 519 | + // 超宽图片 | ||
| 520 | + if (aspectRatio > 1.8) { | ||
| 521 | + pixelWidth = pixelWidth * aspectRatio; | ||
| 522 | + } | ||
| 523 | + // 超高图片 | ||
| 524 | + if (aspectRatio < 0.2) { | ||
| 525 | + pixelWidth = pixelWidth * 0.5; | ||
| 526 | + } | ||
| 527 | + CGFloat pixelHeight = pixelWidth / aspectRatio; | ||
| 528 | + imageSize = CGSizeMake(pixelWidth, pixelHeight); | ||
| 529 | + } | ||
| 530 | + | ||
| 531 | + __block UIImage *image; | ||
| 532 | + // 修复获取图片时出现的瞬间内存过高问题 | ||
| 533 | + // 下面两行代码,来自hsjcom,他的github是:https://github.com/hsjcom 表示感谢 | ||
| 534 | + PHImageRequestOptions *option = [[PHImageRequestOptions alloc] init]; | ||
| 535 | + option.resizeMode = PHImageRequestOptionsResizeModeFast; | ||
| 536 | + int32_t imageRequestID = [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:imageSize contentMode:PHImageContentModeAspectFill options:option resultHandler:^(UIImage *result, NSDictionary *info) { | ||
| 537 | + if (result) { | ||
| 538 | + image = result; | ||
| 539 | + } | ||
| 540 | + BOOL downloadFinined = (![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey]); | ||
| 541 | + if (downloadFinined && result) { | ||
| 542 | + result = [self fixOrientation:result]; | ||
| 543 | + if (completion) completion(result,info,[[info objectForKey:PHImageResultIsDegradedKey] boolValue]); | ||
| 544 | + } | ||
| 545 | + // Download image from iCloud / 从iCloud下载图片 | ||
| 546 | + if ([info objectForKey:PHImageResultIsInCloudKey] && !result && networkAccessAllowed) { | ||
| 547 | + PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init]; | ||
| 548 | + options.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info) { | ||
| 549 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 550 | + if (progressHandler) { | ||
| 551 | + progressHandler(progress, error, stop, info); | ||
| 552 | + } | ||
| 553 | + }); | ||
| 554 | + }; | ||
| 555 | + options.networkAccessAllowed = YES; | ||
| 556 | + options.resizeMode = PHImageRequestOptionsResizeModeFast; | ||
| 557 | + [[PHImageManager defaultManager] requestImageDataForAsset:asset options:options resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { | ||
| 558 | + UIImage *resultImage = [UIImage imageWithData:imageData scale:0.1]; | ||
| 559 | + if (![TZImagePickerConfig sharedInstance].notScaleImage) { | ||
| 560 | + resultImage = [self scaleImage:resultImage toSize:imageSize]; | ||
| 561 | + } | ||
| 562 | + if (!resultImage) { | ||
| 563 | + resultImage = image; | ||
| 564 | + } | ||
| 565 | + resultImage = [self fixOrientation:resultImage]; | ||
| 566 | + if (completion) completion(resultImage,info,NO); | ||
| 567 | + }]; | ||
| 568 | + } | ||
| 569 | + }]; | ||
| 570 | + return imageRequestID; | ||
| 571 | + } else if ([asset isKindOfClass:[ALAsset class]]) { | ||
| 572 | + ALAsset *alAsset = (ALAsset *)asset; | ||
| 573 | + dispatch_async(dispatch_get_global_queue(0,0), ^{ | ||
| 574 | + CGImageRef thumbnailImageRef = alAsset.thumbnail; | ||
| 575 | + UIImage *thumbnailImage = [UIImage imageWithCGImage:thumbnailImageRef scale:2.0 orientation:UIImageOrientationUp]; | ||
| 576 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 577 | + if (completion) completion(thumbnailImage,nil,YES); | ||
| 578 | + | ||
| 579 | + if (photoWidth == TZScreenWidth || photoWidth == self->_photoPreviewMaxWidth) { | ||
| 580 | + dispatch_async(dispatch_get_global_queue(0,0), ^{ | ||
| 581 | + ALAssetRepresentation *assetRep = [alAsset defaultRepresentation]; | ||
| 582 | + CGImageRef fullScrennImageRef = [assetRep fullScreenImage]; | ||
| 583 | + UIImage *fullScrennImage = [UIImage imageWithCGImage:fullScrennImageRef scale:2.0 orientation:UIImageOrientationUp]; | ||
| 584 | + | ||
| 585 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 586 | + if (completion) completion(fullScrennImage,nil,NO); | ||
| 587 | + }); | ||
| 588 | + }); | ||
| 589 | + } | ||
| 590 | + }); | ||
| 591 | + }); | ||
| 592 | + } | ||
| 593 | + return 0; | ||
| 594 | +} | ||
| 595 | + | ||
| 596 | +/// Get postImage / 获取封面图 | ||
| 597 | +- (void)getPostImageWithAlbumModel:(TZAlbumModel *)model completion:(void (^)(UIImage *))completion { | ||
| 598 | + if (iOS8Later) { | ||
| 599 | + id asset = [model.result lastObject]; | ||
| 600 | + if (!self.sortAscendingByModificationDate) { | ||
| 601 | + asset = [model.result firstObject]; | ||
| 602 | + } | ||
| 603 | + [[TZImageManager manager] getPhotoWithAsset:asset photoWidth:80 completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) { | ||
| 604 | + if (completion) completion(photo); | ||
| 605 | + }]; | ||
| 606 | + } else { | ||
| 607 | + ALAssetsGroup *group = model.result; | ||
| 608 | + UIImage *postImage = [UIImage imageWithCGImage:group.posterImage]; | ||
| 609 | + if (completion) completion(postImage); | ||
| 610 | + } | ||
| 611 | +} | ||
| 612 | + | ||
| 613 | +/// Get Original Photo / 获取原图 | ||
| 614 | +- (void)getOriginalPhotoWithAsset:(id)asset completion:(void (^)(UIImage *photo,NSDictionary *info))completion { | ||
| 615 | + [self getOriginalPhotoWithAsset:asset newCompletion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) { | ||
| 616 | + if (completion) { | ||
| 617 | + completion(photo,info); | ||
| 618 | + } | ||
| 619 | + }]; | ||
| 620 | +} | ||
| 621 | + | ||
| 622 | +- (void)getOriginalPhotoWithAsset:(id)asset newCompletion:(void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion { | ||
| 623 | + if ([asset isKindOfClass:[PHAsset class]]) { | ||
| 624 | + PHImageRequestOptions *option = [[PHImageRequestOptions alloc]init]; | ||
| 625 | + option.networkAccessAllowed = YES; | ||
| 626 | + option.resizeMode = PHImageRequestOptionsResizeModeFast; | ||
| 627 | + [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFit options:option resultHandler:^(UIImage *result, NSDictionary *info) { | ||
| 628 | + BOOL downloadFinined = (![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey]); | ||
| 629 | + if (downloadFinined && result) { | ||
| 630 | + result = [self fixOrientation:result]; | ||
| 631 | + BOOL isDegraded = [[info objectForKey:PHImageResultIsDegradedKey] boolValue]; | ||
| 632 | + if (completion) completion(result,info,isDegraded); | ||
| 633 | + } | ||
| 634 | + }]; | ||
| 635 | + } else if ([asset isKindOfClass:[ALAsset class]]) { | ||
| 636 | + ALAsset *alAsset = (ALAsset *)asset; | ||
| 637 | + ALAssetRepresentation *assetRep = [alAsset defaultRepresentation]; | ||
| 638 | + | ||
| 639 | + dispatch_async(dispatch_get_global_queue(0,0), ^{ | ||
| 640 | + CGImageRef originalImageRef = [assetRep fullResolutionImage]; | ||
| 641 | + UIImage *originalImage = [UIImage imageWithCGImage:originalImageRef scale:1.0 orientation:UIImageOrientationUp]; | ||
| 642 | + | ||
| 643 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 644 | + if (completion) completion(originalImage,nil,NO); | ||
| 645 | + }); | ||
| 646 | + }); | ||
| 647 | + } | ||
| 648 | +} | ||
| 649 | + | ||
| 650 | +- (void)getOriginalPhotoDataWithAsset:(id)asset completion:(void (^)(NSData *data,NSDictionary *info,BOOL isDegraded))completion { | ||
| 651 | + [self getOriginalPhotoDataWithAsset:asset progressHandler:nil completion:completion]; | ||
| 652 | +} | ||
| 653 | + | ||
| 654 | +- (void)getOriginalPhotoDataWithAsset:(id)asset progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler completion:(void (^)(NSData *data,NSDictionary *info,BOOL isDegraded))completion { | ||
| 655 | + if ([asset isKindOfClass:[PHAsset class]]) { | ||
| 656 | + PHImageRequestOptions *option = [[PHImageRequestOptions alloc] init]; | ||
| 657 | + option.networkAccessAllowed = YES; | ||
| 658 | + if ([[asset valueForKey:@"filename"] hasSuffix:@"GIF"]) { | ||
| 659 | + // if version isn't PHImageRequestOptionsVersionOriginal, the gif may cann't play | ||
| 660 | + option.version = PHImageRequestOptionsVersionOriginal; | ||
| 661 | + } | ||
| 662 | + [option setProgressHandler:progressHandler]; | ||
| 663 | + option.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat; | ||
| 664 | + [[PHImageManager defaultManager] requestImageDataForAsset:asset options:option resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { | ||
| 665 | + BOOL downloadFinined = (![[info objectForKey:PHImageCancelledKey] boolValue] && ![info objectForKey:PHImageErrorKey]); | ||
| 666 | + if (downloadFinined && imageData) { | ||
| 667 | + if (completion) completion(imageData,info,NO); | ||
| 668 | + } | ||
| 669 | + }]; | ||
| 670 | + } else if ([asset isKindOfClass:[ALAsset class]]) { | ||
| 671 | + ALAsset *alAsset = (ALAsset *)asset; | ||
| 672 | + ALAssetRepresentation *assetRep = [alAsset defaultRepresentation]; | ||
| 673 | + Byte *imageBuffer = (Byte *)malloc(assetRep.size); | ||
| 674 | + NSUInteger bufferSize = [assetRep getBytes:imageBuffer fromOffset:0.0 length:assetRep.size error:nil]; | ||
| 675 | + NSData *imageData = [NSData dataWithBytesNoCopy:imageBuffer length:bufferSize freeWhenDone:YES]; | ||
| 676 | + if (completion) completion(imageData,nil,NO); | ||
| 677 | + } | ||
| 678 | +} | ||
| 679 | + | ||
| 680 | +#pragma mark - Save photo | ||
| 681 | + | ||
| 682 | +- (void)savePhotoWithImage:(UIImage *)image completion:(void (^)(NSError *error))completion { | ||
| 683 | + [self savePhotoWithImage:image location:nil completion:completion]; | ||
| 684 | +} | ||
| 685 | + | ||
| 686 | +- (void)savePhotoWithImage:(UIImage *)image location:(CLLocation *)location completion:(void (^)(NSError *error))completion { | ||
| 687 | + if (iOS8Later) { | ||
| 688 | + [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ | ||
| 689 | + if (@available(iOS 9, *)) { | ||
| 690 | + NSData *data = UIImageJPEGRepresentation(image, 0.9); | ||
| 691 | + PHAssetResourceCreationOptions *options = [[PHAssetResourceCreationOptions alloc] init]; | ||
| 692 | + options.shouldMoveFile = YES; | ||
| 693 | + PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAsset]; | ||
| 694 | + [request addResourceWithType:PHAssetResourceTypePhoto data:data options:options]; | ||
| 695 | + if (location) { | ||
| 696 | + request.location = location; | ||
| 697 | + } | ||
| 698 | + request.creationDate = [NSDate date]; | ||
| 699 | + } else { | ||
| 700 | + PHAssetChangeRequest *request = [PHAssetChangeRequest creationRequestForAssetFromImage:image]; | ||
| 701 | + if (location) { | ||
| 702 | + request.location = location; | ||
| 703 | + } | ||
| 704 | + request.creationDate = [NSDate date]; | ||
| 705 | + } | ||
| 706 | + } completionHandler:^(BOOL success, NSError *error) { | ||
| 707 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 708 | + if (success && completion) { | ||
| 709 | + completion(nil); | ||
| 710 | + } else if (error) { | ||
| 711 | + NSLog(@"保存照片出错:%@",error.localizedDescription); | ||
| 712 | + if (completion) { | ||
| 713 | + completion(error); | ||
| 714 | + } | ||
| 715 | + } | ||
| 716 | + }); | ||
| 717 | + }]; | ||
| 718 | + } else { | ||
| 719 | + [self.assetLibrary writeImageToSavedPhotosAlbum:image.CGImage orientation:[self orientationFromImage:image] completionBlock:^(NSURL *assetURL, NSError *error) { | ||
| 720 | + if (error) { | ||
| 721 | + NSLog(@"保存图片失败:%@",error.localizedDescription); | ||
| 722 | + if (completion) { | ||
| 723 | + completion(error); | ||
| 724 | + } | ||
| 725 | + } else { | ||
| 726 | + // 多给系统0.5秒的时间,让系统去更新相册数据 | ||
| 727 | + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | ||
| 728 | + if (completion) { | ||
| 729 | + completion(nil); | ||
| 730 | + } | ||
| 731 | + }); | ||
| 732 | + } | ||
| 733 | + }]; | ||
| 734 | + } | ||
| 735 | +} | ||
| 736 | + | ||
| 737 | +#pragma mark - Save video | ||
| 738 | + | ||
| 739 | +- (void)saveVideoWithUrl:(NSURL *)url completion:(void (^)(NSError *error))completion { | ||
| 740 | + [self saveVideoWithUrl:url location:nil completion:completion]; | ||
| 741 | +} | ||
| 742 | + | ||
| 743 | +- (void)saveVideoWithUrl:(NSURL *)url location:(CLLocation *)location completion:(void (^)(NSError *error))completion { | ||
| 744 | + if (iOS8Later) { | ||
| 745 | + [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{ | ||
| 746 | + if (@available(iOS 9, *)) { | ||
| 747 | + PHAssetResourceCreationOptions *options = [[PHAssetResourceCreationOptions alloc] init]; | ||
| 748 | + options.shouldMoveFile = YES; | ||
| 749 | + PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAsset]; | ||
| 750 | + [request addResourceWithType:PHAssetResourceTypeVideo fileURL:url options:options]; | ||
| 751 | + if (location) { | ||
| 752 | + request.location = location; | ||
| 753 | + } | ||
| 754 | + request.creationDate = [NSDate date]; | ||
| 755 | + } else { | ||
| 756 | + PHAssetChangeRequest *request = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:url]; | ||
| 757 | + if (location) { | ||
| 758 | + request.location = location; | ||
| 759 | + } | ||
| 760 | + request.creationDate = [NSDate date]; | ||
| 761 | + } | ||
| 762 | + } completionHandler:^(BOOL success, NSError *error) { | ||
| 763 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 764 | + if (success && completion) { | ||
| 765 | + completion(nil); | ||
| 766 | + } else if (error) { | ||
| 767 | + NSLog(@"保存视频出错:%@",error.localizedDescription); | ||
| 768 | + if (completion) { | ||
| 769 | + completion(error); | ||
| 770 | + } | ||
| 771 | + } | ||
| 772 | + }); | ||
| 773 | + }]; | ||
| 774 | + } else { | ||
| 775 | + [self.assetLibrary writeVideoAtPathToSavedPhotosAlbum:url completionBlock:^(NSURL *assetURL, NSError *error) { | ||
| 776 | + if (error) { | ||
| 777 | + NSLog(@"保存视频出错:%@",error.localizedDescription); | ||
| 778 | + if (completion) { | ||
| 779 | + completion(error); | ||
| 780 | + } | ||
| 781 | + } else { | ||
| 782 | + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | ||
| 783 | + if (completion) { | ||
| 784 | + completion(nil); | ||
| 785 | + } | ||
| 786 | + }); | ||
| 787 | + } | ||
| 788 | + }]; | ||
| 789 | + } | ||
| 790 | +} | ||
| 791 | + | ||
| 792 | +#pragma mark - Get Video | ||
| 793 | + | ||
| 794 | +/// Get Video / 获取视频 | ||
| 795 | +- (void)getVideoWithAsset:(id)asset completion:(void (^)(AVPlayerItem *, NSDictionary *))completion { | ||
| 796 | + [self getVideoWithAsset:asset progressHandler:nil completion:completion]; | ||
| 797 | +} | ||
| 798 | + | ||
| 799 | +- (void)getVideoWithAsset:(id)asset progressHandler:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressHandler completion:(void (^)(AVPlayerItem *, NSDictionary *))completion { | ||
| 800 | + if ([asset isKindOfClass:[PHAsset class]]) { | ||
| 801 | + PHVideoRequestOptions *option = [[PHVideoRequestOptions alloc] init]; | ||
| 802 | + option.networkAccessAllowed = YES; | ||
| 803 | + option.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info) { | ||
| 804 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 805 | + if (progressHandler) { | ||
| 806 | + progressHandler(progress, error, stop, info); | ||
| 807 | + } | ||
| 808 | + }); | ||
| 809 | + }; | ||
| 810 | + [[PHImageManager defaultManager] requestPlayerItemForVideo:asset options:option resultHandler:^(AVPlayerItem *playerItem, NSDictionary *info) { | ||
| 811 | + if (completion) completion(playerItem,info); | ||
| 812 | + }]; | ||
| 813 | + } else if ([asset isKindOfClass:[ALAsset class]]) { | ||
| 814 | + ALAsset *alAsset = (ALAsset *)asset; | ||
| 815 | + ALAssetRepresentation *defaultRepresentation = [alAsset defaultRepresentation]; | ||
| 816 | + NSString *uti = [defaultRepresentation UTI]; | ||
| 817 | + NSURL *videoURL = [[asset valueForProperty:ALAssetPropertyURLs] valueForKey:uti]; | ||
| 818 | + AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:videoURL]; | ||
| 819 | + if (completion && playerItem) completion(playerItem,nil); | ||
| 820 | + } | ||
| 821 | +} | ||
| 822 | + | ||
| 823 | +#pragma mark - Export video | ||
| 824 | + | ||
| 825 | +/// Export Video / 导出视频 | ||
| 826 | +- (void)getVideoOutputPathWithAsset:(id)asset success:(void (^)(NSString *outputPath))success failure:(void (^)(NSString *errorMessage, NSError *error))failure { | ||
| 827 | + [self getVideoOutputPathWithAsset:asset presetName:AVAssetExportPreset640x480 success:success failure:failure]; | ||
| 828 | +} | ||
| 829 | + | ||
| 830 | +- (void)getVideoOutputPathWithAsset:(id)asset presetName:(NSString *)presetName success:(void (^)(NSString *outputPath))success failure:(void (^)(NSString *errorMessage, NSError *error))failure { | ||
| 831 | + if ([asset isKindOfClass:[PHAsset class]]) { | ||
| 832 | + PHVideoRequestOptions* options = [[PHVideoRequestOptions alloc] init]; | ||
| 833 | + options.version = PHVideoRequestOptionsVersionOriginal; | ||
| 834 | + options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic; | ||
| 835 | + options.networkAccessAllowed = YES; | ||
| 836 | + [[PHImageManager defaultManager] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset* avasset, AVAudioMix* audioMix, NSDictionary* info){ | ||
| 837 | + // NSLog(@"Info:\n%@",info); | ||
| 838 | + AVURLAsset *videoAsset = (AVURLAsset*)avasset; | ||
| 839 | + // NSLog(@"AVAsset URL: %@",myAsset.URL); | ||
| 840 | + [self startExportVideoWithVideoAsset:videoAsset presetName:presetName success:success failure:failure]; | ||
| 841 | + }]; | ||
| 842 | + } else if ([asset isKindOfClass:[ALAsset class]]) { | ||
| 843 | + NSURL *videoURL =[asset valueForProperty:ALAssetPropertyAssetURL]; // ALAssetPropertyURLs | ||
| 844 | + AVURLAsset *videoAsset = [[AVURLAsset alloc] initWithURL:videoURL options:nil]; | ||
| 845 | + [self startExportVideoWithVideoAsset:videoAsset presetName:presetName success:success failure:failure]; | ||
| 846 | + } | ||
| 847 | +} | ||
| 848 | + | ||
| 849 | +/// Deprecated, Use -getVideoOutputPathWithAsset:failure:success: | ||
| 850 | +- (void)getVideoOutputPathWithAsset:(id)asset completion:(void (^)(NSString *outputPath))completion { | ||
| 851 | + [self getVideoOutputPathWithAsset:asset success:completion failure:nil]; | ||
| 852 | +} | ||
| 853 | + | ||
| 854 | +- (void)startExportVideoWithVideoAsset:(AVURLAsset *)videoAsset presetName:(NSString *)presetName success:(void (^)(NSString *outputPath))success failure:(void (^)(NSString *errorMessage, NSError *error))failure { | ||
| 855 | + // Find compatible presets by video asset. | ||
| 856 | + NSArray *presets = [AVAssetExportSession exportPresetsCompatibleWithAsset:videoAsset]; | ||
| 857 | + | ||
| 858 | + // Begin to compress video | ||
| 859 | + // Now we just compress to low resolution if it supports | ||
| 860 | + // If you need to upload to the server, but server does't support to upload by streaming, | ||
| 861 | + // You can compress the resolution to lower. Or you can support more higher resolution. | ||
| 862 | + if ([presets containsObject:presetName]) { | ||
| 863 | + AVAssetExportSession *session = [[AVAssetExportSession alloc] initWithAsset:videoAsset presetName:presetName]; | ||
| 864 | + | ||
| 865 | + NSDateFormatter *formater = [[NSDateFormatter alloc] init]; | ||
| 866 | + [formater setDateFormat:@"yyyy-MM-dd-HH:mm:ss-SSS"]; | ||
| 867 | +#pragma mark - TODO: 添加: 原有逻辑-根据会话id取得path(LXG) | ||
| 868 | + NSString *filaName = [NSString stringWithFormat:@"output-%@_fileType=videoType.mp4",[formater stringFromDate:[NSDate date]]]; | ||
| 869 | +// NSString *outputPath = [NSHomeDirectory() stringByAppendingFormat:@"/tmp/output-%@.mp4", [formater stringFromDate:[NSDate date]]]; | ||
| 870 | + NSString *outputPath = @""; | ||
| 871 | + if (self.conversationId) { | ||
| 872 | +// outputPath = [[CNTools newChatFilePathByUserId:self.conversationId] stringByAppendingPathComponent:filaName];//聊天 | ||
| 873 | +// | ||
| 874 | + } | ||
| 875 | + else | ||
| 876 | + { | ||
| 877 | + | ||
| 878 | +// outputPath = [[CNTools chatFilePathByUserId:[IMAPlatform sharedInstance].host.userId] stringByAppendingString:filaName];//朋友圈 | ||
| 879 | + } | ||
| 880 | + NSLog(@"video outputPath = %@",outputPath); | ||
| 881 | + | ||
| 882 | + session.outputURL = [NSURL fileURLWithPath:outputPath]; | ||
| 883 | + | ||
| 884 | + // Optimize for network use. | ||
| 885 | + session.shouldOptimizeForNetworkUse = true; | ||
| 886 | + | ||
| 887 | + NSArray *supportedTypeArray = session.supportedFileTypes; | ||
| 888 | + if ([supportedTypeArray containsObject:AVFileTypeMPEG4]) { | ||
| 889 | + session.outputFileType = AVFileTypeMPEG4; | ||
| 890 | + } else if (supportedTypeArray.count == 0) { | ||
| 891 | + if (failure) { | ||
| 892 | + failure(@"该视频类型暂不支持导出", nil); | ||
| 893 | + } | ||
| 894 | + NSLog(@"No supported file types 视频类型暂不支持导出"); | ||
| 895 | + return; | ||
| 896 | + } else { | ||
| 897 | + session.outputFileType = [supportedTypeArray objectAtIndex:0]; | ||
| 898 | + } | ||
| 899 | + | ||
| 900 | + if (![[NSFileManager defaultManager] fileExistsAtPath:[NSHomeDirectory() stringByAppendingFormat:@"/tmp"]]) { | ||
| 901 | + [[NSFileManager defaultManager] createDirectoryAtPath:[NSHomeDirectory() stringByAppendingFormat:@"/tmp"] withIntermediateDirectories:YES attributes:nil error:nil]; | ||
| 902 | + } | ||
| 903 | + | ||
| 904 | + if ([TZImagePickerConfig sharedInstance].needFixComposition) { | ||
| 905 | + AVMutableVideoComposition *videoComposition = [self fixedCompositionWithAsset:videoAsset]; | ||
| 906 | + if (videoComposition.renderSize.width) { | ||
| 907 | + // 修正视频转向 | ||
| 908 | + session.videoComposition = videoComposition; | ||
| 909 | + } | ||
| 910 | + } | ||
| 911 | + | ||
| 912 | + // Begin to export video to the output path asynchronously. | ||
| 913 | +#pragma mark - 添加: 原有逻辑-会话id置空(LXG) | ||
| 914 | + self.conversationId = nil; | ||
| 915 | + [session exportAsynchronouslyWithCompletionHandler:^(void) { | ||
| 916 | + dispatch_async(dispatch_get_main_queue(), ^{ | ||
| 917 | + | ||
| 918 | + switch (session.status) { | ||
| 919 | + case AVAssetExportSessionStatusUnknown: { | ||
| 920 | + NSLog(@"AVAssetExportSessionStatusUnknown"); | ||
| 921 | + } break; | ||
| 922 | + case AVAssetExportSessionStatusWaiting: { | ||
| 923 | + NSLog(@"AVAssetExportSessionStatusWaiting"); | ||
| 924 | + } break; | ||
| 925 | + case AVAssetExportSessionStatusExporting: { | ||
| 926 | + NSLog(@"AVAssetExportSessionStatusExporting"); | ||
| 927 | + } break; | ||
| 928 | + case AVAssetExportSessionStatusCompleted: { | ||
| 929 | + NSLog(@"AVAssetExportSessionStatusCompleted"); | ||
| 930 | + if (success) { | ||
| 931 | + success(outputPath); | ||
| 932 | + } | ||
| 933 | + } break; | ||
| 934 | + case AVAssetExportSessionStatusFailed: { | ||
| 935 | + NSLog(@"AVAssetExportSessionStatusFailed"); | ||
| 936 | + if (failure) { | ||
| 937 | + failure(@"视频导出失败", session.error); | ||
| 938 | + } | ||
| 939 | + } break; | ||
| 940 | + case AVAssetExportSessionStatusCancelled: { | ||
| 941 | + NSLog(@"AVAssetExportSessionStatusCancelled"); | ||
| 942 | + if (failure) { | ||
| 943 | + failure(@"导出任务已被取消", nil); | ||
| 944 | + } | ||
| 945 | + } break; | ||
| 946 | + default: | ||
| 947 | + NSLog(@"未知错误"); | ||
| 948 | + break; | ||
| 949 | + } | ||
| 950 | + }); | ||
| 951 | + }]; | ||
| 952 | + } else { | ||
| 953 | + if (failure) { | ||
| 954 | + NSString *errorMessage = [NSString stringWithFormat:@"当前设备不支持该预设:%@", presetName]; | ||
| 955 | + failure(errorMessage, nil); | ||
| 956 | + } | ||
| 957 | + } | ||
| 958 | +} | ||
| 959 | + | ||
| 960 | +/// Judge is a assets array contain the asset 判断一个assets数组是否包含这个asset | ||
| 961 | +- (BOOL)isAssetsArray:(NSArray *)assets containAsset:(id)asset { | ||
| 962 | + if (iOS8Later) { | ||
| 963 | + return [assets containsObject:asset]; | ||
| 964 | + } else { | ||
| 965 | + NSMutableArray *selectedAssetUrls = [NSMutableArray array]; | ||
| 966 | + for (ALAsset *asset_item in assets) { | ||
| 967 | + [selectedAssetUrls addObject:[asset_item valueForProperty:ALAssetPropertyURLs]]; | ||
| 968 | + } | ||
| 969 | + return [selectedAssetUrls containsObject:[asset valueForProperty:ALAssetPropertyURLs]]; | ||
| 970 | + } | ||
| 971 | +} | ||
| 972 | + | ||
| 973 | +- (BOOL)isCameraRollAlbum:(id)metadata { | ||
| 974 | + if ([metadata isKindOfClass:[PHAssetCollection class]]) { | ||
| 975 | + NSString *versionStr = [[UIDevice currentDevice].systemVersion stringByReplacingOccurrencesOfString:@"." withString:@""]; | ||
| 976 | + if (versionStr.length <= 1) { | ||
| 977 | + versionStr = [versionStr stringByAppendingString:@"00"]; | ||
| 978 | + } else if (versionStr.length <= 2) { | ||
| 979 | + versionStr = [versionStr stringByAppendingString:@"0"]; | ||
| 980 | + } | ||
| 981 | + CGFloat version = versionStr.floatValue; | ||
| 982 | + // 目前已知8.0.0 ~ 8.0.2系统,拍照后的图片会保存在最近添加中 | ||
| 983 | + if (version >= 800 && version <= 802) { | ||
| 984 | + return ((PHAssetCollection *)metadata).assetCollectionSubtype == PHAssetCollectionSubtypeSmartAlbumRecentlyAdded; | ||
| 985 | + } else { | ||
| 986 | + return ((PHAssetCollection *)metadata).assetCollectionSubtype == PHAssetCollectionSubtypeSmartAlbumUserLibrary; | ||
| 987 | + } | ||
| 988 | + } | ||
| 989 | + if ([metadata isKindOfClass:[ALAssetsGroup class]]) { | ||
| 990 | + ALAssetsGroup *group = metadata; | ||
| 991 | + return ([[group valueForProperty:ALAssetsGroupPropertyType] intValue] == ALAssetsGroupSavedPhotos); | ||
| 992 | + } | ||
| 993 | + | ||
| 994 | + return NO; | ||
| 995 | +} | ||
| 996 | + | ||
| 997 | +- (NSString *)getAssetIdentifier:(id)asset { | ||
| 998 | + if (iOS8Later) { | ||
| 999 | + PHAsset *phAsset = (PHAsset *)asset; | ||
| 1000 | + return phAsset.localIdentifier; | ||
| 1001 | + } else { | ||
| 1002 | + ALAsset *alAsset = (ALAsset *)asset; | ||
| 1003 | + NSURL *assetUrl = [alAsset valueForProperty:ALAssetPropertyAssetURL]; | ||
| 1004 | + return assetUrl.absoluteString; | ||
| 1005 | + } | ||
| 1006 | +} | ||
| 1007 | + | ||
| 1008 | +/// 检查照片大小是否满足最小要求 | ||
| 1009 | +- (BOOL)isPhotoSelectableWithAsset:(id)asset { | ||
| 1010 | + CGSize photoSize = [self photoSizeWithAsset:asset]; | ||
| 1011 | + if (self.minPhotoWidthSelectable > photoSize.width || self.minPhotoHeightSelectable > photoSize.height) { | ||
| 1012 | + return NO; | ||
| 1013 | + } | ||
| 1014 | + return YES; | ||
| 1015 | +} | ||
| 1016 | + | ||
| 1017 | +- (CGSize)photoSizeWithAsset:(id)asset { | ||
| 1018 | + if (iOS8Later) { | ||
| 1019 | + PHAsset *phAsset = (PHAsset *)asset; | ||
| 1020 | + return CGSizeMake(phAsset.pixelWidth, phAsset.pixelHeight); | ||
| 1021 | + } else { | ||
| 1022 | + ALAsset *alAsset = (ALAsset *)asset; | ||
| 1023 | + return alAsset.defaultRepresentation.dimensions; | ||
| 1024 | + } | ||
| 1025 | +} | ||
| 1026 | + | ||
| 1027 | +#pragma mark - Private Method | ||
| 1028 | + | ||
| 1029 | +- (TZAlbumModel *)modelWithResult:(id)result name:(NSString *)name isCameraRoll:(BOOL)isCameraRoll needFetchAssets:(BOOL)needFetchAssets { | ||
| 1030 | + TZAlbumModel *model = [[TZAlbumModel alloc] init]; | ||
| 1031 | + [model setResult:result needFetchAssets:needFetchAssets]; | ||
| 1032 | + model.name = name; | ||
| 1033 | + model.isCameraRoll = isCameraRoll; | ||
| 1034 | + if ([result isKindOfClass:[PHFetchResult class]]) { | ||
| 1035 | + PHFetchResult *fetchResult = (PHFetchResult *)result; | ||
| 1036 | + model.count = fetchResult.count; | ||
| 1037 | + } else if ([result isKindOfClass:[ALAssetsGroup class]]) { | ||
| 1038 | + ALAssetsGroup *group = (ALAssetsGroup *)result; | ||
| 1039 | + model.count = [group numberOfAssets]; | ||
| 1040 | + } | ||
| 1041 | + return model; | ||
| 1042 | +} | ||
| 1043 | + | ||
| 1044 | +/// 缩放图片至新尺寸 | ||
| 1045 | +- (UIImage *)scaleImage:(UIImage *)image toSize:(CGSize)size { | ||
| 1046 | + if (image.size.width > size.width) { | ||
| 1047 | + UIGraphicsBeginImageContext(size); | ||
| 1048 | + [image drawInRect:CGRectMake(0, 0, size.width, size.height)]; | ||
| 1049 | + UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); | ||
| 1050 | + UIGraphicsEndImageContext(); | ||
| 1051 | + return newImage; | ||
| 1052 | + | ||
| 1053 | + /* 好像不怎么管用:https://mp.weixin.qq.com/s/CiqMlEIp1Ir2EJSDGgMooQ | ||
| 1054 | + CGFloat maxPixelSize = MAX(size.width, size.height); | ||
| 1055 | + CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)UIImageJPEGRepresentation(image, 0.9), nil); | ||
| 1056 | + NSDictionary *options = @{(__bridge id)kCGImageSourceCreateThumbnailFromImageAlways:(__bridge id)kCFBooleanTrue, | ||
| 1057 | + (__bridge id)kCGImageSourceThumbnailMaxPixelSize:[NSNumber numberWithFloat:maxPixelSize] | ||
| 1058 | + }; | ||
| 1059 | + CGImageRef imageRef = CGImageSourceCreateImageAtIndex(sourceRef, 0, (__bridge CFDictionaryRef)options); | ||
| 1060 | + UIImage *newImage = [UIImage imageWithCGImage:imageRef scale:2 orientation:image.imageOrientation]; | ||
| 1061 | + CGImageRelease(imageRef); | ||
| 1062 | + CFRelease(sourceRef); | ||
| 1063 | + return newImage; | ||
| 1064 | + */ | ||
| 1065 | + } else { | ||
| 1066 | + return image; | ||
| 1067 | + } | ||
| 1068 | +} | ||
| 1069 | + | ||
| 1070 | +/// 判断asset是否是视频 | ||
| 1071 | +- (BOOL)isVideo:(id)asset { | ||
| 1072 | + if (iOS8Later) { | ||
| 1073 | + PHAsset *phAsset = asset; | ||
| 1074 | + return phAsset.mediaType == PHAssetMediaTypeVideo; | ||
| 1075 | + } else { | ||
| 1076 | + ALAsset *alAsset = asset; | ||
| 1077 | + NSString *alAssetType = [[alAsset valueForProperty:ALAssetPropertyType] stringValue]; | ||
| 1078 | + return [alAssetType isEqualToString:ALAssetTypeVideo]; | ||
| 1079 | + } | ||
| 1080 | +} | ||
| 1081 | + | ||
| 1082 | +- (ALAssetOrientation)orientationFromImage:(UIImage *)image { | ||
| 1083 | + NSInteger orientation = image.imageOrientation; | ||
| 1084 | + return orientation; | ||
| 1085 | +} | ||
| 1086 | + | ||
| 1087 | +/// 获取优化后的视频转向信息 | ||
| 1088 | +- (AVMutableVideoComposition *)fixedCompositionWithAsset:(AVAsset *)videoAsset { | ||
| 1089 | + AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition]; | ||
| 1090 | + // 视频转向 | ||
| 1091 | + int degrees = [self degressFromVideoFileWithAsset:videoAsset]; | ||
| 1092 | + if (degrees != 0) { | ||
| 1093 | + CGAffineTransform translateToCenter; | ||
| 1094 | + CGAffineTransform mixedTransform; | ||
| 1095 | + videoComposition.frameDuration = CMTimeMake(1, 30); | ||
| 1096 | + | ||
| 1097 | + NSArray *tracks = [videoAsset tracksWithMediaType:AVMediaTypeVideo]; | ||
| 1098 | + AVAssetTrack *videoTrack = [tracks objectAtIndex:0]; | ||
| 1099 | + | ||
| 1100 | + AVMutableVideoCompositionInstruction *roateInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; | ||
| 1101 | + roateInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, [videoAsset duration]); | ||
| 1102 | + AVMutableVideoCompositionLayerInstruction *roateLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack]; | ||
| 1103 | + | ||
| 1104 | + if (degrees == 90) { | ||
| 1105 | + // 顺时针旋转90° | ||
| 1106 | + translateToCenter = CGAffineTransformMakeTranslation(videoTrack.naturalSize.height, 0.0); | ||
| 1107 | + mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI_2); | ||
| 1108 | + videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.height,videoTrack.naturalSize.width); | ||
| 1109 | + [roateLayerInstruction setTransform:mixedTransform atTime:kCMTimeZero]; | ||
| 1110 | + } else if(degrees == 180){ | ||
| 1111 | + // 顺时针旋转180° | ||
| 1112 | + translateToCenter = CGAffineTransformMakeTranslation(videoTrack.naturalSize.width, videoTrack.naturalSize.height); | ||
| 1113 | + mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI); | ||
| 1114 | + videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.width,videoTrack.naturalSize.height); | ||
| 1115 | + [roateLayerInstruction setTransform:mixedTransform atTime:kCMTimeZero]; | ||
| 1116 | + } else if(degrees == 270){ | ||
| 1117 | + // 顺时针旋转270° | ||
| 1118 | + translateToCenter = CGAffineTransformMakeTranslation(0.0, videoTrack.naturalSize.width); | ||
| 1119 | + mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI_2*3.0); | ||
| 1120 | + videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.height,videoTrack.naturalSize.width); | ||
| 1121 | + [roateLayerInstruction setTransform:mixedTransform atTime:kCMTimeZero]; | ||
| 1122 | + } | ||
| 1123 | + | ||
| 1124 | + roateInstruction.layerInstructions = @[roateLayerInstruction]; | ||
| 1125 | + // 加入视频方向信息 | ||
| 1126 | + videoComposition.instructions = @[roateInstruction]; | ||
| 1127 | + } | ||
| 1128 | + return videoComposition; | ||
| 1129 | +} | ||
| 1130 | + | ||
| 1131 | +/// 获取视频角度 | ||
| 1132 | +- (int)degressFromVideoFileWithAsset:(AVAsset *)asset { | ||
| 1133 | + int degress = 0; | ||
| 1134 | + NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo]; | ||
| 1135 | + if([tracks count] > 0) { | ||
| 1136 | + AVAssetTrack *videoTrack = [tracks objectAtIndex:0]; | ||
| 1137 | + CGAffineTransform t = videoTrack.preferredTransform; | ||
| 1138 | + if(t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0){ | ||
| 1139 | + // Portrait | ||
| 1140 | + degress = 90; | ||
| 1141 | + } else if(t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0){ | ||
| 1142 | + // PortraitUpsideDown | ||
| 1143 | + degress = 270; | ||
| 1144 | + } else if(t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0){ | ||
| 1145 | + // LandscapeRight | ||
| 1146 | + degress = 0; | ||
| 1147 | + } else if(t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0){ | ||
| 1148 | + // LandscapeLeft | ||
| 1149 | + degress = 180; | ||
| 1150 | + } | ||
| 1151 | + } | ||
| 1152 | + return degress; | ||
| 1153 | +} | ||
| 1154 | + | ||
| 1155 | +/// 修正图片转向 | ||
| 1156 | +- (UIImage *)fixOrientation:(UIImage *)aImage { | ||
| 1157 | + if (!self.shouldFixOrientation) return aImage; | ||
| 1158 | + | ||
| 1159 | + // No-op if the orientation is already correct | ||
| 1160 | + if (aImage.imageOrientation == UIImageOrientationUp) | ||
| 1161 | + return aImage; | ||
| 1162 | + | ||
| 1163 | + // We need to calculate the proper transformation to make the image upright. | ||
| 1164 | + // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored. | ||
| 1165 | + CGAffineTransform transform = CGAffineTransformIdentity; | ||
| 1166 | + | ||
| 1167 | + switch (aImage.imageOrientation) { | ||
| 1168 | + case UIImageOrientationDown: | ||
| 1169 | + case UIImageOrientationDownMirrored: | ||
| 1170 | + transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height); | ||
| 1171 | + transform = CGAffineTransformRotate(transform, M_PI); | ||
| 1172 | + break; | ||
| 1173 | + | ||
| 1174 | + case UIImageOrientationLeft: | ||
| 1175 | + case UIImageOrientationLeftMirrored: | ||
| 1176 | + transform = CGAffineTransformTranslate(transform, aImage.size.width, 0); | ||
| 1177 | + transform = CGAffineTransformRotate(transform, M_PI_2); | ||
| 1178 | + break; | ||
| 1179 | + | ||
| 1180 | + case UIImageOrientationRight: | ||
| 1181 | + case UIImageOrientationRightMirrored: | ||
| 1182 | + transform = CGAffineTransformTranslate(transform, 0, aImage.size.height); | ||
| 1183 | + transform = CGAffineTransformRotate(transform, -M_PI_2); | ||
| 1184 | + break; | ||
| 1185 | + default: | ||
| 1186 | + break; | ||
| 1187 | + } | ||
| 1188 | + | ||
| 1189 | + switch (aImage.imageOrientation) { | ||
| 1190 | + case UIImageOrientationUpMirrored: | ||
| 1191 | + case UIImageOrientationDownMirrored: | ||
| 1192 | + transform = CGAffineTransformTranslate(transform, aImage.size.width, 0); | ||
| 1193 | + transform = CGAffineTransformScale(transform, -1, 1); | ||
| 1194 | + break; | ||
| 1195 | + | ||
| 1196 | + case UIImageOrientationLeftMirrored: | ||
| 1197 | + case UIImageOrientationRightMirrored: | ||
| 1198 | + transform = CGAffineTransformTranslate(transform, aImage.size.height, 0); | ||
| 1199 | + transform = CGAffineTransformScale(transform, -1, 1); | ||
| 1200 | + break; | ||
| 1201 | + default: | ||
| 1202 | + break; | ||
| 1203 | + } | ||
| 1204 | + | ||
| 1205 | + // Now we draw the underlying CGImage into a new context, applying the transform | ||
| 1206 | + // calculated above. | ||
| 1207 | + CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height, | ||
| 1208 | + CGImageGetBitsPerComponent(aImage.CGImage), 0, | ||
| 1209 | + CGImageGetColorSpace(aImage.CGImage), | ||
| 1210 | + CGImageGetBitmapInfo(aImage.CGImage)); | ||
| 1211 | + CGContextConcatCTM(ctx, transform); | ||
| 1212 | + switch (aImage.imageOrientation) { | ||
| 1213 | + case UIImageOrientationLeft: | ||
| 1214 | + case UIImageOrientationLeftMirrored: | ||
| 1215 | + case UIImageOrientationRight: | ||
| 1216 | + case UIImageOrientationRightMirrored: | ||
| 1217 | + // Grr... | ||
| 1218 | + CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage); | ||
| 1219 | + break; | ||
| 1220 | + | ||
| 1221 | + default: | ||
| 1222 | + CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage); | ||
| 1223 | + break; | ||
| 1224 | + } | ||
| 1225 | + | ||
| 1226 | + // And now we just create a new UIImage from the drawing context | ||
| 1227 | + CGImageRef cgimg = CGBitmapContextCreateImage(ctx); | ||
| 1228 | + UIImage *img = [UIImage imageWithCGImage:cgimg]; | ||
| 1229 | + CGContextRelease(ctx); | ||
| 1230 | + CGImageRelease(cgimg); | ||
| 1231 | + return img; | ||
| 1232 | +} | ||
| 1233 | + | ||
| 1234 | +#pragma clang diagnostic pop | ||
| 1235 | + | ||
| 1236 | +@end | ||
| 1237 | + | ||
| 1238 | + | ||
| 1239 | +//@implementation TZSortDescriptor | ||
| 1240 | +// | ||
| 1241 | +//- (id)reversedSortDescriptor { | ||
| 1242 | +// return [NSNumber numberWithBool:![TZImageManager manager].sortAscendingByModificationDate]; | ||
| 1243 | +//} | ||
| 1244 | +// | ||
| 1245 | +//@end |
CNLiveImagePickerController/TZImagePickerController.bundle/MMVideoPreviewPlay@2x.png
0 → 100755
3.56 KB
CNLiveImagePickerController/TZImagePickerController.bundle/MMVideoPreviewPlayHL@2x.png
0 → 100755
3.56 KB
CNLiveImagePickerController/TZImagePickerController.bundle/Root.plist
0 → 100755
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
| 3 | +<plist version="1.0"> | ||
| 4 | +<dict> | ||
| 5 | + <key>StringsTable</key> | ||
| 6 | + <string>Root</string> | ||
| 7 | + <key>PreferenceSpecifiers</key> | ||
| 8 | + <array> | ||
| 9 | + <dict> | ||
| 10 | + <key>Type</key> | ||
| 11 | + <string>PSGroupSpecifier</string> | ||
| 12 | + <key>Title</key> | ||
| 13 | + <string>Group</string> | ||
| 14 | + </dict> | ||
| 15 | + <dict> | ||
| 16 | + <key>Type</key> | ||
| 17 | + <string>PSTextFieldSpecifier</string> | ||
| 18 | + <key>Title</key> | ||
| 19 | + <string>Name</string> | ||
| 20 | + <key>Key</key> | ||
| 21 | + <string>name_preference</string> | ||
| 22 | + <key>DefaultValue</key> | ||
| 23 | + <string></string> | ||
| 24 | + <key>IsSecure</key> | ||
| 25 | + <false/> | ||
| 26 | + <key>KeyboardType</key> | ||
| 27 | + <string>Alphabet</string> | ||
| 28 | + <key>AutocapitalizationType</key> | ||
| 29 | + <string>None</string> | ||
| 30 | + <key>AutocorrectionType</key> | ||
| 31 | + <string>No</string> | ||
| 32 | + </dict> | ||
| 33 | + <dict> | ||
| 34 | + <key>Type</key> | ||
| 35 | + <string>PSToggleSwitchSpecifier</string> | ||
| 36 | + <key>Title</key> | ||
| 37 | + <string>Enabled</string> | ||
| 38 | + <key>Key</key> | ||
| 39 | + <string>enabled_preference</string> | ||
| 40 | + <key>DefaultValue</key> | ||
| 41 | + <true/> | ||
| 42 | + </dict> | ||
| 43 | + <dict> | ||
| 44 | + <key>Type</key> | ||
| 45 | + <string>PSSliderSpecifier</string> | ||
| 46 | + <key>Key</key> | ||
| 47 | + <string>slider_preference</string> | ||
| 48 | + <key>DefaultValue</key> | ||
| 49 | + <real>0.5</real> | ||
| 50 | + <key>MinimumValue</key> | ||
| 51 | + <integer>0</integer> | ||
| 52 | + <key>MaximumValue</key> | ||
| 53 | + <integer>1</integer> | ||
| 54 | + <key>MinimumValueImage</key> | ||
| 55 | + <string></string> | ||
| 56 | + <key>MaximumValueImage</key> | ||
| 57 | + <string></string> | ||
| 58 | + </dict> | ||
| 59 | + </array> | ||
| 60 | +</dict> | ||
| 61 | +</plist> |
CNLiveImagePickerController/TZImagePickerController.bundle/VideoSendIcon@2x.png
0 → 100755
223 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/en.lproj/Localizable.strings
0 → 100755
CNLiveImagePickerController/TZImagePickerController.bundle/fb_quxiao@2x.png
0 → 100755
1.16 KB
CNLiveImagePickerController/TZImagePickerController.bundle/navi_back@2x.png
0 → 100755
116 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/ph_select@2x.png
0 → 100755
1.31 KB
CNLiveImagePickerController/TZImagePickerController.bundle/ph_unselect@2x.png
0 → 100755
902 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/photo_def_photoPickerVc@2x.png
0 → 100755
1.13 KB
CNLiveImagePickerController/TZImagePickerController.bundle/photo_def_previewVc@2x.png
0 → 100755
1.13 KB
CNLiveImagePickerController/TZImagePickerController.bundle/photo_number_icon@2x.png
0 → 100755
501 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/photo_original_def@2x.png
0 → 100755
1.56 KB
CNLiveImagePickerController/TZImagePickerController.bundle/photo_original_sel@2x.png
0 → 100755
620 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/photo_sel_photoPickerVc@2x.png
0 → 100755
1006 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/photo_sel_previewVc@2x.png
0 → 100755
1.16 KB
CNLiveImagePickerController/TZImagePickerController.bundle/preview_number_icon@2x.png
0 → 100755
501 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/preview_original_def@2x.png
0 → 100755
392 Bytes
CNLiveImagePickerController/TZImagePickerController.bundle/takePicture80@2x.png
0 → 100755
1.25 KB
CNLiveImagePickerController/TZImagePickerController.bundle/takePicture@2x.png
0 → 100755
2.98 KB
CNLiveImagePickerController/TZImagePickerController.bundle/upload_selection1@2x.png
0 → 100755
1.35 KB
CNLiveImagePickerController/TZImagePickerController.bundle/vi.lproj/Localizable.strings
0 → 100755
| 1 | +"OK" = "Xác nhận"; | ||
| 2 | +"Back" = "Quay lại"; | ||
| 3 | +"Done" = "Hoàn thành"; | ||
| 4 | +"Sorry" = "Xin lỗi"; | ||
| 5 | +"Cancel" = "Hủy"; | ||
| 6 | +"Setting" = "Cài đặt"; | ||
| 7 | +"Photos" = "Hình"; | ||
| 8 | +"Videos" = "Clip"; | ||
| 9 | +"Preview" = "Xem trước"; | ||
| 10 | +"Full image" = "Hình gốc"; | ||
| 11 | +"Processing..." = "Đang xử lý..."; | ||
| 12 | +"Can not use camera" = "Máy chụp hình không khả dụng"; | ||
| 13 | +"Synchronizing photos from iCloud" = "Đang đồng bộ hình ảnh từ ICloud"; | ||
| 14 | +"Can not choose both video and photo" = "Trong lúc chọn hình ảnh không cùng lúc chọn video"; | ||
| 15 | +"Can not choose both photo and GIF" = "Trong lúc chọn hình ảnh không cùng lúc chọn hình GIF"; | ||
| 16 | +"Select the video when in multi state, we will handle the video as a photo" = "Chọn hình ảnh cùng video, video sẽ bị mặc nhận thành hình ảnh và gửi đi."; | ||
| 17 | +"Can not jump to the privacy settings page, please go to the settings page by self, thank you" = "Không thể chuyển tự động qua trang cài đặt riêng tư, bạn hãy thoát ra cà điều chỉnh lại, cám ơn bạn."; | ||
| 18 | + | ||
| 19 | +"Select a maximum of %zd photos" = "Bạn chỉ được chọn nhiều nhất %zd tấm hình"; | ||
| 20 | +"Select a minimum of %zd photos" = "Chọn ít nhất %zd tấm hình"; | ||
| 21 | +"Allow %@ to access your album in \"Settings -> Privacy -> Photos\"" = "Vui lòng tại mục iPhone \" Cài đặt – quyền riêng tư - Ảnh\" mở quyền cho phép %@ truy cập ảnh."; | ||
| 22 | +"Please allow %@ to access your camera in \"Settings -> Privacy -> Camera\"" = "Vui lòng tại mục iPhone \" Cài đặt – quyền riêng tư - Ảnh\" mở quyền cho phép %@ truy cập máy ảnh"; |
CNLiveImagePickerController/TZImagePickerController.bundle/zh-Hans.lproj/Localizable.strings
0 → 100755
CNLiveImagePickerController/TZImagePickerController.bundle/zh-Hant.lproj/Localizable.strings
0 → 100755