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