Commit 89c5d1425d3781882e060e00ccd0927c716127d3
1 parent
f4202c95
提交解决问题
TODO: 1.bundle文件整理 2.宏定义改为系统方法编写
Showing
62 changed files
with
9223 additions
and
0 deletions
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
CNLiveImagePickerController/TZImagePickerController.h
0 → 100755
| 1 | +// | |
| 2 | +// TZImagePickerController.h | |
| 3 | +// TZImagePickerController | |
| 4 | +// | |
| 5 | +// Created by 谭真 on 15/12/24. | |
| 6 | +// Copyright © 2015年 谭真. All rights reserved. | |
| 7 | +// version 2.2.6 - 2018.08.21 | |
| 8 | +// 更多信息,请前往项目的github地址:https://github.com/banchichen/TZImagePickerController | |
| 9 | + | |
| 10 | +/* | |
| 11 | + 经过测试,比起xib的方式,把TZAssetCell改用纯代码的方式来写,滑动帧数明显提高了(约提高10帧左右) | |
| 12 | + | |
| 13 | + 最初发现这个问题并修复的是@小鱼周凌宇同学,她的博客地址: http://zhoulingyu.com/ | |
| 14 | + 表示感谢~ | |
| 15 | + | |
| 16 | + 原来xib确实会导致性能问题啊...大家也要注意了... | |
| 17 | + */ | |
| 18 | + | |
| 19 | +#import <UIKit/UIKit.h> | |
| 20 | +#import "TZAssetModel.h" | |
| 21 | +#import "NSBundle+TZImagePicker.h" | |
| 22 | +#import "TZImageManager.h" | |
| 23 | +#import "TZVideoPlayerController.h" | |
| 24 | +#import "TZGifPhotoPreviewController.h" | |
| 25 | +#import "TZLocationManager.h" | |
| 26 | +#import "TZPhotoPreviewController.h" | |
| 27 | + | |
| 28 | +#define iOS7Later ([UIDevice currentDevice].systemVersion.floatValue >= 7.0f) | |
| 29 | +#define iOS8Later ([UIDevice currentDevice].systemVersion.floatValue >= 8.0f) | |
| 30 | +/// | |
| 31 | +#define iOS9Later ([UIDevice currentDevice].systemVersion.floatValue >= 9.0f) | |
| 32 | +#define iOS9_1Later ([UIDevice currentDevice].systemVersion.floatValue >= 9.1f) | |
| 33 | + | |
| 34 | +@class TZAlbumCell, TZAssetCell; | |
| 35 | +@protocol TZImagePickerControllerDelegate; | |
| 36 | +@interface TZImagePickerController : UINavigationController | |
| 37 | + | |
| 38 | +#pragma mark - | |
| 39 | +/// Use this init method / 用这个初始化方法 | |
| 40 | +- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount delegate:(id<TZImagePickerControllerDelegate>)delegate; | |
| 41 | +- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount columnNumber:(NSInteger)columnNumber delegate:(id<TZImagePickerControllerDelegate>)delegate; | |
| 42 | +- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount columnNumber:(NSInteger)columnNumber delegate:(id<TZImagePickerControllerDelegate>)delegate pushPhotoPickerVc:(BOOL)pushPhotoPickerVc; | |
| 43 | +/// This init method just for previewing photos / 用这个初始化方法以预览图片 | |
| 44 | +- (instancetype)initWithSelectedAssets:(NSMutableArray *)selectedAssets selectedPhotos:(NSMutableArray *)selectedPhotos index:(NSInteger)index; | |
| 45 | +/// This init method for crop photo / 用这个初始化方法以裁剪图片 | |
| 46 | +- (instancetype)initCropTypeWithAsset:(id)asset photo:(UIImage *)photo completion:(void (^)(UIImage *cropImage,id asset))completion; | |
| 47 | + | |
| 48 | +#pragma mark - | |
| 49 | + | |
| 50 | +#pragma mark - 添加:视频编辑(LXG) | |
| 51 | +///** | |
| 52 | +// 是否允许编辑视频,选择一张时候才允许编辑,默认NO | |
| 53 | +// */ | |
| 54 | +//@property (nonatomic, assign) BOOL allowEditVideo; | |
| 55 | + | |
| 56 | +/** | |
| 57 | + 编辑视频时最大裁剪时间,单位:秒,默认10s 且最低10s | |
| 58 | + | |
| 59 | + @discussion 当该参数为10s时,所选视频时长必须大于等于10s才允许进行编辑 | |
| 60 | + */ | |
| 61 | +@property (nonatomic, assign) NSInteger maxEditVideoTime; | |
| 62 | + | |
| 63 | +/** | |
| 64 | + 允许选择视频的最大时长,单位:秒, 默认 120s | |
| 65 | + */ | |
| 66 | +@property (nonatomic, assign) NSInteger maxVideoDuration; | |
| 67 | + | |
| 68 | +/// Default is 9 / 默认最大可选9张图片 | |
| 69 | +@property (nonatomic, assign) NSInteger maxImagesCount; | |
| 70 | + | |
| 71 | +/// The minimum count photos user must pick, Default is 0 | |
| 72 | +/// 最小照片必选张数,默认是0 | |
| 73 | +@property (nonatomic, assign) NSInteger minImagesCount; | |
| 74 | + | |
| 75 | +/// Always enale the done button, not require minimum 1 photo be picked | |
| 76 | +/// 让完成按钮一直可以点击,无须最少选择一张图片 | |
| 77 | +@property (nonatomic, assign) BOOL alwaysEnableDoneBtn; | |
| 78 | + | |
| 79 | +/// Sort photos ascending by modificationDate,Default is YES | |
| 80 | +/// 对照片排序,按修改时间升序,默认是YES。如果设置为NO,最新的照片会显示在最前面,内部的拍照按钮会排在第一个 | |
| 81 | +@property (nonatomic, assign) BOOL sortAscendingByModificationDate; | |
| 82 | + | |
| 83 | +/// The pixel width of output image, Default is 828px / 导出图片的宽度,默认828像素宽 | |
| 84 | +@property (nonatomic, assign) CGFloat photoWidth; | |
| 85 | + | |
| 86 | +/// Default is 600px / 默认600像素宽 | |
| 87 | +@property (nonatomic, assign) CGFloat photoPreviewMaxWidth; | |
| 88 | + | |
| 89 | +/// Default is 15, While fetching photo, HUD will dismiss automatic if timeout; | |
| 90 | +/// 超时时间,默认为15秒,当取图片时间超过15秒还没有取成功时,会自动dismiss HUD; | |
| 91 | +@property (nonatomic, assign) NSInteger timeout; | |
| 92 | + | |
| 93 | +/// Default is YES, if set NO, the original photo button will hide. user can't picking original photo. | |
| 94 | +/// 默认为YES,如果设置为NO,原图按钮将隐藏,用户不能选择发送原图 | |
| 95 | +@property (nonatomic, assign) BOOL allowPickingOriginalPhoto; | |
| 96 | + | |
| 97 | +/// Default is YES, if set NO, user can't picking video. | |
| 98 | +/// 默认为YES,如果设置为NO,用户将不能选择视频 | |
| 99 | +@property (nonatomic, assign) BOOL allowPickingVideo; | |
| 100 | +/// Default is NO / 默认为NO,为YES时可以多选视频/gif/图片,和照片共享最大可选张数maxImagesCount的限制 | |
| 101 | +@property (nonatomic, assign) BOOL allowPickingMultipleVideo; | |
| 102 | + | |
| 103 | +/// Default is NO, if set YES, user can picking gif image. | |
| 104 | +/// 默认为NO,如果设置为YES,用户可以选择gif图片 | |
| 105 | +@property (nonatomic, assign) BOOL allowPickingGif; | |
| 106 | + | |
| 107 | +/// Default is YES, if set NO, user can't picking image. | |
| 108 | +/// 默认为YES,如果设置为NO,用户将不能选择发送图片 | |
| 109 | +@property (nonatomic, assign) BOOL allowPickingImage; | |
| 110 | + | |
| 111 | +/// Default is YES, if set NO, user can't take picture. | |
| 112 | +/// 默认为YES,如果设置为NO, 用户将不能拍摄照片 | |
| 113 | +@property (nonatomic, assign) BOOL allowTakePicture; | |
| 114 | +@property (nonatomic, assign) BOOL allowCameraLocation; | |
| 115 | + | |
| 116 | +/// Default is YES, if set NO, user can't take video. | |
| 117 | +/// 默认为YES,如果设置为NO, 用户将不能拍摄视频 | |
| 118 | +@property(nonatomic, assign) BOOL allowTakeVideo; | |
| 119 | +/// Default value is 10 minutes / 视频最大拍摄时间,默认是10分钟,单位是秒 | |
| 120 | +@property (assign, nonatomic) NSTimeInterval videoMaximumDuration; | |
| 121 | +/// Customizing UIImagePickerController's other properties, such as videoQuality / 定制UIImagePickerController的其它属性,比如视频拍摄质量videoQuality | |
| 122 | +@property (nonatomic, copy) void(^uiImagePickerControllerSettingBlock)(UIImagePickerController *imagePickerController); | |
| 123 | + | |
| 124 | +/// 首选语言,如果设置了就用该语言,不设则取当前系统语言。 | |
| 125 | +/// 由于目前只支持中文、繁体中文、英文、越南语。故该属性只支持zh-Hans、zh-Hant、en、vi四种值,其余值无效。 | |
| 126 | +@property (copy, nonatomic) NSString *preferredLanguage; | |
| 127 | + | |
| 128 | +/// 语言bundle,preferredLanguage变化时languageBundle会变化 | |
| 129 | +/// 可通过手动设置bundle,让选择器支持新的的语言(需要在设置preferredLanguage后设置languageBundle)。欢迎提交PR把语言文件提交上来~ | |
| 130 | +@property (strong, nonatomic) NSBundle *languageBundle; | |
| 131 | + | |
| 132 | +/// Default is YES, if set NO, user can't preview photo. | |
| 133 | +/// 默认为YES,如果设置为NO,预览按钮将隐藏,用户将不能去预览照片 | |
| 134 | +@property (nonatomic, assign) BOOL allowPreview; | |
| 135 | + | |
| 136 | +/// Default is YES, if set NO, the picker don't dismiss itself. | |
| 137 | +/// 默认为YES,如果设置为NO, 选择器将不会自己dismiss | |
| 138 | +@property(nonatomic, assign) BOOL autoDismiss; | |
| 139 | + | |
| 140 | +/// Default is NO, if set YES, in the delegate method the photos and infos will be nil, only assets hava value. | |
| 141 | +/// 默认为NO,如果设置为YES,代理方法里photos和infos会是nil,只返回assets | |
| 142 | +@property (assign, nonatomic) BOOL onlyReturnAsset; | |
| 143 | + | |
| 144 | +/// Default is NO, if set YES, will show the image's selected index. | |
| 145 | +/// 默认为NO,如果设置为YES,会显示照片的选中序号 | |
| 146 | +@property (assign, nonatomic) BOOL showSelectedIndex; | |
| 147 | + | |
| 148 | +/// Default is NO, if set YES, when selected photos's count up to maxImagesCount, other photo will show float layer what's color is cannotSelectLayerColor. | |
| 149 | +/// 默认是NO,如果设置为YES,当照片选择张数达到maxImagesCount时,其它照片会显示颜色为cannotSelectLayerColor的浮层 | |
| 150 | +@property (assign, nonatomic) BOOL showPhotoCannotSelectLayer; | |
| 151 | +/// Default is white color with 0.8 alpha; | |
| 152 | +@property (strong, nonatomic) UIColor *cannotSelectLayerColor; | |
| 153 | + | |
| 154 | +/// Default is No, if set YES, the result photo will not be scaled to photoWidth pixel width. The photoWidth default is 828px | |
| 155 | +/// 默认是NO,如果设置为YES,内部不会缩放图片到photoWidth像素宽 | |
| 156 | +@property (assign, nonatomic) BOOL notScaleImage; | |
| 157 | + | |
| 158 | +/// 默认是NO,如果设置为YES,导出视频时会修正转向(慎重设为YES,可能导致部分安卓下拍的视频导出失败) | |
| 159 | +@property (assign, nonatomic) BOOL needFixComposition; | |
| 160 | + | |
| 161 | +/// The photos user have selected | |
| 162 | +/// 用户选中过的图片数组 | |
| 163 | +@property (nonatomic, strong) NSMutableArray *selectedAssets; | |
| 164 | +@property (nonatomic, strong) NSMutableArray<TZAssetModel *> *selectedModels; | |
| 165 | +@property (nonatomic, strong) NSMutableArray *selectedAssetIds; | |
| 166 | +- (void)addSelectedModel:(TZAssetModel *)model; | |
| 167 | +- (void)removeSelectedModel:(TZAssetModel *)model; | |
| 168 | + | |
| 169 | +/// Minimum selectable photo width, Default is 0 | |
| 170 | +/// 最小可选中的图片宽度,默认是0,小于这个宽度的图片不可选中 | |
| 171 | +@property (nonatomic, assign) NSInteger minPhotoWidthSelectable; | |
| 172 | +@property (nonatomic, assign) NSInteger minPhotoHeightSelectable; | |
| 173 | +/// Hide the photo what can not be selected, Default is NO | |
| 174 | +/// 隐藏不可以选中的图片,默认是NO,不推荐将其设置为YES | |
| 175 | +@property (nonatomic, assign) BOOL hideWhenCanNotSelect; | |
| 176 | +/// Deprecated, Use statusBarStyle (顶部statusBar 是否为系统默认的黑色,默认为NO) | |
| 177 | +@property (nonatomic, assign) BOOL isStatusBarDefault __attribute__((deprecated("Use -statusBarStyle."))); | |
| 178 | +/// statusBar的样式,默认为UIStatusBarStyleLightContent | |
| 179 | +@property (assign, nonatomic) UIStatusBarStyle statusBarStyle; | |
| 180 | + | |
| 181 | +#pragma mark - | |
| 182 | +/// Single selection mode, valid when maxImagesCount = 1 | |
| 183 | +/// 单选模式,maxImagesCount为1时才生效 | |
| 184 | +@property (nonatomic, assign) BOOL showSelectBtn; ///< 在单选模式下,照片列表页中,显示选择按钮,默认为NO | |
| 185 | +@property (nonatomic, assign) BOOL allowCrop; ///< 允许裁剪,默认为YES,showSelectBtn为NO才生效 | |
| 186 | +@property (nonatomic, assign) CGRect cropRect; ///< 裁剪框的尺寸 | |
| 187 | +@property (nonatomic, assign) CGRect cropRectPortrait; ///< 裁剪框的尺寸(竖屏) | |
| 188 | +@property (nonatomic, assign) CGRect cropRectLandscape; ///< 裁剪框的尺寸(横屏) | |
| 189 | +@property (nonatomic, assign) BOOL needCircleCrop; ///< 需要圆形裁剪框 | |
| 190 | +@property (nonatomic, assign) NSInteger circleCropRadius; ///< 圆形裁剪框半径大小 | |
| 191 | +@property (nonatomic, copy) void (^cropViewSettingBlock)(UIView *cropView); ///< 自定义裁剪框的其他属性 | |
| 192 | +@property (nonatomic, copy) void (^navLeftBarButtonSettingBlock)(UIButton *leftButton); ///< 自定义返回按钮样式及其属性 | |
| 193 | + | |
| 194 | +/// 【自定义各页面/组件的样式】在界面初始化/组件setModel完成后调用,允许外界修改样式等 | |
| 195 | +@property (nonatomic, copy) void (^photoPickerPageUIConfigBlock)(UICollectionView *collectionView, UIView *bottomToolBar, UIButton *previewButton, UIButton *originalPhotoButton, UILabel *originalPhotoLabel, UIButton *doneButton, UIImageView *numberImageView, UILabel *numberLabel, UIView *divideLine); | |
| 196 | +@property (nonatomic, copy) void (^photoPreviewPageUIConfigBlock)(UICollectionView *collectionView, UIView *naviBar, UIButton *backButton, UIButton *selectButton, UILabel *indexLabel, UIView *toolBar, UIButton *originalPhotoButton, UILabel *originalPhotoLabel, UIButton *doneButton, UIImageView *numberImageView, UILabel *numberLabel); | |
| 197 | +@property (nonatomic, copy) void (^videoPreviewPageUIConfigBlock)(UIButton *playButton, UIView *toolBar, UIButton *doneButton); | |
| 198 | +@property (nonatomic, copy) void (^gifPreviewPageUIConfigBlock)(UIView *toolBar, UIButton *doneButton); | |
| 199 | +@property (nonatomic, copy) void (^assetCellDidSetModelBlock)(TZAssetCell *cell, UIImageView *imageView, UIImageView *selectImageView, UILabel *indexLabel, UIView *bottomView, UILabel *timeLength, UIImageView *videoImgView); | |
| 200 | +@property (nonatomic, copy) void (^albumCellDidSetModelBlock)(TZAlbumCell *cell, UIImageView *posterImageView, UILabel *titleLabel); | |
| 201 | +/// 【自定义各页面/组件的frame】在界面viewDidLayoutSubviews/组件layoutSubviews后调用,允许外界修改frame等 | |
| 202 | +@property (nonatomic, copy) void (^photoPickerPageDidLayoutSubviewsBlock)(UICollectionView *collectionView, UIView *bottomToolBar, UIButton *previewButton, UIButton *originalPhotoButton, UILabel *originalPhotoLabel, UIButton *doneButton, UIImageView *numberImageView, UILabel *numberLabel, UIView *divideLine); | |
| 203 | +@property (nonatomic, copy) void (^photoPreviewPageDidLayoutSubviewsBlock)(UICollectionView *collectionView, UIView *naviBar, UIButton *backButton, UIButton *selectButton, UILabel *indexLabel, UIView *toolBar, UIButton *originalPhotoButton, UILabel *originalPhotoLabel, UIButton *doneButton, UIImageView *numberImageView, UILabel *numberLabel); | |
| 204 | +@property (nonatomic, copy) void (^videoPreviewPageDidLayoutSubviewsBlock)(UIButton *playButton, UIView *toolBar, UIButton *doneButton); | |
| 205 | +@property (nonatomic, copy) void (^gifPreviewPageDidLayoutSubviewsBlock)(UIView *toolBar, UIButton *doneButton); | |
| 206 | +@property (nonatomic, copy) void (^assetCellDidLayoutSubviewsBlock)(TZAssetCell *cell, UIImageView *imageView, UIImageView *selectImageView, UILabel *indexLabel, UIView *bottomView, UILabel *timeLength, UIImageView *videoImgView); | |
| 207 | +@property (nonatomic, copy) void (^albumCellDidLayoutSubviewsBlock)(TZAlbumCell *cell, UIImageView *posterImageView, UILabel *titleLabel); | |
| 208 | + | |
| 209 | +#pragma mark - | |
| 210 | +- (id)showAlertWithTitle:(NSString *)title; | |
| 211 | +- (void)hideAlertView:(id)alertView; | |
| 212 | +- (void)showProgressHUD; | |
| 213 | +- (void)hideProgressHUD; | |
| 214 | +@property (nonatomic, assign) BOOL isSelectOriginalPhoto; | |
| 215 | +@property (assign, nonatomic) BOOL needShowStatusBar; | |
| 216 | + | |
| 217 | +#pragma mark - | |
| 218 | +@property (nonatomic, copy) NSString *takePictureImageName __attribute__((deprecated("Use -takePictureImage."))); | |
| 219 | +@property (nonatomic, copy) NSString *photoSelImageName __attribute__((deprecated("Use -photoSelImage."))); | |
| 220 | +@property (nonatomic, copy) NSString *photoDefImageName __attribute__((deprecated("Use -photoDefImage."))); | |
| 221 | +@property (nonatomic, copy) NSString *photoOriginSelImageName __attribute__((deprecated("Use -photoOriginSelImage."))); | |
| 222 | +@property (nonatomic, copy) NSString *photoOriginDefImageName __attribute__((deprecated("Use -photoOriginDefImage."))); | |
| 223 | +@property (nonatomic, copy) NSString *photoPreviewOriginDefImageName __attribute__((deprecated("Use -photoPreviewOriginDefImage."))); | |
| 224 | +@property (nonatomic, copy) NSString *photoNumberIconImageName __attribute__((deprecated("Use -photoNumberIconImage."))); | |
| 225 | +@property (nonatomic, strong) UIImage *takePictureImage; | |
| 226 | +@property (nonatomic, strong) UIImage *photoSelImage; | |
| 227 | +@property (nonatomic, strong) UIImage *photoDefImage; | |
| 228 | +@property (nonatomic, strong) UIImage *photoOriginSelImage; | |
| 229 | +@property (nonatomic, strong) UIImage *photoOriginDefImage; | |
| 230 | +@property (nonatomic, strong) UIImage *photoPreviewOriginDefImage; | |
| 231 | +@property (nonatomic, strong) UIImage *photoNumberIconImage; | |
| 232 | + | |
| 233 | +#pragma mark - | |
| 234 | +/// Appearance / 外观颜色 + 按钮文字 | |
| 235 | +@property (nonatomic, strong) UIColor *oKButtonTitleColorNormal; | |
| 236 | +@property (nonatomic, strong) UIColor *oKButtonTitleColorDisabled; | |
| 237 | +@property (nonatomic, strong) UIColor *naviBgColor; | |
| 238 | +@property (nonatomic, strong) UIColor *naviTitleColor; | |
| 239 | +@property (nonatomic, strong) UIFont *naviTitleFont; | |
| 240 | +@property (nonatomic, strong) UIColor *barItemTextColor; | |
| 241 | +@property (nonatomic, strong) UIFont *barItemTextFont; | |
| 242 | +@property (nonatomic, strong) UIColor *editBtnTitleColor;///LXG编辑按钮title颜色 | |
| 243 | + | |
| 244 | +@property (nonatomic, copy) NSString *doneBtnTitleStr; | |
| 245 | +@property (nonatomic, copy) NSString *cancelBtnTitleStr; | |
| 246 | +@property (nonatomic, copy) NSString *previewBtnTitleStr; | |
| 247 | +@property (nonatomic, copy) NSString *fullImageBtnTitleStr; | |
| 248 | +@property (nonatomic, copy) NSString *settingBtnTitleStr; | |
| 249 | +@property (nonatomic, copy) NSString *processHintStr; | |
| 250 | +@property (nonatomic, copy) NSString *editBtnTitleStr;///LXG编辑按钮 | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | +/// Icon theme color, default is green color like wechat, the value is r:31 g:185 b:34. Currently only support image selection icon when showSelectedIndex is YES. If you need it, please set it as soon as possible | |
| 255 | +/// icon主题色,默认是微信的绿色,值是r:31 g:185 b:34。目前仅支持showSelectedIndex为YES时的图片选中icon。如需要,请尽早设置它。 | |
| 256 | +@property (strong, nonatomic) UIColor *iconThemeColor; | |
| 257 | + | |
| 258 | +#pragma mark - | |
| 259 | +- (void)cancelButtonClick; | |
| 260 | + | |
| 261 | +// The picker should dismiss itself; when it dismissed these handle will be called. | |
| 262 | +// You can also set autoDismiss to NO, then the picker don't dismiss itself. | |
| 263 | +// If isOriginalPhoto is YES, user picked the original photo. | |
| 264 | +// You can get original photo with asset, by the method [[TZImageManager manager] getOriginalPhotoWithAsset:completion:]. | |
| 265 | +// The UIImage Object in photos default width is 828px, you can set it by photoWidth property. | |
| 266 | +// 这个照片选择器会自己dismiss,当选择器dismiss的时候,会执行下面的handle | |
| 267 | +// 你也可以设置autoDismiss属性为NO,选择器就不会自己dismis了 | |
| 268 | +// 如果isSelectOriginalPhoto为YES,表明用户选择了原图 | |
| 269 | +// 你可以通过一个asset获得原图,通过这个方法:[[TZImageManager manager] getOriginalPhotoWithAsset:completion:] | |
| 270 | +// photos数组里的UIImage对象,默认是828像素宽,你可以通过设置photoWidth属性的值来改变它 | |
| 271 | +@property (nonatomic, copy) void (^didFinishPickingPhotosHandle)(NSArray<UIImage *> *photos,NSArray *assets,BOOL isSelectOriginalPhoto); | |
| 272 | +@property (nonatomic, copy) void (^didFinishPickingPhotosWithInfosHandle)(NSArray<UIImage *> *photos,NSArray *assets,BOOL isSelectOriginalPhoto,NSArray<NSDictionary *> *infos); | |
| 273 | +@property (nonatomic, copy) void (^imagePickerControllerDidCancelHandle)(void); | |
| 274 | + | |
| 275 | +// If user picking a video, this handle will be called. | |
| 276 | +// If system version > iOS8,asset is kind of PHAsset class, else is ALAsset class. | |
| 277 | +// 如果用户选择了一个视频,下面的handle会被执行 | |
| 278 | +// 如果系统版本大于iOS8,asset是PHAsset类的对象,否则是ALAsset类的对象 | |
| 279 | +@property (nonatomic, copy) void (^didFinishPickingVideoHandle)(UIImage *coverImage,id asset); | |
| 280 | + | |
| 281 | +// If user picking a gif image, this callback will be called. | |
| 282 | +// 如果用户选择了一个gif图片,下面的handle会被执行 | |
| 283 | +@property (nonatomic, copy) void (^didFinishPickingGifImageHandle)(UIImage *animatedImage,id sourceAssets); | |
| 284 | + | |
| 285 | +@property (nonatomic, weak) id<TZImagePickerControllerDelegate> pickerDelegate; | |
| 286 | + | |
| 287 | +@end | |
| 288 | + | |
| 289 | + | |
| 290 | +@protocol TZImagePickerControllerDelegate <NSObject> | |
| 291 | +@optional | |
| 292 | +// The picker should dismiss itself; when it dismissed these handle will be called. | |
| 293 | +// You can also set autoDismiss to NO, then the picker don't dismiss itself. | |
| 294 | +// If isOriginalPhoto is YES, user picked the original photo. | |
| 295 | +// You can get original photo with asset, by the method [[TZImageManager manager] getOriginalPhotoWithAsset:completion:]. | |
| 296 | +// The UIImage Object in photos default width is 828px, you can set it by photoWidth property. | |
| 297 | +// 这个照片选择器会自己dismiss,当选择器dismiss的时候,会执行下面的handle | |
| 298 | +// 你也可以设置autoDismiss属性为NO,选择器就不会自己dismis了 | |
| 299 | +// 如果isSelectOriginalPhoto为YES,表明用户选择了原图 | |
| 300 | +// 你可以通过一个asset获得原图,通过这个方法:[[TZImageManager manager] getOriginalPhotoWithAsset:completion:] | |
| 301 | +// photos数组里的UIImage对象,默认是828像素宽,你可以通过设置photoWidth属性的值来改变它 | |
| 302 | +- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingPhotos:(NSArray<UIImage *> *)photos sourceAssets:(NSArray *)assets isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto; | |
| 303 | +- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingPhotos:(NSArray<UIImage *> *)photos sourceAssets:(NSArray *)assets isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto infos:(NSArray<NSDictionary *> *)infos; | |
| 304 | +//- (void)imagePickerControllerDidCancel:(TZImagePickerController *)picker __attribute__((deprecated("Use -tz_imagePickerControllerDidCancel:."))); | |
| 305 | +- (void)tz_imagePickerControllerDidCancel:(TZImagePickerController *)picker; | |
| 306 | + | |
| 307 | +// If user picking a video, this callback will be called. | |
| 308 | +// If system version > iOS8,asset is kind of PHAsset class, else is ALAsset class. | |
| 309 | +// 如果用户选择了一个视频,下面的handle会被执行 | |
| 310 | +// 如果系统版本大于iOS8,asset是PHAsset类的对象,否则是ALAsset类的对象 | |
| 311 | +- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingVideo:(UIImage *)coverImage sourceAssets:(id)asset; | |
| 312 | + | |
| 313 | +// If user picking a gif image, this callback will be called. | |
| 314 | +// 如果用户选择了一个gif图片,下面的handle会被执行 | |
| 315 | +- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingGifImage:(UIImage *)animatedImage sourceAssets:(id)asset; | |
| 316 | + | |
| 317 | +// Decide album show or not't | |
| 318 | +// 决定相册显示与否 albumName:相册名字 result:相册原始数据 | |
| 319 | +- (BOOL)isAlbumCanSelect:(NSString *)albumName result:(id)result; | |
| 320 | + | |
| 321 | +// Decide asset show or not't | |
| 322 | +// 决定照片显示与否 | |
| 323 | +- (BOOL)isAssetCanSelect:(id)asset; | |
| 324 | +@end | |
| 325 | + | |
| 326 | + | |
| 327 | +@interface TZAlbumPickerController : UIViewController | |
| 328 | +@property (nonatomic, assign) NSInteger columnNumber; | |
| 329 | +@property (assign, nonatomic) BOOL isFirstAppear; | |
| 330 | +- (void)configTableView; | |
| 331 | +@end | |
| 332 | + | |
| 333 | + | |
| 334 | +@interface UIImage (MyBundle) | |
| 335 | ++ (UIImage *)imageNamedFromMyBundle:(NSString *)name; | |
| 336 | +@end | |
| 337 | + | |
| 338 | + | |
| 339 | +@interface NSString (TzExtension) | |
| 340 | +- (BOOL)tz_containsString:(NSString *)string; | |
| 341 | +- (CGSize)tz_calculateSizeWithAttributes:(NSDictionary *)attributes maxSize:(CGSize)maxSize; | |
| 342 | +@end | |
| 343 | + | |
| 344 | + | |
| 345 | +@interface TZCommonTools : NSObject | |
| 346 | ++ (BOOL)tz_isIPhoneX; | |
| 347 | ++ (CGFloat)tz_statusBarHeight; | |
| 348 | +// 获得Info.plist数据字典 | |
| 349 | ++ (NSDictionary *)tz_getInfoDictionary; | |
| 350 | +@end | |
| 351 | + | |
| 352 | + | |
| 353 | +@interface TZImagePickerConfig : NSObject | |
| 354 | ++ (instancetype)sharedInstance; | |
| 355 | +@property (copy, nonatomic) NSString *preferredLanguage; | |
| 356 | +@property(nonatomic, assign) BOOL allowPickingImage; | |
| 357 | +@property (nonatomic, assign) BOOL allowPickingVideo; | |
| 358 | +@property (strong, nonatomic) NSBundle *languageBundle; | |
| 359 | +/// 默认是200,如果一个GIF过大,里面图片个数可能超过1000,会导致内存飙升而崩溃 | |
| 360 | +@property (assign, nonatomic) NSInteger gifPreviewMaxImagesCount; | |
| 361 | +@property (assign, nonatomic) BOOL showSelectedIndex; | |
| 362 | +@property (assign, nonatomic) BOOL showPhotoCannotSelectLayer; | |
| 363 | +@property (assign, nonatomic) BOOL notScaleImage; | |
| 364 | +@property (assign, nonatomic) BOOL needFixComposition; | |
| 365 | +@end | ... | ... |
CNLiveImagePickerController/TZImagePickerController.m
0 → 100755
| 1 | +// | |
| 2 | +// TZImagePickerController.m | |
| 3 | +// TZImagePickerController | |
| 4 | +// | |
| 5 | +// Created by 谭真 on 15/12/24. | |
| 6 | +// Copyright © 2015年 谭真. All rights reserved. | |
| 7 | +// version 2.2.6 - 2018.08.21 | |
| 8 | +// 更多信息,请前往项目的github地址:https://github.com/banchichen/TZImagePickerController | |
| 9 | + | |
| 10 | +#import "TZImagePickerController.h" | |
| 11 | +#import "TZPhotoPickerController.h" | |
| 12 | +#import "TZPhotoPreviewController.h" | |
| 13 | +#import "TZAssetModel.h" | |
| 14 | +#import "TZAssetCell.h" | |
| 15 | +#import "UIView+Layout.h" | |
| 16 | +#import "TZImageManager.h" | |
| 17 | +#import <sys/utsname.h> | |
| 18 | + | |
| 19 | +@interface TZImagePickerController () { | |
| 20 | + NSTimer *_timer; | |
| 21 | + UILabel *_tipLabel; | |
| 22 | + UIButton *_settingBtn; | |
| 23 | + BOOL _pushPhotoPickerVc; | |
| 24 | + BOOL _didPushPhotoPickerVc; | |
| 25 | + | |
| 26 | + UIButton *_progressHUD; | |
| 27 | + UIView *_HUDContainer; | |
| 28 | + UIActivityIndicatorView *_HUDIndicatorView; | |
| 29 | + UILabel *_HUDLabel; | |
| 30 | + | |
| 31 | + UIStatusBarStyle _originStatusBarStyle; | |
| 32 | +} | |
| 33 | +/// Default is 4, Use in photos collectionView in TZPhotoPickerController | |
| 34 | +/// 默认4列, TZPhotoPickerController中的照片collectionView | |
| 35 | +@property (nonatomic, assign) NSInteger columnNumber; | |
| 36 | +@end | |
| 37 | + | |
| 38 | +@implementation TZImagePickerController | |
| 39 | + | |
| 40 | +- (instancetype)init { | |
| 41 | + self = [super init]; | |
| 42 | + if (self) { | |
| 43 | + self = [self initWithMaxImagesCount:9 delegate:nil]; | |
| 44 | + } | |
| 45 | + return self; | |
| 46 | +} | |
| 47 | + | |
| 48 | +#pragma clang diagnostic push | |
| 49 | +#pragma clang diagnostic ignored "-Wdeprecated-declarations" | |
| 50 | +- (void)viewDidLoad { | |
| 51 | + [super viewDidLoad]; | |
| 52 | + self.needShowStatusBar = ![UIApplication sharedApplication].statusBarHidden; | |
| 53 | + self.view.backgroundColor = [UIColor whiteColor]; | |
| 54 | + self.navigationBar.barStyle = UIBarStyleBlack; | |
| 55 | + self.navigationBar.translucent = YES; | |
| 56 | + [TZImageManager manager].shouldFixOrientation = NO; | |
| 57 | + | |
| 58 | + // Default appearance, you can reset these after this method | |
| 59 | + // 默认的外观,你可以在这个方法后重置 | |
| 60 | + self.oKButtonTitleColorNormal = [UIColor colorWithRed:(83/255.0) green:(179/255.0) blue:(17/255.0) alpha:1.0]; | |
| 61 | + self.oKButtonTitleColorDisabled = [UIColor colorWithRed:(83/255.0) green:(179/255.0) blue:(17/255.0) alpha:0.5]; | |
| 62 | + self.editBtnTitleColor = [UIColor colorWithRed:(83/255.0) green:(179/255.0) blue:(17/255.0) alpha:1.0];///LXG 默认编辑按钮颜色 | |
| 63 | + | |
| 64 | + if (iOS7Later) { | |
| 65 | + self.navigationBar.barTintColor = [UIColor colorWithRed:(34/255.0) green:(34/255.0) blue:(34/255.0) alpha:1.0]; | |
| 66 | + self.navigationBar.tintColor = [UIColor whiteColor]; | |
| 67 | + self.automaticallyAdjustsScrollViewInsets = NO; | |
| 68 | + if (self.needShowStatusBar) [UIApplication sharedApplication].statusBarHidden = NO; | |
| 69 | + } | |
| 70 | +} | |
| 71 | + | |
| 72 | +- (void)setNaviBgColor:(UIColor *)naviBgColor { | |
| 73 | + _naviBgColor = naviBgColor; | |
| 74 | + if (iOS7Later) { | |
| 75 | + self.navigationBar.barTintColor = naviBgColor; | |
| 76 | + } | |
| 77 | +} | |
| 78 | + | |
| 79 | +- (void)setNaviTitleColor:(UIColor *)naviTitleColor { | |
| 80 | + _naviTitleColor = naviTitleColor; | |
| 81 | + [self configNaviTitleAppearance]; | |
| 82 | +} | |
| 83 | + | |
| 84 | +- (void)setNaviTitleFont:(UIFont *)naviTitleFont { | |
| 85 | + _naviTitleFont = naviTitleFont; | |
| 86 | + [self configNaviTitleAppearance]; | |
| 87 | +} | |
| 88 | + | |
| 89 | +- (void)configNaviTitleAppearance { | |
| 90 | + NSMutableDictionary *textAttrs = [NSMutableDictionary dictionary]; | |
| 91 | + if (self.naviTitleColor) { | |
| 92 | + textAttrs[NSForegroundColorAttributeName] = self.naviTitleColor; | |
| 93 | + } | |
| 94 | + if (self.naviTitleFont) { | |
| 95 | + textAttrs[NSFontAttributeName] = self.naviTitleFont; | |
| 96 | + } | |
| 97 | + self.navigationBar.titleTextAttributes = textAttrs; | |
| 98 | +} | |
| 99 | + | |
| 100 | +- (void)setBarItemTextFont:(UIFont *)barItemTextFont { | |
| 101 | + _barItemTextFont = barItemTextFont; | |
| 102 | + [self configBarButtonItemAppearance]; | |
| 103 | +} | |
| 104 | + | |
| 105 | +- (void)setBarItemTextColor:(UIColor *)barItemTextColor { | |
| 106 | + _barItemTextColor = barItemTextColor; | |
| 107 | + [self configBarButtonItemAppearance]; | |
| 108 | +} | |
| 109 | + | |
| 110 | +- (void)setIsStatusBarDefault:(BOOL)isStatusBarDefault { | |
| 111 | + _isStatusBarDefault = isStatusBarDefault; | |
| 112 | + | |
| 113 | + if (isStatusBarDefault) { | |
| 114 | + self.statusBarStyle = iOS7Later ? UIStatusBarStyleDefault : UIStatusBarStyleBlackOpaque; | |
| 115 | + } else { | |
| 116 | + self.statusBarStyle = iOS7Later ? UIStatusBarStyleLightContent : UIStatusBarStyleBlackOpaque; | |
| 117 | + } | |
| 118 | +} | |
| 119 | + | |
| 120 | +- (void)configBarButtonItemAppearance { | |
| 121 | + UIBarButtonItem *barItem; | |
| 122 | + if (@available(iOS 9, *)) { | |
| 123 | + barItem = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[TZImagePickerController class]]]; | |
| 124 | + } else { | |
| 125 | + barItem = [UIBarButtonItem appearanceWhenContainedIn:[TZImagePickerController class], nil]; | |
| 126 | + } | |
| 127 | + NSMutableDictionary *textAttrs = [NSMutableDictionary dictionary]; | |
| 128 | + textAttrs[NSForegroundColorAttributeName] = self.barItemTextColor; | |
| 129 | + textAttrs[NSFontAttributeName] = self.barItemTextFont; | |
| 130 | + [barItem setTitleTextAttributes:textAttrs forState:UIControlStateNormal]; | |
| 131 | +} | |
| 132 | + | |
| 133 | +- (void)viewWillAppear:(BOOL)animated { | |
| 134 | + [super viewWillAppear:animated]; | |
| 135 | + _originStatusBarStyle = [UIApplication sharedApplication].statusBarStyle; | |
| 136 | + [UIApplication sharedApplication].statusBarStyle = self.statusBarStyle; | |
| 137 | +} | |
| 138 | + | |
| 139 | +- (void)viewWillDisappear:(BOOL)animated { | |
| 140 | + [super viewWillDisappear:animated]; | |
| 141 | + [UIApplication sharedApplication].statusBarStyle = _originStatusBarStyle; | |
| 142 | + [self hideProgressHUD]; | |
| 143 | +} | |
| 144 | + | |
| 145 | +- (UIStatusBarStyle)preferredStatusBarStyle { | |
| 146 | + return self.statusBarStyle; | |
| 147 | +} | |
| 148 | + | |
| 149 | +- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount delegate:(id<TZImagePickerControllerDelegate>)delegate { | |
| 150 | + return [self initWithMaxImagesCount:maxImagesCount columnNumber:4 delegate:delegate pushPhotoPickerVc:YES]; | |
| 151 | +} | |
| 152 | + | |
| 153 | +- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount columnNumber:(NSInteger)columnNumber delegate:(id<TZImagePickerControllerDelegate>)delegate { | |
| 154 | + return [self initWithMaxImagesCount:maxImagesCount columnNumber:columnNumber delegate:delegate pushPhotoPickerVc:YES]; | |
| 155 | +} | |
| 156 | + | |
| 157 | +- (instancetype)initWithMaxImagesCount:(NSInteger)maxImagesCount columnNumber:(NSInteger)columnNumber delegate:(id<TZImagePickerControllerDelegate>)delegate pushPhotoPickerVc:(BOOL)pushPhotoPickerVc { | |
| 158 | + _pushPhotoPickerVc = pushPhotoPickerVc; | |
| 159 | + TZAlbumPickerController *albumPickerVc = [[TZAlbumPickerController alloc] init]; | |
| 160 | + albumPickerVc.isFirstAppear = YES; | |
| 161 | + albumPickerVc.columnNumber = columnNumber; | |
| 162 | + self = [super initWithRootViewController:albumPickerVc]; | |
| 163 | + if (self) { | |
| 164 | + self.maxImagesCount = maxImagesCount > 0 ? maxImagesCount : 9; // Default is 9 / 默认最大可选9张图片 | |
| 165 | + self.pickerDelegate = delegate; | |
| 166 | + self.selectedAssets = [NSMutableArray array]; | |
| 167 | + | |
| 168 | +#pragma mark - 添加:视频编辑(LXG) | |
| 169 | + self.maxEditVideoTime = 10; | |
| 170 | + self.maxVideoDuration = 120; | |
| 171 | + // Allow user picking original photo and video, you also can set No after this method | |
| 172 | + // 默认准许用户选择原图和视频, 你也可以在这个方法后置为NO | |
| 173 | + self.allowPickingOriginalPhoto = YES; | |
| 174 | + self.allowPickingVideo = YES; | |
| 175 | + self.allowPickingImage = YES; | |
| 176 | + self.allowTakePicture = YES; | |
| 177 | + self.allowTakeVideo = YES; | |
| 178 | + self.videoMaximumDuration = 10 * 60; | |
| 179 | + self.sortAscendingByModificationDate = YES; | |
| 180 | + self.autoDismiss = YES; | |
| 181 | + self.columnNumber = columnNumber; | |
| 182 | + [self configDefaultSetting]; | |
| 183 | + | |
| 184 | + if (![[TZImageManager manager] authorizationStatusAuthorized]) { | |
| 185 | + _tipLabel = [[UILabel alloc] init]; | |
| 186 | + _tipLabel.frame = CGRectMake(8, 120, self.view.tz_width - 16, 60); | |
| 187 | + _tipLabel.textAlignment = NSTextAlignmentCenter; | |
| 188 | + _tipLabel.numberOfLines = 0; | |
| 189 | + _tipLabel.font = [UIFont systemFontOfSize:16]; | |
| 190 | + _tipLabel.textColor = [UIColor blackColor]; | |
| 191 | + | |
| 192 | + NSDictionary *infoDict = [TZCommonTools tz_getInfoDictionary]; | |
| 193 | + NSString *appName = [infoDict valueForKey:@"CFBundleDisplayName"]; | |
| 194 | + if (!appName) appName = [infoDict valueForKey:@"CFBundleName"]; | |
| 195 | + NSString *tipText = [NSString stringWithFormat:[NSBundle tz_localizedStringForKey:@"Allow %@ to access your album in \"Settings -> Privacy -> Photos\""],appName]; | |
| 196 | + _tipLabel.text = tipText; | |
| 197 | + [self.view addSubview:_tipLabel]; | |
| 198 | + | |
| 199 | + if (iOS8Later) { | |
| 200 | + _settingBtn = [UIButton buttonWithType:UIButtonTypeSystem]; | |
| 201 | + [_settingBtn setTitle:self.settingBtnTitleStr forState:UIControlStateNormal]; | |
| 202 | + _settingBtn.frame = CGRectMake(0, 180, self.view.tz_width, 44); | |
| 203 | + _settingBtn.titleLabel.font = [UIFont systemFontOfSize:18]; | |
| 204 | + [_settingBtn addTarget:self action:@selector(settingBtnClick) forControlEvents:UIControlEventTouchUpInside]; | |
| 205 | + [self.view addSubview:_settingBtn]; | |
| 206 | + } | |
| 207 | + | |
| 208 | + if ([TZImageManager authorizationStatus] == 0) { | |
| 209 | + _timer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(observeAuthrizationStatusChange) userInfo:nil repeats:NO]; | |
| 210 | + } | |
| 211 | + } else { | |
| 212 | + [self pushPhotoPickerVc]; | |
| 213 | + } | |
| 214 | + } | |
| 215 | + return self; | |
| 216 | +} | |
| 217 | + | |
| 218 | +/// This init method just for previewing photos / 用这个初始化方法以预览图片 | |
| 219 | +- (instancetype)initWithSelectedAssets:(NSMutableArray *)selectedAssets selectedPhotos:(NSMutableArray *)selectedPhotos index:(NSInteger)index{ | |
| 220 | + TZPhotoPreviewController *previewVc = [[TZPhotoPreviewController alloc] init]; | |
| 221 | + self = [super initWithRootViewController:previewVc]; | |
| 222 | + if (self) { | |
| 223 | + self.selectedAssets = [NSMutableArray arrayWithArray:selectedAssets]; | |
| 224 | + self.allowPickingOriginalPhoto = self.allowPickingOriginalPhoto; | |
| 225 | + [self configDefaultSetting]; | |
| 226 | + | |
| 227 | + previewVc.photos = [NSMutableArray arrayWithArray:selectedPhotos]; | |
| 228 | + previewVc.currentIndex = index; | |
| 229 | + __weak typeof(self) weakSelf = self; | |
| 230 | + [previewVc setDoneButtonClickBlockWithPreviewType:^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isSelectOriginalPhoto) { | |
| 231 | + __strong typeof(weakSelf) strongSelf = weakSelf; | |
| 232 | + [strongSelf dismissViewControllerAnimated:YES completion:^{ | |
| 233 | + if (!strongSelf) return; | |
| 234 | + if (strongSelf.didFinishPickingPhotosHandle) { | |
| 235 | + strongSelf.didFinishPickingPhotosHandle(photos,assets,isSelectOriginalPhoto); | |
| 236 | + } | |
| 237 | + }]; | |
| 238 | + }]; | |
| 239 | + } | |
| 240 | + return self; | |
| 241 | +} | |
| 242 | + | |
| 243 | +/// This init method for crop photo / 用这个初始化方法以裁剪图片 | |
| 244 | +- (instancetype)initCropTypeWithAsset:(id)asset photo:(UIImage *)photo completion:(void (^)(UIImage *cropImage,id asset))completion { | |
| 245 | + TZPhotoPreviewController *previewVc = [[TZPhotoPreviewController alloc] init]; | |
| 246 | + self = [super initWithRootViewController:previewVc]; | |
| 247 | + if (self) { | |
| 248 | + self.maxImagesCount = 1; | |
| 249 | + self.allowCrop = YES; | |
| 250 | + self.selectedAssets = [NSMutableArray arrayWithArray:@[asset]]; | |
| 251 | + [self configDefaultSetting]; | |
| 252 | + | |
| 253 | + previewVc.photos = [NSMutableArray arrayWithArray:@[photo]]; | |
| 254 | + previewVc.isCropImage = YES; | |
| 255 | + previewVc.currentIndex = 0; | |
| 256 | + __weak typeof(self) weakSelf = self; | |
| 257 | + [previewVc setDoneButtonClickBlockCropMode:^(UIImage *cropImage, id asset) { | |
| 258 | + __strong typeof(weakSelf) strongSelf = weakSelf; | |
| 259 | + [strongSelf dismissViewControllerAnimated:YES completion:^{ | |
| 260 | + if (completion) { | |
| 261 | + completion(cropImage,asset); | |
| 262 | + } | |
| 263 | + }]; | |
| 264 | + }]; | |
| 265 | + } | |
| 266 | + return self; | |
| 267 | +} | |
| 268 | + | |
| 269 | +- (void)configDefaultSetting { | |
| 270 | + self.timeout = 15; | |
| 271 | + self.photoWidth = 828.0; | |
| 272 | + self.photoPreviewMaxWidth = 600; | |
| 273 | + self.naviTitleColor = [UIColor whiteColor]; | |
| 274 | + self.naviTitleFont = [UIFont systemFontOfSize:17]; | |
| 275 | + self.barItemTextFont = [UIFont systemFontOfSize:15]; | |
| 276 | + self.barItemTextColor = [UIColor whiteColor]; | |
| 277 | + self.allowPreview = YES; | |
| 278 | + // 2.2.26版本,不主动缩放图片,降低内存占用 | |
| 279 | + self.notScaleImage = YES; | |
| 280 | + self.needFixComposition = NO; | |
| 281 | + self.statusBarStyle = UIStatusBarStyleLightContent; | |
| 282 | + self.cannotSelectLayerColor = [[UIColor whiteColor] colorWithAlphaComponent:0.8]; | |
| 283 | + self.allowCameraLocation = YES; | |
| 284 | + | |
| 285 | + self.iconThemeColor = [UIColor colorWithRed:31 / 255.0 green:185 / 255.0 blue:34 / 255.0 alpha:1.0]; | |
| 286 | + [self configDefaultBtnTitle]; | |
| 287 | + | |
| 288 | + CGFloat cropViewWH = MIN(self.view.tz_width, self.view.tz_height) / 3 * 2; | |
| 289 | + self.cropRect = CGRectMake((self.view.tz_width - cropViewWH) / 2, (self.view.tz_height - cropViewWH) / 2, cropViewWH, cropViewWH); | |
| 290 | +} | |
| 291 | + | |
| 292 | +- (void)configDefaultImageName { | |
| 293 | + self.takePictureImageName = @"takePicture80"; | |
| 294 | + self.photoSelImageName = @"photo_sel_photoPickerVc"; | |
| 295 | + self.photoDefImageName = @"photo_def_photoPickerVc"; | |
| 296 | + self.photoNumberIconImage = [self createImageWithColor:nil size:CGSizeMake(24, 24) radius:12]; // @"photo_number_icon"; | |
| 297 | + self.photoPreviewOriginDefImageName = @"preview_original_def"; | |
| 298 | + self.photoOriginDefImageName = @"photo_original_def"; | |
| 299 | + self.photoOriginSelImageName = @"photo_original_sel"; | |
| 300 | +} | |
| 301 | + | |
| 302 | +- (void)setTakePictureImageName:(NSString *)takePictureImageName { | |
| 303 | + _takePictureImageName = takePictureImageName; | |
| 304 | + _takePictureImage = [UIImage imageNamedFromMyBundle:takePictureImageName]; | |
| 305 | +} | |
| 306 | + | |
| 307 | +- (void)setPhotoSelImageName:(NSString *)photoSelImageName { | |
| 308 | + _photoSelImageName = photoSelImageName; | |
| 309 | + _photoSelImage = [UIImage imageNamedFromMyBundle:photoSelImageName]; | |
| 310 | +} | |
| 311 | + | |
| 312 | +- (void)setPhotoDefImageName:(NSString *)photoDefImageName { | |
| 313 | + _photoDefImageName = photoDefImageName; | |
| 314 | + _photoDefImage = [UIImage imageNamedFromMyBundle:photoDefImageName]; | |
| 315 | +} | |
| 316 | + | |
| 317 | +- (void)setPhotoNumberIconImageName:(NSString *)photoNumberIconImageName { | |
| 318 | + _photoNumberIconImageName = photoNumberIconImageName; | |
| 319 | + _photoNumberIconImage = [UIImage imageNamedFromMyBundle:photoNumberIconImageName]; | |
| 320 | +} | |
| 321 | + | |
| 322 | +- (void)setPhotoPreviewOriginDefImageName:(NSString *)photoPreviewOriginDefImageName { | |
| 323 | + _photoPreviewOriginDefImageName = photoPreviewOriginDefImageName; | |
| 324 | + _photoPreviewOriginDefImage = [UIImage imageNamedFromMyBundle:photoPreviewOriginDefImageName]; | |
| 325 | +} | |
| 326 | + | |
| 327 | +- (void)setPhotoOriginDefImageName:(NSString *)photoOriginDefImageName { | |
| 328 | + _photoOriginDefImageName = photoOriginDefImageName; | |
| 329 | + _photoOriginDefImage = [UIImage imageNamedFromMyBundle:photoOriginDefImageName]; | |
| 330 | +} | |
| 331 | + | |
| 332 | +- (void)setPhotoOriginSelImageName:(NSString *)photoOriginSelImageName { | |
| 333 | + _photoOriginSelImageName = photoOriginSelImageName; | |
| 334 | + _photoOriginSelImage = [UIImage imageNamedFromMyBundle:photoOriginSelImageName]; | |
| 335 | +} | |
| 336 | + | |
| 337 | +- (void)setIconThemeColor:(UIColor *)iconThemeColor { | |
| 338 | + _iconThemeColor = iconThemeColor; | |
| 339 | + [self configDefaultImageName]; | |
| 340 | +} | |
| 341 | + | |
| 342 | +- (void)configDefaultBtnTitle { | |
| 343 | + self.doneBtnTitleStr = [NSBundle tz_localizedStringForKey:@"Done"]; | |
| 344 | + self.cancelBtnTitleStr = [NSBundle tz_localizedStringForKey:@"Cancel"]; | |
| 345 | + self.previewBtnTitleStr = [NSBundle tz_localizedStringForKey:@"Preview"]; | |
| 346 | + self.fullImageBtnTitleStr = [NSBundle tz_localizedStringForKey:@"Full image"]; | |
| 347 | + self.settingBtnTitleStr = [NSBundle tz_localizedStringForKey:@"Setting"]; | |
| 348 | + self.processHintStr = [NSBundle tz_localizedStringForKey:@"Processing..."]; | |
| 349 | + self.editBtnTitleStr = [NSBundle tz_localizedStringForKey:@"Edit"]; | |
| 350 | +} | |
| 351 | + | |
| 352 | +- (void)setShowSelectedIndex:(BOOL)showSelectedIndex { | |
| 353 | + _showSelectedIndex = showSelectedIndex; | |
| 354 | + if (showSelectedIndex) { | |
| 355 | + self.photoSelImage = [self createImageWithColor:nil size:CGSizeMake(24, 24) radius:12]; | |
| 356 | + } | |
| 357 | + [TZImagePickerConfig sharedInstance].showSelectedIndex = showSelectedIndex; | |
| 358 | +} | |
| 359 | + | |
| 360 | +- (void)setShowPhotoCannotSelectLayer:(BOOL)showPhotoCannotSelectLayer { | |
| 361 | + _showPhotoCannotSelectLayer = showPhotoCannotSelectLayer; | |
| 362 | + [TZImagePickerConfig sharedInstance].showPhotoCannotSelectLayer = showPhotoCannotSelectLayer; | |
| 363 | +} | |
| 364 | + | |
| 365 | +- (void)setNotScaleImage:(BOOL)notScaleImage { | |
| 366 | + _notScaleImage = notScaleImage; | |
| 367 | + [TZImagePickerConfig sharedInstance].notScaleImage = notScaleImage; | |
| 368 | +} | |
| 369 | + | |
| 370 | +- (void)setNeedFixComposition:(BOOL)needFixComposition { | |
| 371 | + _needFixComposition = needFixComposition; | |
| 372 | + [TZImagePickerConfig sharedInstance].needFixComposition = needFixComposition; | |
| 373 | +} | |
| 374 | + | |
| 375 | +- (void)observeAuthrizationStatusChange { | |
| 376 | + [_timer invalidate]; | |
| 377 | + _timer = nil; | |
| 378 | + if ([TZImageManager authorizationStatus] == 0) { | |
| 379 | + _timer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(observeAuthrizationStatusChange) userInfo:nil repeats:NO]; | |
| 380 | + } | |
| 381 | + | |
| 382 | + if ([[TZImageManager manager] authorizationStatusAuthorized]) { | |
| 383 | + [_tipLabel removeFromSuperview]; | |
| 384 | + [_settingBtn removeFromSuperview]; | |
| 385 | + | |
| 386 | + [self pushPhotoPickerVc]; | |
| 387 | + | |
| 388 | + TZAlbumPickerController *albumPickerVc = (TZAlbumPickerController *)self.visibleViewController; | |
| 389 | + if ([albumPickerVc isKindOfClass:[TZAlbumPickerController class]]) { | |
| 390 | + [albumPickerVc configTableView]; | |
| 391 | + } | |
| 392 | + } | |
| 393 | +} | |
| 394 | + | |
| 395 | +- (void)pushPhotoPickerVc { | |
| 396 | + _didPushPhotoPickerVc = NO; | |
| 397 | + // 1.6.8 判断是否需要push到照片选择页,如果_pushPhotoPickerVc为NO,则不push | |
| 398 | + if (!_didPushPhotoPickerVc && _pushPhotoPickerVc) { | |
| 399 | + TZPhotoPickerController *photoPickerVc = [[TZPhotoPickerController alloc] init]; | |
| 400 | + photoPickerVc.isFirstAppear = YES; | |
| 401 | + photoPickerVc.columnNumber = self.columnNumber; | |
| 402 | + [[TZImageManager manager] getCameraRollAlbum:self.allowPickingVideo allowPickingImage:self.allowPickingImage needFetchAssets:NO completion:^(TZAlbumModel *model) { | |
| 403 | + photoPickerVc.model = model; | |
| 404 | + [self pushViewController:photoPickerVc animated:YES]; | |
| 405 | + self->_didPushPhotoPickerVc = YES; | |
| 406 | + }]; | |
| 407 | + } | |
| 408 | +} | |
| 409 | + | |
| 410 | +- (id)showAlertWithTitle:(NSString *)title { | |
| 411 | + if (iOS8Later) { | |
| 412 | + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:nil preferredStyle:UIAlertControllerStyleAlert]; | |
| 413 | + [alertController addAction:[UIAlertAction actionWithTitle:[NSBundle tz_localizedStringForKey:@"OK"] style:UIAlertActionStyleDefault handler:nil]]; | |
| 414 | + [self presentViewController:alertController animated:YES completion:nil]; | |
| 415 | + return alertController; | |
| 416 | + } else { | |
| 417 | + UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:title message:nil delegate:nil cancelButtonTitle:[NSBundle tz_localizedStringForKey:@"OK"] otherButtonTitles:nil, nil]; | |
| 418 | + [alertView show]; | |
| 419 | + return alertView; | |
| 420 | + } | |
| 421 | +} | |
| 422 | + | |
| 423 | +- (void)hideAlertView:(id)alertView { | |
| 424 | + if ([alertView isKindOfClass:[UIAlertController class]]) { | |
| 425 | + UIAlertController *alertC = alertView; | |
| 426 | + [alertC dismissViewControllerAnimated:YES completion:nil]; | |
| 427 | + } else if ([alertView isKindOfClass:[UIAlertView class]]) { | |
| 428 | + UIAlertView *alertV = alertView; | |
| 429 | + [alertV dismissWithClickedButtonIndex:0 animated:YES]; | |
| 430 | + } | |
| 431 | + alertView = nil; | |
| 432 | +} | |
| 433 | + | |
| 434 | +- (void)showProgressHUD { | |
| 435 | + if (!_progressHUD) { | |
| 436 | + _progressHUD = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 437 | + [_progressHUD setBackgroundColor:[UIColor clearColor]]; | |
| 438 | + _progressHUD.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);///解决多次点击内存溢出问题(lxg) | |
| 439 | + | |
| 440 | + _HUDContainer = [[UIView alloc] init]; | |
| 441 | + _HUDContainer.layer.cornerRadius = 8; | |
| 442 | + _HUDContainer.clipsToBounds = YES; | |
| 443 | + _HUDContainer.backgroundColor = [UIColor darkGrayColor]; | |
| 444 | + _HUDContainer.alpha = 0.7; | |
| 445 | + | |
| 446 | + _HUDIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite]; | |
| 447 | + | |
| 448 | + _HUDLabel = [[UILabel alloc] init]; | |
| 449 | + _HUDLabel.textAlignment = NSTextAlignmentCenter; | |
| 450 | + _HUDLabel.text = self.processHintStr; | |
| 451 | + _HUDLabel.font = [UIFont systemFontOfSize:15]; | |
| 452 | + _HUDLabel.textColor = [UIColor whiteColor]; | |
| 453 | + | |
| 454 | + [_HUDContainer addSubview:_HUDLabel]; | |
| 455 | + [_HUDContainer addSubview:_HUDIndicatorView]; | |
| 456 | + [_progressHUD addSubview:_HUDContainer]; | |
| 457 | + } | |
| 458 | + [_HUDIndicatorView startAnimating]; | |
| 459 | + [[UIApplication sharedApplication].keyWindow addSubview:_progressHUD]; | |
| 460 | + | |
| 461 | + // if over time, dismiss HUD automatic | |
| 462 | + __weak typeof(self) weakSelf = self; | |
| 463 | + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.timeout * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | |
| 464 | + __strong typeof(weakSelf) strongSelf = weakSelf; | |
| 465 | + [strongSelf hideProgressHUD]; | |
| 466 | + }); | |
| 467 | +} | |
| 468 | + | |
| 469 | +- (void)hideProgressHUD { | |
| 470 | + if (_progressHUD) { | |
| 471 | + [_HUDIndicatorView stopAnimating]; | |
| 472 | + [_progressHUD removeFromSuperview]; | |
| 473 | + } | |
| 474 | +} | |
| 475 | + | |
| 476 | +- (void)setMaxImagesCount:(NSInteger)maxImagesCount { | |
| 477 | + _maxImagesCount = maxImagesCount; | |
| 478 | + if (maxImagesCount > 1) { | |
| 479 | + _showSelectBtn = YES; | |
| 480 | + _allowCrop = NO; | |
| 481 | + } | |
| 482 | +} | |
| 483 | + | |
| 484 | +- (void)setShowSelectBtn:(BOOL)showSelectBtn { | |
| 485 | + _showSelectBtn = showSelectBtn; | |
| 486 | + // 多选模式下,不允许让showSelectBtn为NO | |
| 487 | + if (!showSelectBtn && _maxImagesCount > 1) { | |
| 488 | + _showSelectBtn = YES; | |
| 489 | + } | |
| 490 | +} | |
| 491 | + | |
| 492 | +- (void)setAllowCrop:(BOOL)allowCrop { | |
| 493 | + _allowCrop = _maxImagesCount > 1 ? NO : allowCrop; | |
| 494 | + if (allowCrop) { // 允许裁剪的时候,不能选原图和GIF | |
| 495 | + self.allowPickingOriginalPhoto = NO; | |
| 496 | + self.allowPickingGif = NO; | |
| 497 | + } | |
| 498 | +} | |
| 499 | + | |
| 500 | +- (void)setCircleCropRadius:(NSInteger)circleCropRadius { | |
| 501 | + _circleCropRadius = circleCropRadius; | |
| 502 | + self.cropRect = CGRectMake(self.view.tz_width / 2 - circleCropRadius, self.view.tz_height / 2 - _circleCropRadius, _circleCropRadius * 2, _circleCropRadius * 2); | |
| 503 | +} | |
| 504 | + | |
| 505 | +- (void)setCropRect:(CGRect)cropRect { | |
| 506 | + _cropRect = cropRect; | |
| 507 | + _cropRectPortrait = cropRect; | |
| 508 | + CGFloat widthHeight = cropRect.size.width; | |
| 509 | + _cropRectLandscape = CGRectMake((self.view.tz_height - widthHeight) / 2, cropRect.origin.x, widthHeight, widthHeight); | |
| 510 | +} | |
| 511 | + | |
| 512 | +- (void)setTimeout:(NSInteger)timeout { | |
| 513 | + _timeout = timeout; | |
| 514 | + if (timeout < 5) { | |
| 515 | + _timeout = 5; | |
| 516 | + } else if (_timeout > 60) { | |
| 517 | + _timeout = 60; | |
| 518 | + } | |
| 519 | +} | |
| 520 | + | |
| 521 | +- (void)setPickerDelegate:(id<TZImagePickerControllerDelegate>)pickerDelegate { | |
| 522 | + _pickerDelegate = pickerDelegate; | |
| 523 | + [TZImageManager manager].pickerDelegate = pickerDelegate; | |
| 524 | +} | |
| 525 | + | |
| 526 | +- (void)setColumnNumber:(NSInteger)columnNumber { | |
| 527 | + _columnNumber = columnNumber; | |
| 528 | + if (columnNumber <= 2) { | |
| 529 | + _columnNumber = 2; | |
| 530 | + } else if (columnNumber >= 6) { | |
| 531 | + _columnNumber = 6; | |
| 532 | + } | |
| 533 | + | |
| 534 | + TZAlbumPickerController *albumPickerVc = [self.childViewControllers firstObject]; | |
| 535 | + albumPickerVc.columnNumber = _columnNumber; | |
| 536 | + [TZImageManager manager].columnNumber = _columnNumber; | |
| 537 | +} | |
| 538 | + | |
| 539 | +- (void)setMinPhotoWidthSelectable:(NSInteger)minPhotoWidthSelectable { | |
| 540 | + _minPhotoWidthSelectable = minPhotoWidthSelectable; | |
| 541 | + [TZImageManager manager].minPhotoWidthSelectable = minPhotoWidthSelectable; | |
| 542 | +} | |
| 543 | + | |
| 544 | +- (void)setMinPhotoHeightSelectable:(NSInteger)minPhotoHeightSelectable { | |
| 545 | + _minPhotoHeightSelectable = minPhotoHeightSelectable; | |
| 546 | + [TZImageManager manager].minPhotoHeightSelectable = minPhotoHeightSelectable; | |
| 547 | +} | |
| 548 | + | |
| 549 | +- (void)setHideWhenCanNotSelect:(BOOL)hideWhenCanNotSelect { | |
| 550 | + _hideWhenCanNotSelect = hideWhenCanNotSelect; | |
| 551 | + [TZImageManager manager].hideWhenCanNotSelect = hideWhenCanNotSelect; | |
| 552 | +} | |
| 553 | + | |
| 554 | +- (void)setPhotoPreviewMaxWidth:(CGFloat)photoPreviewMaxWidth { | |
| 555 | + _photoPreviewMaxWidth = photoPreviewMaxWidth; | |
| 556 | + if (photoPreviewMaxWidth > 800) { | |
| 557 | + _photoPreviewMaxWidth = 800; | |
| 558 | + } else if (photoPreviewMaxWidth < 500) { | |
| 559 | + _photoPreviewMaxWidth = 500; | |
| 560 | + } | |
| 561 | + [TZImageManager manager].photoPreviewMaxWidth = _photoPreviewMaxWidth; | |
| 562 | +} | |
| 563 | + | |
| 564 | +- (void)setPhotoWidth:(CGFloat)photoWidth { | |
| 565 | + _photoWidth = photoWidth; | |
| 566 | + [TZImageManager manager].photoWidth = photoWidth; | |
| 567 | +} | |
| 568 | + | |
| 569 | +- (void)setSelectedAssets:(NSMutableArray *)selectedAssets { | |
| 570 | + _selectedAssets = selectedAssets; | |
| 571 | + _selectedModels = [NSMutableArray array]; | |
| 572 | + _selectedAssetIds = [NSMutableArray array]; | |
| 573 | + for (id asset in selectedAssets) { | |
| 574 | + TZAssetModel *model = [TZAssetModel modelWithAsset:asset type:[[TZImageManager manager] getAssetType:asset]]; | |
| 575 | + model.isSelected = YES; | |
| 576 | + [self addSelectedModel:model]; | |
| 577 | + } | |
| 578 | +} | |
| 579 | + | |
| 580 | +- (void)setAllowPickingImage:(BOOL)allowPickingImage { | |
| 581 | + _allowPickingImage = allowPickingImage; | |
| 582 | + [TZImagePickerConfig sharedInstance].allowPickingImage = allowPickingImage; | |
| 583 | + if (!allowPickingImage) { | |
| 584 | + _allowTakePicture = NO; | |
| 585 | + } | |
| 586 | +} | |
| 587 | + | |
| 588 | +- (void)setAllowPickingVideo:(BOOL)allowPickingVideo { | |
| 589 | + _allowPickingVideo = allowPickingVideo; | |
| 590 | + [TZImagePickerConfig sharedInstance].allowPickingVideo = allowPickingVideo; | |
| 591 | + if (!allowPickingVideo) { | |
| 592 | + _allowTakeVideo = NO; | |
| 593 | + } | |
| 594 | +} | |
| 595 | + | |
| 596 | +- (void)setPreferredLanguage:(NSString *)preferredLanguage { | |
| 597 | + _preferredLanguage = preferredLanguage; | |
| 598 | + [TZImagePickerConfig sharedInstance].preferredLanguage = preferredLanguage; | |
| 599 | + [self configDefaultBtnTitle]; | |
| 600 | +} | |
| 601 | + | |
| 602 | +- (void)setLanguageBundle:(NSBundle *)languageBundle { | |
| 603 | + _languageBundle = languageBundle; | |
| 604 | + [TZImagePickerConfig sharedInstance].languageBundle = languageBundle; | |
| 605 | + [self configDefaultBtnTitle]; | |
| 606 | +} | |
| 607 | + | |
| 608 | +- (void)setSortAscendingByModificationDate:(BOOL)sortAscendingByModificationDate { | |
| 609 | + _sortAscendingByModificationDate = sortAscendingByModificationDate; | |
| 610 | + [TZImageManager manager].sortAscendingByModificationDate = sortAscendingByModificationDate; | |
| 611 | +} | |
| 612 | + | |
| 613 | +- (void)settingBtnClick { | |
| 614 | + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]]; | |
| 615 | +} | |
| 616 | + | |
| 617 | +- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated { | |
| 618 | + if (iOS7Later) { | |
| 619 | + viewController.automaticallyAdjustsScrollViewInsets = NO; | |
| 620 | + } | |
| 621 | + [super pushViewController:viewController animated:animated]; | |
| 622 | +} | |
| 623 | + | |
| 624 | +- (void)dealloc { | |
| 625 | + // NSLog(@"%@ dealloc",NSStringFromClass(self.class)); | |
| 626 | +} | |
| 627 | + | |
| 628 | +- (void)addSelectedModel:(TZAssetModel *)model { | |
| 629 | + [_selectedModels addObject:model]; | |
| 630 | + NSString *assetId = [[TZImageManager manager] getAssetIdentifier:model.asset]; | |
| 631 | + [_selectedAssetIds addObject:assetId]; | |
| 632 | +} | |
| 633 | + | |
| 634 | +- (void)removeSelectedModel:(TZAssetModel *)model { | |
| 635 | + [_selectedModels removeObject:model]; | |
| 636 | + NSString *assetId = [[TZImageManager manager] getAssetIdentifier:model.asset]; | |
| 637 | + [_selectedAssetIds removeObject:assetId]; | |
| 638 | +} | |
| 639 | + | |
| 640 | +- (UIImage *)createImageWithColor:(UIColor *)color size:(CGSize)size radius:(CGFloat)radius { | |
| 641 | + if (!color) { | |
| 642 | + color = self.iconThemeColor; | |
| 643 | + } | |
| 644 | + CGRect rect = CGRectMake(0.0f, 0.0f, size.width, size.height); | |
| 645 | + UIGraphicsBeginImageContextWithOptions(rect.size, NO, [UIScreen mainScreen].scale); | |
| 646 | + CGContextRef context = UIGraphicsGetCurrentContext(); | |
| 647 | + CGContextSetFillColorWithColor(context, [color CGColor]); | |
| 648 | + UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius]; | |
| 649 | + CGContextAddPath(context, path.CGPath); | |
| 650 | + CGContextFillPath(context); | |
| 651 | + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); | |
| 652 | + UIGraphicsEndImageContext(); | |
| 653 | + return image; | |
| 654 | +} | |
| 655 | + | |
| 656 | +#pragma mark - UIContentContainer | |
| 657 | + | |
| 658 | +- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator { | |
| 659 | + [self willInterfaceOrientionChange]; | |
| 660 | + if (size.width > size.height) { | |
| 661 | + _cropRect = _cropRectLandscape; | |
| 662 | + } else { | |
| 663 | + _cropRect = _cropRectPortrait; | |
| 664 | + } | |
| 665 | +} | |
| 666 | + | |
| 667 | +- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { | |
| 668 | + [self willInterfaceOrientionChange]; | |
| 669 | + if (toInterfaceOrientation >= 3) { | |
| 670 | + _cropRect = _cropRectLandscape; | |
| 671 | + } else { | |
| 672 | + _cropRect = _cropRectPortrait; | |
| 673 | + } | |
| 674 | +} | |
| 675 | + | |
| 676 | +- (void)willInterfaceOrientionChange { | |
| 677 | + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.02 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | |
| 678 | + if (![UIApplication sharedApplication].statusBarHidden) { | |
| 679 | + if (iOS7Later && self.needShowStatusBar) [UIApplication sharedApplication].statusBarHidden = NO; | |
| 680 | + } | |
| 681 | + }); | |
| 682 | +} | |
| 683 | + | |
| 684 | +#pragma mark - Layout | |
| 685 | + | |
| 686 | +- (void)viewDidLayoutSubviews { | |
| 687 | + [super viewDidLayoutSubviews]; | |
| 688 | + | |
| 689 | + _HUDContainer.frame = CGRectMake((self.view.tz_width - 120) / 2, (self.view.tz_height - 90) / 2, 120, 90); | |
| 690 | + _HUDIndicatorView.frame = CGRectMake(45, 15, 30, 30); | |
| 691 | + _HUDLabel.frame = CGRectMake(0,40, 120, 50); | |
| 692 | +} | |
| 693 | + | |
| 694 | +#pragma mark - Public | |
| 695 | + | |
| 696 | +- (void)cancelButtonClick { | |
| 697 | + if (self.autoDismiss) { | |
| 698 | + [self dismissViewControllerAnimated:YES completion:^{ | |
| 699 | + [self callDelegateMethod]; | |
| 700 | + }]; | |
| 701 | + } else { | |
| 702 | + [self callDelegateMethod]; | |
| 703 | + } | |
| 704 | +} | |
| 705 | + | |
| 706 | +- (void)callDelegateMethod { | |
| 707 | + if ([self.pickerDelegate respondsToSelector:@selector(tz_imagePickerControllerDidCancel:)]) { | |
| 708 | + [self.pickerDelegate tz_imagePickerControllerDidCancel:self]; | |
| 709 | + } | |
| 710 | + if (self.imagePickerControllerDidCancelHandle) { | |
| 711 | + self.imagePickerControllerDidCancelHandle(); | |
| 712 | + } | |
| 713 | +} | |
| 714 | + | |
| 715 | +@end | |
| 716 | + | |
| 717 | + | |
| 718 | +@interface TZAlbumPickerController ()<UITableViewDataSource,UITableViewDelegate> { | |
| 719 | + UITableView *_tableView; | |
| 720 | +} | |
| 721 | +@property (nonatomic, strong) NSMutableArray *albumArr; | |
| 722 | +@end | |
| 723 | + | |
| 724 | +@implementation TZAlbumPickerController | |
| 725 | + | |
| 726 | +- (void)viewDidLoad { | |
| 727 | + [super viewDidLoad]; | |
| 728 | + self.isFirstAppear = YES; | |
| 729 | + self.view.backgroundColor = [UIColor whiteColor]; | |
| 730 | + | |
| 731 | + TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 732 | + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:imagePickerVc.cancelBtnTitleStr style:UIBarButtonItemStylePlain target:imagePickerVc action:@selector(cancelButtonClick)]; | |
| 733 | +} | |
| 734 | + | |
| 735 | +- (void)viewWillAppear:(BOOL)animated { | |
| 736 | + [super viewWillAppear:animated]; | |
| 737 | + TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 738 | + [imagePickerVc hideProgressHUD]; | |
| 739 | + if (imagePickerVc.allowPickingImage) { | |
| 740 | + self.navigationItem.title = [NSBundle tz_localizedStringForKey:@"Photos"]; | |
| 741 | + } else if (imagePickerVc.allowPickingVideo) { | |
| 742 | + self.navigationItem.title = [NSBundle tz_localizedStringForKey:@"Videos"]; | |
| 743 | + } | |
| 744 | + | |
| 745 | + if (self.isFirstAppear && !imagePickerVc.navLeftBarButtonSettingBlock) { | |
| 746 | + self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:[NSBundle tz_localizedStringForKey:@"Back"] style:UIBarButtonItemStylePlain target:nil action:nil]; | |
| 747 | + } | |
| 748 | + | |
| 749 | + [self configTableView]; | |
| 750 | +} | |
| 751 | + | |
| 752 | +- (void)configTableView { | |
| 753 | + if (![[TZImageManager manager] authorizationStatusAuthorized]) { | |
| 754 | + return; | |
| 755 | + } | |
| 756 | + | |
| 757 | + if (self.isFirstAppear) { | |
| 758 | + TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 759 | + [imagePickerVc showProgressHUD]; | |
| 760 | + } | |
| 761 | + | |
| 762 | + dispatch_async(dispatch_get_global_queue(0, 0), ^{ | |
| 763 | + TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 764 | + [[TZImageManager manager] getAllAlbums:imagePickerVc.allowPickingVideo allowPickingImage:imagePickerVc.allowPickingImage needFetchAssets:!self.isFirstAppear completion:^(NSArray<TZAlbumModel *> *models) { | |
| 765 | + dispatch_async(dispatch_get_main_queue(), ^{ | |
| 766 | + self->_albumArr = [NSMutableArray arrayWithArray:models]; | |
| 767 | + for (TZAlbumModel *albumModel in self->_albumArr) { | |
| 768 | + albumModel.selectedModels = imagePickerVc.selectedModels; | |
| 769 | + } | |
| 770 | + [imagePickerVc hideProgressHUD]; | |
| 771 | + | |
| 772 | + if (self.isFirstAppear) { | |
| 773 | + self.isFirstAppear = NO; | |
| 774 | + [self configTableView]; | |
| 775 | + } | |
| 776 | + | |
| 777 | + if (!self->_tableView) { | |
| 778 | + self->_tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; | |
| 779 | + self->_tableView.rowHeight = 70; | |
| 780 | + self->_tableView.tableFooterView = [[UIView alloc] init]; | |
| 781 | + self->_tableView.dataSource = self; | |
| 782 | + self->_tableView.delegate = self; | |
| 783 | + [self->_tableView registerClass:[TZAlbumCell class] forCellReuseIdentifier:@"TZAlbumCell"]; | |
| 784 | + [self.view addSubview:self->_tableView]; | |
| 785 | + } else { | |
| 786 | + [self->_tableView reloadData]; | |
| 787 | + } | |
| 788 | + }); | |
| 789 | + }]; | |
| 790 | + }); | |
| 791 | +} | |
| 792 | + | |
| 793 | +- (void)dealloc { | |
| 794 | + // NSLog(@"%@ dealloc",NSStringFromClass(self.class)); | |
| 795 | +} | |
| 796 | + | |
| 797 | +#pragma mark - Layout | |
| 798 | + | |
| 799 | +- (void)viewDidLayoutSubviews { | |
| 800 | + [super viewDidLayoutSubviews]; | |
| 801 | + | |
| 802 | + CGFloat top = 0; | |
| 803 | + CGFloat tableViewHeight = 0; | |
| 804 | + CGFloat naviBarHeight = self.navigationController.navigationBar.tz_height; | |
| 805 | + BOOL isStatusBarHidden = [UIApplication sharedApplication].isStatusBarHidden; | |
| 806 | + if (self.navigationController.navigationBar.isTranslucent) { | |
| 807 | + top = naviBarHeight; | |
| 808 | + if (iOS7Later && !isStatusBarHidden) top += [TZCommonTools tz_statusBarHeight]; | |
| 809 | + tableViewHeight = self.view.tz_height - top; | |
| 810 | + } else { | |
| 811 | + tableViewHeight = self.view.tz_height; | |
| 812 | + } | |
| 813 | + _tableView.frame = CGRectMake(0, top, self.view.tz_width, tableViewHeight); | |
| 814 | +} | |
| 815 | + | |
| 816 | +#pragma mark - UITableViewDataSource && Delegate | |
| 817 | + | |
| 818 | +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { | |
| 819 | + return _albumArr.count; | |
| 820 | +} | |
| 821 | + | |
| 822 | +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { | |
| 823 | + TZAlbumCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TZAlbumCell"]; | |
| 824 | + TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 825 | + cell.albumCellDidLayoutSubviewsBlock = imagePickerVc.albumCellDidLayoutSubviewsBlock; | |
| 826 | + cell.albumCellDidSetModelBlock = imagePickerVc.albumCellDidSetModelBlock; | |
| 827 | + cell.selectedCountButton.backgroundColor = imagePickerVc.iconThemeColor; | |
| 828 | + cell.model = _albumArr[indexPath.row]; | |
| 829 | + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; | |
| 830 | + return cell; | |
| 831 | +} | |
| 832 | + | |
| 833 | +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { | |
| 834 | + TZPhotoPickerController *photoPickerVc = [[TZPhotoPickerController alloc] init]; | |
| 835 | + photoPickerVc.columnNumber = self.columnNumber; | |
| 836 | + TZAlbumModel *model = _albumArr[indexPath.row]; | |
| 837 | + photoPickerVc.model = model; | |
| 838 | + [self.navigationController pushViewController:photoPickerVc animated:YES]; | |
| 839 | + [tableView deselectRowAtIndexPath:indexPath animated:NO]; | |
| 840 | +} | |
| 841 | + | |
| 842 | +#pragma clang diagnostic pop | |
| 843 | + | |
| 844 | +@end | |
| 845 | + | |
| 846 | + | |
| 847 | +@implementation UIImage (MyBundle) | |
| 848 | + | |
| 849 | ++ (UIImage *)imageNamedFromMyBundle:(NSString *)name { | |
| 850 | + NSBundle *imageBundle = [NSBundle tz_imagePickerBundle]; | |
| 851 | + name = [name stringByAppendingString:@"@2x"]; | |
| 852 | + NSString *imagePath = [imageBundle pathForResource:name ofType:@"png"]; | |
| 853 | + UIImage *image = [UIImage imageWithContentsOfFile:imagePath]; | |
| 854 | + if (!image) { | |
| 855 | + // 兼容业务方自己设置图片的方式 | |
| 856 | + name = [name stringByReplacingOccurrencesOfString:@"@2x" withString:@""]; | |
| 857 | + image = [UIImage imageNamed:name]; | |
| 858 | + } | |
| 859 | + return image; | |
| 860 | +} | |
| 861 | + | |
| 862 | +@end | |
| 863 | + | |
| 864 | + | |
| 865 | +@implementation NSString (TzExtension) | |
| 866 | + | |
| 867 | +- (BOOL)tz_containsString:(NSString *)string { | |
| 868 | + if (iOS8Later) { | |
| 869 | + return [self containsString:string]; | |
| 870 | + } else { | |
| 871 | + NSRange range = [self rangeOfString:string]; | |
| 872 | + return range.location != NSNotFound; | |
| 873 | + } | |
| 874 | +} | |
| 875 | + | |
| 876 | +#pragma clang diagnostic push | |
| 877 | +#pragma clang diagnostic ignored "-Wdeprecated-declarations" | |
| 878 | +- (CGSize)tz_calculateSizeWithAttributes:(NSDictionary *)attributes maxSize:(CGSize)maxSize { | |
| 879 | + CGSize size; | |
| 880 | + if (iOS7Later) { | |
| 881 | + size = [self boundingRectWithSize:maxSize options:NSStringDrawingUsesFontLeading attributes:attributes context:nil].size; | |
| 882 | + } else { | |
| 883 | + size = [self sizeWithFont:attributes[NSFontAttributeName] constrainedToSize:maxSize]; | |
| 884 | + } | |
| 885 | + return size; | |
| 886 | +} | |
| 887 | +#pragma clang diagnostic pop | |
| 888 | + | |
| 889 | +@end | |
| 890 | + | |
| 891 | + | |
| 892 | +@implementation TZCommonTools | |
| 893 | + | |
| 894 | ++ (BOOL)tz_isIPhoneX { | |
| 895 | + struct utsname systemInfo; | |
| 896 | + uname(&systemInfo); | |
| 897 | + NSString *platform = [NSString stringWithCString:systemInfo.machine encoding:NSASCIIStringEncoding]; | |
| 898 | + if ([platform isEqualToString:@"i386"] || [platform isEqualToString:@"x86_64"]) { | |
| 899 | + // 模拟器下采用屏幕的高度来判断 | |
| 900 | + return (CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(375, 812)) || | |
| 901 | + CGSizeEqualToSize([UIScreen mainScreen].bounds.size, CGSizeMake(812, 375))); | |
| 902 | + } | |
| 903 | + // iPhone10,6是美版iPhoneX 感谢hegelsu指出:https://github.com/banchichen/TZImagePickerController/issues/635 | |
| 904 | + BOOL isIPhoneX = [platform isEqualToString:@"iPhone10,3"] || [platform isEqualToString:@"iPhone10,6"]; | |
| 905 | + return isIPhoneX; | |
| 906 | +} | |
| 907 | + | |
| 908 | ++ (CGFloat)tz_statusBarHeight { | |
| 909 | + return [self tz_isIPhoneX] ? 44 : 20; | |
| 910 | +} | |
| 911 | + | |
| 912 | +// 获得Info.plist数据字典 | |
| 913 | ++ (NSDictionary *)tz_getInfoDictionary { | |
| 914 | + NSDictionary *infoDict = [NSBundle mainBundle].localizedInfoDictionary; | |
| 915 | + if (!infoDict || !infoDict.count) { | |
| 916 | + infoDict = [NSBundle mainBundle].infoDictionary; | |
| 917 | + } | |
| 918 | + if (!infoDict || !infoDict.count) { | |
| 919 | + NSString *path = [[NSBundle mainBundle] pathForResource:@"Info" ofType:@"plist"]; | |
| 920 | + infoDict = [NSDictionary dictionaryWithContentsOfFile:path]; | |
| 921 | + } | |
| 922 | + return infoDict ? infoDict : @{}; | |
| 923 | +} | |
| 924 | +@end | |
| 925 | + | |
| 926 | + | |
| 927 | +@implementation TZImagePickerConfig | |
| 928 | + | |
| 929 | ++ (instancetype)sharedInstance { | |
| 930 | + static dispatch_once_t onceToken; | |
| 931 | + static TZImagePickerConfig *config = nil; | |
| 932 | + dispatch_once(&onceToken, ^{ | |
| 933 | + if (config == nil) { | |
| 934 | + config = [[TZImagePickerConfig alloc] init]; | |
| 935 | + config.preferredLanguage = nil; | |
| 936 | + config.gifPreviewMaxImagesCount = 200; | |
| 937 | + } | |
| 938 | + }); | |
| 939 | + return config; | |
| 940 | +} | |
| 941 | + | |
| 942 | +- (void)setPreferredLanguage:(NSString *)preferredLanguage { | |
| 943 | + _preferredLanguage = preferredLanguage; | |
| 944 | + | |
| 945 | + if (!preferredLanguage || !preferredLanguage.length) { | |
| 946 | + preferredLanguage = [NSLocale preferredLanguages].firstObject; | |
| 947 | + } | |
| 948 | + if ([preferredLanguage rangeOfString:@"zh-Hans"].location != NSNotFound) { | |
| 949 | + preferredLanguage = @"zh-Hans"; | |
| 950 | + } else if ([preferredLanguage rangeOfString:@"zh-Hant"].location != NSNotFound) { | |
| 951 | + preferredLanguage = @"zh-Hant"; | |
| 952 | + } else if ([preferredLanguage rangeOfString:@"vi"].location != NSNotFound) { | |
| 953 | + preferredLanguage = @"vi"; | |
| 954 | + } else { | |
| 955 | + preferredLanguage = @"en"; | |
| 956 | + } | |
| 957 | + _languageBundle = [NSBundle bundleWithPath:[[NSBundle tz_imagePickerBundle] pathForResource:preferredLanguage ofType:@"lproj"]]; | |
| 958 | +} | |
| 959 | + | |
| 960 | +@end | ... | ... |
CNLiveImagePickerController/TZLocationManager.h
0 → 100755
| 1 | +// | |
| 2 | +// TZLocationManager.h | |
| 3 | +// TZImagePickerController | |
| 4 | +// | |
| 5 | +// Created by 谭真 on 2017/06/03. | |
| 6 | +// Copyright © 2017年 谭真. All rights reserved. | |
| 7 | +// 定位管理类 | |
| 8 | + | |
| 9 | + | |
| 10 | +#import <Foundation/Foundation.h> | |
| 11 | +#import <CoreLocation/CoreLocation.h> | |
| 12 | + | |
| 13 | +@interface TZLocationManager : NSObject | |
| 14 | + | |
| 15 | ++ (instancetype)manager; | |
| 16 | + | |
| 17 | +/// 开始定位 | |
| 18 | +- (void)startLocation; | |
| 19 | +- (void)startLocationWithSuccessBlock:(void (^)(NSArray<CLLocation *> *))successBlock failureBlock:(void (^)(NSError *error))failureBlock; | |
| 20 | +- (void)startLocationWithGeocoderBlock:(void (^)(NSArray *geocoderArray))geocoderBlock; | |
| 21 | +- (void)startLocationWithSuccessBlock:(void (^)(NSArray<CLLocation *> *))successBlock failureBlock:(void (^)(NSError *error))failureBlock geocoderBlock:(void (^)(NSArray *geocoderArray))geocoderBlock; | |
| 22 | + | |
| 23 | +@end | |
| 24 | + | ... | ... |
CNLiveImagePickerController/TZLocationManager.m
0 → 100755
| 1 | +// | |
| 2 | +// TZLocationManager.m | |
| 3 | +// TZImagePickerController | |
| 4 | +// | |
| 5 | +// Created by 谭真 on 2017/06/03. | |
| 6 | +// Copyright © 2017年 谭真. All rights reserved. | |
| 7 | +// 定位管理类 | |
| 8 | + | |
| 9 | +#import "TZLocationManager.h" | |
| 10 | +#import "TZImagePickerController.h" | |
| 11 | + | |
| 12 | +@interface TZLocationManager ()<CLLocationManagerDelegate> | |
| 13 | +@property (nonatomic, strong) CLLocationManager *locationManager; | |
| 14 | +/// 定位成功的回调block | |
| 15 | +@property (nonatomic, copy) void (^successBlock)(NSArray<CLLocation *> *); | |
| 16 | +/// 编码成功的回调block | |
| 17 | +@property (nonatomic, copy) void (^geocodeBlock)(NSArray *geocodeArray); | |
| 18 | +/// 定位失败的回调block | |
| 19 | +@property (nonatomic, copy) void (^failureBlock)(NSError *error); | |
| 20 | +@end | |
| 21 | + | |
| 22 | +@implementation TZLocationManager | |
| 23 | + | |
| 24 | ++ (instancetype)manager { | |
| 25 | + static TZLocationManager *manager; | |
| 26 | + static dispatch_once_t onceToken; | |
| 27 | + dispatch_once(&onceToken, ^{ | |
| 28 | + manager = [[self alloc] init]; | |
| 29 | + manager.locationManager = [[CLLocationManager alloc] init]; | |
| 30 | + manager.locationManager.delegate = manager; | |
| 31 | + if (iOS8Later) { | |
| 32 | + [manager.locationManager requestWhenInUseAuthorization]; | |
| 33 | + } | |
| 34 | + }); | |
| 35 | + return manager; | |
| 36 | +} | |
| 37 | + | |
| 38 | +- (void)startLocation { | |
| 39 | + [self startLocationWithSuccessBlock:nil failureBlock:nil geocoderBlock:nil]; | |
| 40 | +} | |
| 41 | + | |
| 42 | +- (void)startLocationWithSuccessBlock:(void (^)(NSArray<CLLocation *> *))successBlock failureBlock:(void (^)(NSError *error))failureBlock { | |
| 43 | + [self startLocationWithSuccessBlock:successBlock failureBlock:failureBlock geocoderBlock:nil]; | |
| 44 | +} | |
| 45 | + | |
| 46 | +- (void)startLocationWithGeocoderBlock:(void (^)(NSArray *geocoderArray))geocoderBlock { | |
| 47 | + [self startLocationWithSuccessBlock:nil failureBlock:nil geocoderBlock:geocoderBlock]; | |
| 48 | +} | |
| 49 | + | |
| 50 | +- (void)startLocationWithSuccessBlock:(void (^)(NSArray<CLLocation *> *))successBlock failureBlock:(void (^)(NSError *error))failureBlock geocoderBlock:(void (^)(NSArray *geocoderArray))geocoderBlock { | |
| 51 | + [self.locationManager startUpdatingLocation]; | |
| 52 | + _successBlock = successBlock; | |
| 53 | + _geocodeBlock = geocoderBlock; | |
| 54 | + _failureBlock = failureBlock; | |
| 55 | +} | |
| 56 | + | |
| 57 | +#pragma mark - CLLocationManagerDelegate | |
| 58 | + | |
| 59 | +/// 地理位置发生改变时触发 | |
| 60 | +- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations { | |
| 61 | + [manager stopUpdatingLocation]; | |
| 62 | + | |
| 63 | + if (_successBlock) { | |
| 64 | + _successBlock(locations); | |
| 65 | + } | |
| 66 | + | |
| 67 | + if (_geocodeBlock && locations.count) { | |
| 68 | + CLGeocoder *geocoder = [[CLGeocoder alloc] init]; | |
| 69 | + [geocoder reverseGeocodeLocation:[locations firstObject] completionHandler:^(NSArray *array, NSError *error) { | |
| 70 | + self->_geocodeBlock(array); | |
| 71 | + }]; | |
| 72 | + } | |
| 73 | +} | |
| 74 | + | |
| 75 | +/// 定位失败回调方法 | |
| 76 | +- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { | |
| 77 | + NSLog(@"定位失败, 错误: %@",error); | |
| 78 | + switch([error code]) { | |
| 79 | + case kCLErrorDenied: { // 用户禁止了定位权限 | |
| 80 | + | |
| 81 | + } break; | |
| 82 | + default: break; | |
| 83 | + } | |
| 84 | + if (_failureBlock) { | |
| 85 | + _failureBlock(error); | |
| 86 | + } | |
| 87 | +} | |
| 88 | + | |
| 89 | +@end | ... | ... |
CNLiveImagePickerController/TZPhotoPickerController.h
0 → 100755
| 1 | +// | |
| 2 | +// TZPhotoPickerController.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 | + | |
| 11 | +@class TZAlbumModel; | |
| 12 | +@interface TZPhotoPickerController : UIViewController | |
| 13 | + | |
| 14 | +@property (nonatomic, assign) BOOL isFirstAppear; | |
| 15 | +@property (nonatomic, assign) NSInteger columnNumber; | |
| 16 | +@property (nonatomic, strong) TZAlbumModel *model; | |
| 17 | +@end | |
| 18 | + | |
| 19 | + | |
| 20 | +@interface TZCollectionView : UICollectionView | |
| 21 | + | |
| 22 | +@end | ... | ... |
CNLiveImagePickerController/TZPhotoPickerController.m
0 → 100755
| 1 | +// | |
| 2 | +// TZPhotoPickerController.m | |
| 3 | +// TZImagePickerController | |
| 4 | +// | |
| 5 | +// Created by 谭真 on 15/12/24. | |
| 6 | +// Copyright © 2015年 谭真. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "TZPhotoPickerController.h" | |
| 10 | +#import "TZImagePickerController.h" | |
| 11 | +#import "TZPhotoPreviewController.h" | |
| 12 | +#import "TZAssetCell.h" | |
| 13 | +#import "TZAssetModel.h" | |
| 14 | +#import "UIView+Layout.h" | |
| 15 | +#import "TZImageManager.h" | |
| 16 | +#import "TZVideoPlayerController.h" | |
| 17 | +#import "TZGifPhotoPreviewController.h" | |
| 18 | +#import "TZLocationManager.h" | |
| 19 | +#import <MobileCoreServices/MobileCoreServices.h> | |
| 20 | +#import <AssetsLibrary/AssetsLibrary.h>///lxg | |
| 21 | + | |
| 22 | +@interface TZPhotoPickerController ()<UICollectionViewDataSource,UICollectionViewDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate,UIAlertViewDelegate> { | |
| 23 | + NSMutableArray *_models; | |
| 24 | + | |
| 25 | + UIView *_bottomToolBar; | |
| 26 | + UIButton *_previewButton; | |
| 27 | + UIButton *_doneButton; | |
| 28 | + UIImageView *_numberImageView; | |
| 29 | + UILabel *_numberLabel; | |
| 30 | + UIButton *_originalPhotoButton; | |
| 31 | + UILabel *_originalPhotoLabel; | |
| 32 | + UIView *_divideLine; | |
| 33 | + | |
| 34 | + BOOL _shouldScrollToBottom; | |
| 35 | + BOOL _showTakePhotoBtn; | |
| 36 | + | |
| 37 | + CGFloat _offsetItemCount; | |
| 38 | + | |
| 39 | + BOOL _isClickDone;///防止多次点击(LXG) | |
| 40 | +} | |
| 41 | +@property CGRect previousPreheatRect; | |
| 42 | +@property (nonatomic, assign) BOOL isSelectOriginalPhoto; | |
| 43 | +@property (nonatomic, strong) TZCollectionView *collectionView; | |
| 44 | +@property (strong, nonatomic) UICollectionViewFlowLayout *layout; | |
| 45 | +@property (nonatomic, strong) UIImagePickerController *imagePickerVc; | |
| 46 | +@property (strong, nonatomic) CLLocation *location; | |
| 47 | +@property (assign, nonatomic) BOOL useCachedImage; | |
| 48 | +@end | |
| 49 | + | |
| 50 | +static CGSize AssetGridThumbnailSize; | |
| 51 | +static CGFloat itemMargin = 5; | |
| 52 | + | |
| 53 | +@implementation TZPhotoPickerController | |
| 54 | + | |
| 55 | +#pragma clang diagnostic push | |
| 56 | +#pragma clang diagnostic ignored "-Wdeprecated-declarations" | |
| 57 | +- (UIImagePickerController *)imagePickerVc { | |
| 58 | + if (_imagePickerVc == nil) { | |
| 59 | + _imagePickerVc = [[UIImagePickerController alloc] init]; | |
| 60 | + _imagePickerVc.delegate = self; | |
| 61 | + // set appearance / 改变相册选择页的导航栏外观 | |
| 62 | + if (iOS7Later) { | |
| 63 | + _imagePickerVc.navigationBar.barTintColor = self.navigationController.navigationBar.barTintColor; | |
| 64 | + } | |
| 65 | + _imagePickerVc.navigationBar.tintColor = self.navigationController.navigationBar.tintColor; | |
| 66 | + UIBarButtonItem *tzBarItem, *BarItem; | |
| 67 | + if (@available(iOS 9, *)) { | |
| 68 | + tzBarItem = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[TZImagePickerController class]]]; | |
| 69 | + BarItem = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UIImagePickerController class]]]; | |
| 70 | + } else { | |
| 71 | + tzBarItem = [UIBarButtonItem appearanceWhenContainedIn:[TZImagePickerController class], nil]; | |
| 72 | + BarItem = [UIBarButtonItem appearanceWhenContainedIn:[UIImagePickerController class], nil]; | |
| 73 | + } | |
| 74 | + NSDictionary *titleTextAttributes = [tzBarItem titleTextAttributesForState:UIControlStateNormal]; | |
| 75 | + [BarItem setTitleTextAttributes:titleTextAttributes forState:UIControlStateNormal]; | |
| 76 | + } | |
| 77 | + return _imagePickerVc; | |
| 78 | +} | |
| 79 | + | |
| 80 | +- (void)viewDidLoad { | |
| 81 | + [super viewDidLoad]; | |
| 82 | + _isClickDone = NO; | |
| 83 | + self.isFirstAppear = YES; | |
| 84 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 85 | + _isSelectOriginalPhoto = tzImagePickerVc.isSelectOriginalPhoto; | |
| 86 | + _shouldScrollToBottom = YES; | |
| 87 | + self.view.backgroundColor = [UIColor whiteColor]; | |
| 88 | + self.navigationItem.title = _model.name; | |
| 89 | + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:tzImagePickerVc.cancelBtnTitleStr style:UIBarButtonItemStylePlain target:tzImagePickerVc action:@selector(cancelButtonClick)]; | |
| 90 | + if (tzImagePickerVc.navLeftBarButtonSettingBlock) { | |
| 91 | + UIButton *leftButton = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 92 | + leftButton.frame = CGRectMake(0, 0, 44, 44); | |
| 93 | + [leftButton addTarget:self action:@selector(navLeftBarButtonClick) forControlEvents:UIControlEventTouchUpInside]; | |
| 94 | + tzImagePickerVc.navLeftBarButtonSettingBlock(leftButton); | |
| 95 | + self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:leftButton]; | |
| 96 | + } else if (tzImagePickerVc.childViewControllers.count) { | |
| 97 | + [tzImagePickerVc.childViewControllers firstObject].navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:[NSBundle tz_localizedStringForKey:@"Back"] style:UIBarButtonItemStylePlain target:nil action:nil]; | |
| 98 | + } | |
| 99 | + _showTakePhotoBtn = _model.isCameraRoll && ((tzImagePickerVc.allowTakePicture && tzImagePickerVc.allowPickingImage) || (tzImagePickerVc.allowTakeVideo && tzImagePickerVc.allowPickingVideo)); | |
| 100 | + // [self resetCachedAssets]; | |
| 101 | + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didChangeStatusBarOrientationNotification:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; | |
| 102 | +} | |
| 103 | + | |
| 104 | +- (void)fetchAssetModels { | |
| 105 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 106 | + if (_isFirstAppear && !_model.models.count) { | |
| 107 | + [tzImagePickerVc showProgressHUD]; | |
| 108 | + } | |
| 109 | + dispatch_async(dispatch_get_global_queue(0, 0), ^{ | |
| 110 | + if (!tzImagePickerVc.sortAscendingByModificationDate && self->_isFirstAppear && iOS8Later && self->_model.isCameraRoll) { | |
| 111 | + [[TZImageManager manager] getCameraRollAlbum:tzImagePickerVc.allowPickingVideo allowPickingImage:tzImagePickerVc.allowPickingImage needFetchAssets:YES completion:^(TZAlbumModel *model) { | |
| 112 | + self->_model = model; | |
| 113 | + self->_models = [NSMutableArray arrayWithArray:self->_model.models]; | |
| 114 | + [self initSubviews]; | |
| 115 | + }]; | |
| 116 | + } else { | |
| 117 | + if (self->_showTakePhotoBtn || !iOS8Later || self->_isFirstAppear) { | |
| 118 | + [[TZImageManager manager] getAssetsFromFetchResult:self->_model.result completion:^(NSArray<TZAssetModel *> *models) { | |
| 119 | + self->_models = [NSMutableArray arrayWithArray:models]; | |
| 120 | + [self initSubviews]; | |
| 121 | + }]; | |
| 122 | + } else { | |
| 123 | + self->_models = [NSMutableArray arrayWithArray:self->_model.models]; | |
| 124 | + [self initSubviews]; | |
| 125 | + } | |
| 126 | + } | |
| 127 | + }); | |
| 128 | +} | |
| 129 | + | |
| 130 | +- (void)initSubviews { | |
| 131 | + dispatch_async(dispatch_get_main_queue(), ^{ | |
| 132 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 133 | + [tzImagePickerVc hideProgressHUD]; | |
| 134 | + | |
| 135 | + [self checkSelectedModels]; | |
| 136 | + [self configCollectionView]; | |
| 137 | + self->_collectionView.hidden = YES; | |
| 138 | + [self configBottomToolBar]; | |
| 139 | + | |
| 140 | + [self scrollCollectionViewToBottom]; | |
| 141 | + }); | |
| 142 | +} | |
| 143 | + | |
| 144 | +- (void)viewWillDisappear:(BOOL)animated { | |
| 145 | + [super viewWillDisappear:animated]; | |
| 146 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 147 | + tzImagePickerVc.isSelectOriginalPhoto = _isSelectOriginalPhoto; | |
| 148 | +} | |
| 149 | + | |
| 150 | +- (BOOL)prefersStatusBarHidden { | |
| 151 | + return NO; | |
| 152 | +} | |
| 153 | + | |
| 154 | +- (void)configCollectionView { | |
| 155 | + _layout = [[UICollectionViewFlowLayout alloc] init]; | |
| 156 | + _collectionView = [[TZCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:_layout]; | |
| 157 | + _collectionView.backgroundColor = [UIColor whiteColor]; | |
| 158 | + _collectionView.dataSource = self; | |
| 159 | + _collectionView.delegate = self; | |
| 160 | + _collectionView.alwaysBounceHorizontal = NO; | |
| 161 | + _collectionView.contentInset = UIEdgeInsetsMake(itemMargin, itemMargin, itemMargin, itemMargin); | |
| 162 | + | |
| 163 | + if (_showTakePhotoBtn) { | |
| 164 | + _collectionView.contentSize = CGSizeMake(self.view.tz_width, ((_model.count + self.columnNumber) / self.columnNumber) * self.view.tz_width); | |
| 165 | + } else { | |
| 166 | + _collectionView.contentSize = CGSizeMake(self.view.tz_width, ((_model.count + self.columnNumber - 1) / self.columnNumber) * self.view.tz_width); | |
| 167 | + } | |
| 168 | + [self.view addSubview:_collectionView]; | |
| 169 | + [_collectionView registerClass:[TZAssetCell class] forCellWithReuseIdentifier:@"TZAssetCell"]; | |
| 170 | + [_collectionView registerClass:[TZAssetCameraCell class] forCellWithReuseIdentifier:@"TZAssetCameraCell"]; | |
| 171 | +} | |
| 172 | + | |
| 173 | +- (void)viewWillAppear:(BOOL)animated { | |
| 174 | + [super viewWillAppear:animated]; | |
| 175 | + // Determine the size of the thumbnails to request from the PHCachingImageManager | |
| 176 | + CGFloat scale = 2.0; | |
| 177 | + if ([UIScreen mainScreen].bounds.size.width > 600) { | |
| 178 | + scale = 1.0; | |
| 179 | + } | |
| 180 | + CGSize cellSize = ((UICollectionViewFlowLayout *)_collectionView.collectionViewLayout).itemSize; | |
| 181 | + AssetGridThumbnailSize = CGSizeMake(cellSize.width * scale, cellSize.height * scale); | |
| 182 | + | |
| 183 | + if (!_models) { | |
| 184 | + [self fetchAssetModels]; | |
| 185 | + } | |
| 186 | +} | |
| 187 | + | |
| 188 | +- (void)viewDidAppear:(BOOL)animated { | |
| 189 | + [super viewDidAppear:animated]; | |
| 190 | + if (iOS8Later) { | |
| 191 | + // [self updateCachedAssets]; | |
| 192 | + } | |
| 193 | +} | |
| 194 | + | |
| 195 | +- (void)configBottomToolBar { | |
| 196 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 197 | + if (!tzImagePickerVc.showSelectBtn) return; | |
| 198 | + | |
| 199 | + _bottomToolBar = [[UIView alloc] initWithFrame:CGRectZero]; | |
| 200 | + CGFloat rgb = 253 / 255.0; | |
| 201 | + _bottomToolBar.backgroundColor = [UIColor colorWithRed:rgb green:rgb blue:rgb alpha:1.0]; | |
| 202 | + | |
| 203 | + _previewButton = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 204 | + [_previewButton addTarget:self action:@selector(previewButtonClick) forControlEvents:UIControlEventTouchUpInside]; | |
| 205 | + _previewButton.titleLabel.font = [UIFont systemFontOfSize:16]; | |
| 206 | + [_previewButton setTitle:tzImagePickerVc.previewBtnTitleStr forState:UIControlStateNormal]; | |
| 207 | + [_previewButton setTitle:tzImagePickerVc.previewBtnTitleStr forState:UIControlStateDisabled]; | |
| 208 | + [_previewButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; | |
| 209 | + [_previewButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateDisabled]; | |
| 210 | + _previewButton.enabled = tzImagePickerVc.selectedModels.count; | |
| 211 | + | |
| 212 | + if (tzImagePickerVc.allowPickingOriginalPhoto) { | |
| 213 | + _originalPhotoButton = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 214 | + _originalPhotoButton.imageEdgeInsets = UIEdgeInsetsMake(0, -10, 0, 0); | |
| 215 | + [_originalPhotoButton addTarget:self action:@selector(originalPhotoButtonClick) forControlEvents:UIControlEventTouchUpInside]; | |
| 216 | + _originalPhotoButton.titleLabel.font = [UIFont systemFontOfSize:16]; | |
| 217 | + [_originalPhotoButton setTitle:tzImagePickerVc.fullImageBtnTitleStr forState:UIControlStateNormal]; | |
| 218 | + [_originalPhotoButton setTitle:tzImagePickerVc.fullImageBtnTitleStr forState:UIControlStateSelected]; | |
| 219 | + [_originalPhotoButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal]; | |
| 220 | + [_originalPhotoButton setTitleColor:[UIColor blackColor] forState:UIControlStateSelected]; | |
| 221 | + [_originalPhotoButton setImage:tzImagePickerVc.photoOriginDefImage forState:UIControlStateNormal]; | |
| 222 | + [_originalPhotoButton setImage:tzImagePickerVc.photoOriginSelImage forState:UIControlStateSelected]; | |
| 223 | + _originalPhotoButton.imageView.clipsToBounds = YES; | |
| 224 | + _originalPhotoButton.imageView.contentMode = UIViewContentModeScaleAspectFit; | |
| 225 | + _originalPhotoButton.selected = _isSelectOriginalPhoto; | |
| 226 | + _originalPhotoButton.enabled = tzImagePickerVc.selectedModels.count > 0; | |
| 227 | + | |
| 228 | + _originalPhotoLabel = [[UILabel alloc] init]; | |
| 229 | + _originalPhotoLabel.textAlignment = NSTextAlignmentLeft; | |
| 230 | + _originalPhotoLabel.font = [UIFont systemFontOfSize:16]; | |
| 231 | + _originalPhotoLabel.textColor = [UIColor blackColor]; | |
| 232 | + if (_isSelectOriginalPhoto) [self getSelectedPhotoBytes]; | |
| 233 | + } | |
| 234 | + | |
| 235 | + _doneButton = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 236 | + _doneButton.titleLabel.font = [UIFont systemFontOfSize:16]; | |
| 237 | + [_doneButton addTarget:self action:@selector(doneButtonClick) forControlEvents:UIControlEventTouchUpInside]; | |
| 238 | + [_doneButton setTitle:tzImagePickerVc.doneBtnTitleStr forState:UIControlStateNormal]; | |
| 239 | + [_doneButton setTitle:tzImagePickerVc.doneBtnTitleStr forState:UIControlStateDisabled]; | |
| 240 | + [_doneButton setTitleColor:tzImagePickerVc.oKButtonTitleColorNormal forState:UIControlStateNormal]; | |
| 241 | + [_doneButton setTitleColor:tzImagePickerVc.oKButtonTitleColorDisabled forState:UIControlStateDisabled]; | |
| 242 | + _doneButton.enabled = tzImagePickerVc.selectedModels.count || tzImagePickerVc.alwaysEnableDoneBtn; | |
| 243 | + | |
| 244 | + _numberImageView = [[UIImageView alloc] initWithImage:tzImagePickerVc.photoNumberIconImage]; | |
| 245 | + _numberImageView.hidden = tzImagePickerVc.selectedModels.count <= 0; | |
| 246 | + _numberImageView.clipsToBounds = YES; | |
| 247 | + _numberImageView.contentMode = UIViewContentModeScaleAspectFit; | |
| 248 | + _numberImageView.backgroundColor = [UIColor clearColor]; | |
| 249 | + | |
| 250 | + _numberLabel = [[UILabel alloc] init]; | |
| 251 | + _numberLabel.font = [UIFont systemFontOfSize:15]; | |
| 252 | + _numberLabel.textColor = [UIColor whiteColor]; | |
| 253 | + _numberLabel.textAlignment = NSTextAlignmentCenter; | |
| 254 | + _numberLabel.text = [NSString stringWithFormat:@"%zd",tzImagePickerVc.selectedModels.count]; | |
| 255 | + _numberLabel.hidden = tzImagePickerVc.selectedModels.count <= 0; | |
| 256 | + _numberLabel.backgroundColor = [UIColor clearColor]; | |
| 257 | +#pragma mark - 添加: 选择图片个数显示(LXG) | |
| 258 | + _numberImageView.hidden = YES; | |
| 259 | + _numberLabel.hidden = YES; | |
| 260 | + [_doneButton setTitle:[NSString stringWithFormat:@"完成(%zd)",tzImagePickerVc.selectedModels.count] forState:UIControlStateNormal]; | |
| 261 | + | |
| 262 | + _divideLine = [[UIView alloc] init]; | |
| 263 | + CGFloat rgb2 = 222 / 255.0; | |
| 264 | + _divideLine.backgroundColor = [UIColor colorWithRed:rgb2 green:rgb2 blue:rgb2 alpha:1.0]; | |
| 265 | + | |
| 266 | + [_bottomToolBar addSubview:_divideLine]; | |
| 267 | + [_bottomToolBar addSubview:_previewButton]; | |
| 268 | + [_bottomToolBar addSubview:_doneButton]; | |
| 269 | + [_bottomToolBar addSubview:_numberImageView]; | |
| 270 | + [_bottomToolBar addSubview:_numberLabel]; | |
| 271 | + [_bottomToolBar addSubview:_originalPhotoButton]; | |
| 272 | + [self.view addSubview:_bottomToolBar]; | |
| 273 | + [_originalPhotoButton addSubview:_originalPhotoLabel]; | |
| 274 | + | |
| 275 | + if (tzImagePickerVc.photoPickerPageUIConfigBlock) { | |
| 276 | + tzImagePickerVc.photoPickerPageUIConfigBlock(_collectionView, _bottomToolBar, _previewButton, _originalPhotoButton, _originalPhotoLabel, _doneButton, _numberImageView, _numberLabel, _divideLine); | |
| 277 | + } | |
| 278 | +} | |
| 279 | + | |
| 280 | +#pragma mark - Layout | |
| 281 | + | |
| 282 | +- (void)viewDidLayoutSubviews { | |
| 283 | + [super viewDidLayoutSubviews]; | |
| 284 | + | |
| 285 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 286 | + | |
| 287 | + CGFloat top = 0; | |
| 288 | + CGFloat collectionViewHeight = 0; | |
| 289 | + CGFloat naviBarHeight = self.navigationController.navigationBar.tz_height; | |
| 290 | + BOOL isStatusBarHidden = [UIApplication sharedApplication].isStatusBarHidden; | |
| 291 | + CGFloat toolBarHeight = [TZCommonTools tz_isIPhoneX] ? 50 + (83 - 49) : 50; | |
| 292 | + if (self.navigationController.navigationBar.isTranslucent) { | |
| 293 | + top = naviBarHeight; | |
| 294 | + if (iOS7Later && !isStatusBarHidden) top += [TZCommonTools tz_statusBarHeight]; | |
| 295 | + collectionViewHeight = tzImagePickerVc.showSelectBtn ? self.view.tz_height - toolBarHeight - top : self.view.tz_height - top;; | |
| 296 | + } else { | |
| 297 | + collectionViewHeight = tzImagePickerVc.showSelectBtn ? self.view.tz_height - toolBarHeight : self.view.tz_height; | |
| 298 | + } | |
| 299 | + _collectionView.frame = CGRectMake(0, top, self.view.tz_width, collectionViewHeight); | |
| 300 | + CGFloat itemWH = (self.view.tz_width - (self.columnNumber + 1) * itemMargin) / self.columnNumber; | |
| 301 | + _layout.itemSize = CGSizeMake(itemWH, itemWH); | |
| 302 | + _layout.minimumInteritemSpacing = itemMargin; | |
| 303 | + _layout.minimumLineSpacing = itemMargin; | |
| 304 | + [_collectionView setCollectionViewLayout:_layout]; | |
| 305 | + if (_offsetItemCount > 0) { | |
| 306 | + CGFloat offsetY = _offsetItemCount * (_layout.itemSize.height + _layout.minimumLineSpacing); | |
| 307 | + [_collectionView setContentOffset:CGPointMake(0, offsetY)]; | |
| 308 | + } | |
| 309 | + | |
| 310 | + CGFloat toolBarTop = 0; | |
| 311 | + if (!self.navigationController.navigationBar.isHidden) { | |
| 312 | + toolBarTop = self.view.tz_height - toolBarHeight; | |
| 313 | + } else { | |
| 314 | + CGFloat navigationHeight = naviBarHeight; | |
| 315 | + if (iOS7Later) navigationHeight += [TZCommonTools tz_statusBarHeight]; | |
| 316 | + toolBarTop = self.view.tz_height - toolBarHeight - navigationHeight; | |
| 317 | + } | |
| 318 | + _bottomToolBar.frame = CGRectMake(0, toolBarTop, self.view.tz_width, toolBarHeight); | |
| 319 | + CGFloat previewWidth = [tzImagePickerVc.previewBtnTitleStr tz_calculateSizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16]} maxSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)].width + 2; | |
| 320 | + if (!tzImagePickerVc.allowPreview) { | |
| 321 | + previewWidth = 0.0; | |
| 322 | + } | |
| 323 | + _previewButton.frame = CGRectMake(10, 3, previewWidth, 44); | |
| 324 | + _previewButton.tz_width = !tzImagePickerVc.showSelectBtn ? 0 : previewWidth; | |
| 325 | + if (tzImagePickerVc.allowPickingOriginalPhoto) { | |
| 326 | + CGFloat fullImageWidth = [tzImagePickerVc.fullImageBtnTitleStr tz_calculateSizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:13]} maxSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)].width; | |
| 327 | + _originalPhotoButton.frame = CGRectMake(CGRectGetMaxX(_previewButton.frame), 0, fullImageWidth + 56, 50); | |
| 328 | + _originalPhotoLabel.frame = CGRectMake(fullImageWidth + 46, 0, 80, 50); | |
| 329 | + } | |
| 330 | + [_doneButton sizeToFit]; | |
| 331 | + _doneButton.frame = CGRectMake(self.view.tz_width - _doneButton.tz_width - 12, 0, _doneButton.tz_width, 50); | |
| 332 | + _numberImageView.frame = CGRectMake(_doneButton.tz_left - 24 - 5, 13, 24, 24); | |
| 333 | + _numberLabel.frame = _numberImageView.frame; | |
| 334 | + _divideLine.frame = CGRectMake(0, 0, self.view.tz_width, 1); | |
| 335 | + | |
| 336 | + [TZImageManager manager].columnNumber = [TZImageManager manager].columnNumber; | |
| 337 | + [self.collectionView reloadData]; | |
| 338 | + | |
| 339 | + if (tzImagePickerVc.photoPickerPageDidLayoutSubviewsBlock) { | |
| 340 | + tzImagePickerVc.photoPickerPageDidLayoutSubviewsBlock(_collectionView, _bottomToolBar, _previewButton, _originalPhotoButton, _originalPhotoLabel, _doneButton, _numberImageView, _numberLabel, _divideLine); | |
| 341 | + } | |
| 342 | +} | |
| 343 | + | |
| 344 | +#pragma mark - Notification | |
| 345 | + | |
| 346 | +- (void)didChangeStatusBarOrientationNotification:(NSNotification *)noti { | |
| 347 | + _offsetItemCount = _collectionView.contentOffset.y / (_layout.itemSize.height + _layout.minimumLineSpacing); | |
| 348 | +} | |
| 349 | + | |
| 350 | +#pragma mark - Click Event | |
| 351 | +- (void)navLeftBarButtonClick{ | |
| 352 | + [self.navigationController popViewControllerAnimated:YES]; | |
| 353 | +} | |
| 354 | +- (void)previewButtonClick { | |
| 355 | + TZPhotoPreviewController *photoPreviewVc = [[TZPhotoPreviewController alloc] init]; | |
| 356 | + [self pushPhotoPrevireViewController:photoPreviewVc needCheckSelectedModels:YES]; | |
| 357 | +} | |
| 358 | + | |
| 359 | +- (void)originalPhotoButtonClick { | |
| 360 | + _originalPhotoButton.selected = !_originalPhotoButton.isSelected; | |
| 361 | + _isSelectOriginalPhoto = _originalPhotoButton.isSelected; | |
| 362 | + _originalPhotoLabel.hidden = YES; ///lxg添加 | |
| 363 | +// _originalPhotoLabel.hidden = !_originalPhotoButton.isSelected;///lxg注释 | |
| 364 | + if (_isSelectOriginalPhoto) { | |
| 365 | + [self getSelectedPhotoBytes]; | |
| 366 | + } | |
| 367 | +} | |
| 368 | + | |
| 369 | +- (void)doneButtonClick { | |
| 370 | + | |
| 371 | + if (!_isClickDone) { | |
| 372 | + _isClickDone = YES; | |
| 373 | + }else{ | |
| 374 | + return; | |
| 375 | + } | |
| 376 | + | |
| 377 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 378 | + // 1.6.8 判断是否满足最小必选张数的限制 | |
| 379 | + if (tzImagePickerVc.minImagesCount && tzImagePickerVc.selectedModels.count < tzImagePickerVc.minImagesCount) { | |
| 380 | + NSString *title = [NSString stringWithFormat:[NSBundle tz_localizedStringForKey:@"Select a minimum of %zd photos"], tzImagePickerVc.minImagesCount]; | |
| 381 | + [tzImagePickerVc showAlertWithTitle:title]; | |
| 382 | + return; | |
| 383 | + } | |
| 384 | + | |
| 385 | + [tzImagePickerVc showProgressHUD]; | |
| 386 | + NSMutableArray *assets = [NSMutableArray array]; | |
| 387 | + NSMutableArray *photos; | |
| 388 | + NSMutableArray *infoArr; | |
| 389 | + if (tzImagePickerVc.onlyReturnAsset) { // not fetch image | |
| 390 | + for (NSInteger i = 0; i < tzImagePickerVc.selectedModels.count; i++) { | |
| 391 | + TZAssetModel *model = tzImagePickerVc.selectedModels[i]; | |
| 392 | + [assets addObject:model.asset]; | |
| 393 | + } | |
| 394 | + } else { // fetch image | |
| 395 | + photos = [NSMutableArray array]; | |
| 396 | + infoArr = [NSMutableArray array]; | |
| 397 | + for (NSInteger i = 0; i < tzImagePickerVc.selectedModels.count; i++) { [photos addObject:@1];[assets addObject:@1];[infoArr addObject:@1]; } | |
| 398 | + | |
| 399 | + __block BOOL havenotShowAlert = YES; | |
| 400 | + [TZImageManager manager].shouldFixOrientation = YES; | |
| 401 | + __block id alertView; | |
| 402 | + for (NSInteger i = 0; i < tzImagePickerVc.selectedModels.count; i++) { | |
| 403 | + TZAssetModel *model = tzImagePickerVc.selectedModels[i]; | |
| 404 | + [[TZImageManager manager] getPhotoWithAsset:model.asset completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) { | |
| 405 | + if (isDegraded) return; | |
| 406 | + if (photo) { | |
| 407 | + if (![TZImagePickerConfig sharedInstance].notScaleImage) { | |
| 408 | + photo = [[TZImageManager manager] scaleImage:photo toSize:CGSizeMake(tzImagePickerVc.photoWidth, (int)(tzImagePickerVc.photoWidth * photo.size.height / photo.size.width))]; | |
| 409 | + } | |
| 410 | + [photos replaceObjectAtIndex:i withObject:photo]; | |
| 411 | + } | |
| 412 | + if (info) [infoArr replaceObjectAtIndex:i withObject:info]; | |
| 413 | + [assets replaceObjectAtIndex:i withObject:model.asset]; | |
| 414 | +// [self getPhotoMetadataWithAsset:model.asset]; | |
| 415 | + for (id item in photos) { if ([item isKindOfClass:[NSNumber class]]) return; } | |
| 416 | + | |
| 417 | + if (havenotShowAlert) { | |
| 418 | + [tzImagePickerVc hideAlertView:alertView]; | |
| 419 | + [self didGetAllPhotos:photos assets:assets infoArr:infoArr]; | |
| 420 | + } | |
| 421 | + } progressHandler:^(double progress, NSError *error, BOOL *stop, NSDictionary *info) { | |
| 422 | + // 如果图片正在从iCloud同步中,提醒用户 | |
| 423 | + if (progress < 1 && havenotShowAlert && !alertView) { | |
| 424 | + [tzImagePickerVc hideProgressHUD]; | |
| 425 | + alertView = [tzImagePickerVc showAlertWithTitle:[NSBundle tz_localizedStringForKey:@"Synchronizing photos from iCloud"]]; | |
| 426 | + havenotShowAlert = NO; | |
| 427 | + return; | |
| 428 | + } | |
| 429 | + if (progress >= 1) { | |
| 430 | + havenotShowAlert = YES; | |
| 431 | + } | |
| 432 | + } networkAccessAllowed:YES]; | |
| 433 | + } | |
| 434 | + } | |
| 435 | + if (tzImagePickerVc.selectedModels.count <= 0 || tzImagePickerVc.onlyReturnAsset) { | |
| 436 | + [self didGetAllPhotos:photos assets:assets infoArr:infoArr]; | |
| 437 | + } | |
| 438 | +} | |
| 439 | + | |
| 440 | +//- (void)getPhotoMetadataWithAsset:(PHAsset *)asset | |
| 441 | +//{ | |
| 442 | +// PHImageRequestOptions *request = [PHImageRequestOptions new]; | |
| 443 | +// request.version = PHImageRequestOptionsVersionCurrent; | |
| 444 | +// request.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat; | |
| 445 | +// request.resizeMode = PHImageRequestOptionsResizeModeNone; | |
| 446 | +// request.synchronous = YES; | |
| 447 | +// NSData *data1 = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"file:///var/mobile/Media/DCIM/100APPLE/IMG_0178.JPG"]]; | |
| 448 | +// UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"file:///var/mobile/Media/DCIM/100APPLE/1.JPG"]]]; | |
| 449 | +// [[PHImageManager defaultManager] requestImageDataForAsset:asset options: request resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { | |
| 450 | +// NSURL *url = [info valueForKey:@"PHImageFileURLKey"]; | |
| 451 | +// NSString *str = [url absoluteString]; //url>string | |
| 452 | +// NSArray *arr = [str componentsSeparatedByString:@"/"]; | |
| 453 | +// NSString *imgName = [arr lastObject]; | |
| 454 | +// NSLog(@"imgName:%@",imgName); | |
| 455 | +// | |
| 456 | +// CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)imageData, NULL); | |
| 457 | +// if (NULL != source) { | |
| 458 | +// NSDictionary * metadataDic = (NSDictionary *)CFBridgingRelease(CGImageSourceCopyPropertiesAtIndex(source, 0, NULL)); | |
| 459 | +// NSLog(@"metada:%@",metadataDic); | |
| 460 | +// CFRelease(source); | |
| 461 | +// } | |
| 462 | +// }]; | |
| 463 | +//} | |
| 464 | + | |
| 465 | +- (void)didGetAllPhotos:(NSArray *)photos assets:(NSArray *)assets infoArr:(NSArray *)infoArr { | |
| 466 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 467 | + [tzImagePickerVc hideProgressHUD]; | |
| 468 | + | |
| 469 | + if (tzImagePickerVc.autoDismiss) { | |
| 470 | + [self.navigationController dismissViewControllerAnimated:YES completion:^{ | |
| 471 | + [self callDelegateMethodWithPhotos:photos assets:assets infoArr:infoArr]; | |
| 472 | + }]; | |
| 473 | + } else { | |
| 474 | + [self callDelegateMethodWithPhotos:photos assets:assets infoArr:infoArr]; | |
| 475 | + } | |
| 476 | +} | |
| 477 | + | |
| 478 | +- (void)callDelegateMethodWithPhotos:(NSArray *)photos assets:(NSArray *)assets infoArr:(NSArray *)infoArr { | |
| 479 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 480 | + if (tzImagePickerVc.allowPickingVideo && tzImagePickerVc.maxImagesCount == 1) { | |
| 481 | + if ([[TZImageManager manager] isVideo:[assets firstObject]]) { | |
| 482 | + if ([tzImagePickerVc.pickerDelegate respondsToSelector:@selector(imagePickerController:didFinishPickingVideo:sourceAssets:)]) { | |
| 483 | + [tzImagePickerVc.pickerDelegate imagePickerController:tzImagePickerVc didFinishPickingVideo:[photos firstObject] sourceAssets:[assets firstObject]]; | |
| 484 | + } | |
| 485 | + if (tzImagePickerVc.didFinishPickingVideoHandle) { | |
| 486 | + tzImagePickerVc.didFinishPickingVideoHandle([photos firstObject], [assets firstObject]); | |
| 487 | + } | |
| 488 | + return; | |
| 489 | + } | |
| 490 | + } | |
| 491 | + | |
| 492 | + if ([tzImagePickerVc.pickerDelegate respondsToSelector:@selector(imagePickerController:didFinishPickingPhotos:sourceAssets:isSelectOriginalPhoto:)]) { | |
| 493 | + [tzImagePickerVc.pickerDelegate imagePickerController:tzImagePickerVc didFinishPickingPhotos:photos sourceAssets:assets isSelectOriginalPhoto:_isSelectOriginalPhoto]; | |
| 494 | + } | |
| 495 | + if ([tzImagePickerVc.pickerDelegate respondsToSelector:@selector(imagePickerController:didFinishPickingPhotos:sourceAssets:isSelectOriginalPhoto:infos:)]) { | |
| 496 | + [tzImagePickerVc.pickerDelegate imagePickerController:tzImagePickerVc didFinishPickingPhotos:photos sourceAssets:assets isSelectOriginalPhoto:_isSelectOriginalPhoto infos:infoArr]; | |
| 497 | + } | |
| 498 | + if (tzImagePickerVc.didFinishPickingPhotosHandle) { | |
| 499 | + tzImagePickerVc.didFinishPickingPhotosHandle(photos,assets,_isSelectOriginalPhoto); | |
| 500 | + } | |
| 501 | + if (tzImagePickerVc.didFinishPickingPhotosWithInfosHandle) { | |
| 502 | + tzImagePickerVc.didFinishPickingPhotosWithInfosHandle(photos,assets,_isSelectOriginalPhoto,infoArr); | |
| 503 | + } | |
| 504 | +} | |
| 505 | + | |
| 506 | +#pragma mark - UICollectionViewDataSource && Delegate | |
| 507 | + | |
| 508 | +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { | |
| 509 | + if (_showTakePhotoBtn) { | |
| 510 | + return _models.count + 1; | |
| 511 | + } | |
| 512 | + return _models.count; | |
| 513 | +} | |
| 514 | + | |
| 515 | +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { | |
| 516 | + // the cell lead to take a picture / 去拍照的cell | |
| 517 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 518 | + if (((tzImagePickerVc.sortAscendingByModificationDate && indexPath.item >= _models.count) || (!tzImagePickerVc.sortAscendingByModificationDate && indexPath.item == 0)) && _showTakePhotoBtn) { | |
| 519 | + TZAssetCameraCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TZAssetCameraCell" forIndexPath:indexPath]; | |
| 520 | + cell.imageView.image = tzImagePickerVc.takePictureImage; | |
| 521 | + if ([tzImagePickerVc.takePictureImageName isEqualToString:@"takePicture80"]) { | |
| 522 | + cell.imageView.contentMode = UIViewContentModeCenter; | |
| 523 | + CGFloat rgb = 223 / 255.0; | |
| 524 | + cell.imageView.backgroundColor = [UIColor colorWithRed:rgb green:rgb blue:rgb alpha:1.0]; | |
| 525 | + } else { | |
| 526 | + cell.imageView.backgroundColor = [UIColor colorWithWhite:1.000 alpha:0.500]; | |
| 527 | + } | |
| 528 | + return cell; | |
| 529 | + } | |
| 530 | + // the cell dipaly photo or video / 展示照片或视频的cell | |
| 531 | + TZAssetCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TZAssetCell" forIndexPath:indexPath]; | |
| 532 | + cell.allowPickingMultipleVideo = tzImagePickerVc.allowPickingMultipleVideo; | |
| 533 | + cell.photoDefImage = tzImagePickerVc.photoDefImage; | |
| 534 | + cell.photoSelImage = tzImagePickerVc.photoSelImage; | |
| 535 | + cell.useCachedImage = self.useCachedImage; | |
| 536 | + cell.assetCellDidSetModelBlock = tzImagePickerVc.assetCellDidSetModelBlock; | |
| 537 | + cell.assetCellDidLayoutSubviewsBlock = tzImagePickerVc.assetCellDidLayoutSubviewsBlock; | |
| 538 | + TZAssetModel *model; | |
| 539 | + if (tzImagePickerVc.sortAscendingByModificationDate || !_showTakePhotoBtn) { | |
| 540 | + model = _models[indexPath.item]; | |
| 541 | + } else { | |
| 542 | + model = _models[indexPath.item - 1]; | |
| 543 | + } | |
| 544 | + cell.allowPickingGif = tzImagePickerVc.allowPickingGif; | |
| 545 | + cell.model = model; | |
| 546 | + if (model.isSelected && tzImagePickerVc.showSelectedIndex) { | |
| 547 | + NSString *assetId = [[TZImageManager manager] getAssetIdentifier:model.asset]; | |
| 548 | + cell.index = [tzImagePickerVc.selectedAssetIds indexOfObject:assetId] + 1; | |
| 549 | + } | |
| 550 | + cell.showSelectBtn = tzImagePickerVc.showSelectBtn; | |
| 551 | + cell.allowPreview = tzImagePickerVc.allowPreview; | |
| 552 | + | |
| 553 | + if (tzImagePickerVc.selectedModels.count >= tzImagePickerVc.maxImagesCount && tzImagePickerVc.showPhotoCannotSelectLayer && !model.isSelected) { | |
| 554 | + cell.cannotSelectLayerButton.backgroundColor = tzImagePickerVc.cannotSelectLayerColor; | |
| 555 | + cell.cannotSelectLayerButton.hidden = NO; | |
| 556 | + } else { | |
| 557 | + cell.cannotSelectLayerButton.hidden = YES; | |
| 558 | + } | |
| 559 | + | |
| 560 | + __weak typeof(cell) weakCell = cell; | |
| 561 | + __weak typeof(self) weakSelf = self; | |
| 562 | + __weak typeof(_numberImageView.layer) weakLayer = _numberImageView.layer; | |
| 563 | + cell.didSelectPhotoBlock = ^(BOOL isSelected) { | |
| 564 | + __strong typeof(weakCell) strongCell = weakCell; | |
| 565 | + __strong typeof(weakSelf) strongSelf = weakSelf; | |
| 566 | + __strong typeof(weakLayer) strongLayer = weakLayer; | |
| 567 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)strongSelf.navigationController; | |
| 568 | + // 1. cancel select / 取消选择 | |
| 569 | + if (isSelected) { | |
| 570 | + strongCell.selectPhotoButton.selected = NO; | |
| 571 | + model.isSelected = NO; | |
| 572 | + NSArray *selectedModels = [NSArray arrayWithArray:tzImagePickerVc.selectedModels]; | |
| 573 | + for (TZAssetModel *model_item in selectedModels) { | |
| 574 | + if ([[[TZImageManager manager] getAssetIdentifier:model.asset] isEqualToString:[[TZImageManager manager] getAssetIdentifier:model_item.asset]]) { | |
| 575 | + [tzImagePickerVc removeSelectedModel:model_item]; | |
| 576 | + break; | |
| 577 | + } | |
| 578 | + } | |
| 579 | + [strongSelf refreshBottomToolBarStatus]; | |
| 580 | + if (tzImagePickerVc.showSelectedIndex || tzImagePickerVc.showPhotoCannotSelectLayer) { | |
| 581 | + [strongSelf setUseCachedImageAndReloadData]; | |
| 582 | + } | |
| 583 | + [UIView showOscillatoryAnimationWithLayer:strongLayer type:TZOscillatoryAnimationToSmaller]; | |
| 584 | + } else { | |
| 585 | + // 2. select:check if over the maxImagesCount / 选择照片,检查是否超过了最大个数的限制 | |
| 586 | + if (tzImagePickerVc.selectedModels.count < tzImagePickerVc.maxImagesCount) { | |
| 587 | + ///lxg禁止选中限制时长视频 | |
| 588 | + NSTimeInterval duration = 0.0; | |
| 589 | + if ([model.asset isKindOfClass:[PHAsset class]]) { | |
| 590 | + PHAsset *asset = model.asset; | |
| 591 | + duration = asset.duration; | |
| 592 | + }else if ([model.asset isKindOfClass:[ALAsset class]]){ | |
| 593 | + duration = [[model.asset valueForProperty:ALAssetPropertyDuration] doubleValue]; | |
| 594 | + } | |
| 595 | + if (model.type == TZAssetModelMediaTypeVideo | |
| 596 | + && tzImagePickerVc.allowPickingMultipleVideo | |
| 597 | + && duration > tzImagePickerVc.maxVideoDuration) {//视频,支持混选,时长超过最大时长限制 | |
| 598 | + NSInteger minutes = tzImagePickerVc.maxVideoDuration / 60; | |
| 599 | + NSInteger seconds = tzImagePickerVc.maxVideoDuration % 60; | |
| 600 | + NSString *timeStr = [NSString string]; | |
| 601 | + if (minutes == 0) { | |
| 602 | + timeStr = [NSString stringWithFormat:@"不能选择超过%li秒视频",(long)seconds]; | |
| 603 | + }else if (seconds == 0){ | |
| 604 | + timeStr = [NSString stringWithFormat:@"不能选择超过%li分视频",(long)minutes]; | |
| 605 | + }else{ | |
| 606 | + timeStr = [NSString stringWithFormat:@"不能选择超过%li分%li秒视频",(long)minutes,(long)seconds]; | |
| 607 | + } | |
| 608 | + [tzImagePickerVc showAlertWithTitle:timeStr]; | |
| 609 | + | |
| 610 | + return; | |
| 611 | + } | |
| 612 | + | |
| 613 | + | |
| 614 | + if (tzImagePickerVc.maxImagesCount == 1 && !tzImagePickerVc.allowPreview) { | |
| 615 | + model.isSelected = YES; | |
| 616 | + [tzImagePickerVc addSelectedModel:model]; | |
| 617 | + [strongSelf doneButtonClick]; | |
| 618 | + return; | |
| 619 | + } | |
| 620 | + strongCell.selectPhotoButton.selected = YES; | |
| 621 | + model.isSelected = YES; | |
| 622 | + if (tzImagePickerVc.showSelectedIndex || tzImagePickerVc.showPhotoCannotSelectLayer) { | |
| 623 | + model.needOscillatoryAnimation = YES; | |
| 624 | + [strongSelf setUseCachedImageAndReloadData]; | |
| 625 | + } | |
| 626 | + [tzImagePickerVc addSelectedModel:model]; | |
| 627 | + [strongSelf refreshBottomToolBarStatus]; | |
| 628 | + [UIView showOscillatoryAnimationWithLayer:strongLayer type:TZOscillatoryAnimationToSmaller]; | |
| 629 | + } else { | |
| 630 | + NSString *title = [NSString stringWithFormat:[NSBundle tz_localizedStringForKey:@"Select a maximum of %zd photos"], tzImagePickerVc.maxImagesCount]; | |
| 631 | + [tzImagePickerVc showAlertWithTitle:title]; | |
| 632 | + } | |
| 633 | + } | |
| 634 | + }; | |
| 635 | + return cell; | |
| 636 | +} | |
| 637 | + | |
| 638 | +- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { | |
| 639 | + // take a photo / 去拍照 | |
| 640 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 641 | + if (((tzImagePickerVc.sortAscendingByModificationDate && indexPath.item >= _models.count) || (!tzImagePickerVc.sortAscendingByModificationDate && indexPath.item == 0)) && _showTakePhotoBtn) { | |
| 642 | + [self takePhoto]; return; | |
| 643 | + } | |
| 644 | + // preview phote or video / 预览照片或视频 | |
| 645 | + NSInteger index = indexPath.item; | |
| 646 | + if (!tzImagePickerVc.sortAscendingByModificationDate && _showTakePhotoBtn) { | |
| 647 | + index = indexPath.item - 1; | |
| 648 | + } | |
| 649 | +// TZAssetModel *model = _models[index]; | |
| 650 | + | |
| 651 | +#pragma mark - 聊天里面进入和朋友圈中进入LXG | |
| 652 | + | |
| 653 | + TZPhotoPreviewController *photoPreviewVc = [[TZPhotoPreviewController alloc] init]; | |
| 654 | + photoPreviewVc.currentIndex = index; | |
| 655 | + photoPreviewVc.models = _models; | |
| 656 | + photoPreviewVc.isSelectOriginalPhoto = tzImagePickerVc.isSelectOriginalPhoto; | |
| 657 | + [self pushPhotoPrevireViewController:photoPreviewVc]; | |
| 658 | + | |
| 659 | + | |
| 660 | +// ///LXG进行 注释 | |
| 661 | +// if (model.type == TZAssetModelMediaTypeVideo && !tzImagePickerVc.allowPickingMultipleVideo) {///支持混选+点击视频 | |
| 662 | +// if (tzImagePickerVc.selectedModels.count > 0) { | |
| 663 | +// TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 664 | +// [imagePickerVc showAlertWithTitle:[NSBundle tz_localizedStringForKey:@"Can not choose both video and photo"]]; | |
| 665 | +// } else { | |
| 666 | +// TZVideoPlayerController *videoPlayerVc = [[TZVideoPlayerController alloc] init]; | |
| 667 | +// videoPlayerVc.model = model; | |
| 668 | +// videoPlayerVc.isOther = NO;//相册内部进入(LXG) | |
| 669 | +// [self.navigationController pushViewController:videoPlayerVc animated:YES]; | |
| 670 | +// } | |
| 671 | +// } else if (model.type == TZAssetModelMediaTypePhotoGif && tzImagePickerVc.allowPickingGif && !tzImagePickerVc.allowPickingMultipleVideo) {//支持混选+GIF | |
| 672 | +// if (tzImagePickerVc.selectedModels.count > 0) { | |
| 673 | +// TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 674 | +// [imagePickerVc showAlertWithTitle:[NSBundle tz_localizedStringForKey:@"Can not choose both photo and GIF"]]; | |
| 675 | +// } else { | |
| 676 | +// TZGifPhotoPreviewController *gifPreviewVc = [[TZGifPhotoPreviewController alloc] init]; | |
| 677 | +// gifPreviewVc.model = model; | |
| 678 | +// [self.navigationController pushViewController:gifPreviewVc animated:YES]; | |
| 679 | +// } | |
| 680 | +// } else { | |
| 681 | +//#pragma mark - 当可以混选 | |
| 682 | +// TZPhotoPreviewController *photoPreviewVc = [[TZPhotoPreviewController alloc] init]; | |
| 683 | +// photoPreviewVc.currentIndex = index; | |
| 684 | +// photoPreviewVc.models = _models; | |
| 685 | +// [self pushPhotoPrevireViewController:photoPreviewVc]; | |
| 686 | +// } | |
| 687 | +} | |
| 688 | + | |
| 689 | +#pragma mark - UIScrollViewDelegate | |
| 690 | + | |
| 691 | +- (void)scrollViewDidScroll:(UIScrollView *)scrollView { | |
| 692 | + if (iOS8Later) { | |
| 693 | + // [self updateCachedAssets]; | |
| 694 | + } | |
| 695 | +} | |
| 696 | + | |
| 697 | +#pragma mark - Private Method | |
| 698 | + | |
| 699 | +- (void)setUseCachedImageAndReloadData { | |
| 700 | + self.useCachedImage = YES; | |
| 701 | + [self.collectionView reloadData]; | |
| 702 | + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | |
| 703 | + self.useCachedImage = NO; | |
| 704 | + }); | |
| 705 | +} | |
| 706 | + | |
| 707 | +/// 拍照按钮点击事件 | |
| 708 | +- (void)takePhoto { | |
| 709 | + AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; | |
| 710 | + if ((authStatus == AVAuthorizationStatusRestricted || authStatus ==AVAuthorizationStatusDenied) && iOS7Later) { | |
| 711 | + | |
| 712 | + NSDictionary *infoDict = [TZCommonTools tz_getInfoDictionary]; | |
| 713 | + // 无权限 做一个友好的提示 | |
| 714 | + NSString *appName = [infoDict valueForKey:@"CFBundleDisplayName"]; | |
| 715 | + if (!appName) appName = [infoDict valueForKey:@"CFBundleName"]; | |
| 716 | + | |
| 717 | + NSString *message = [NSString stringWithFormat:[NSBundle tz_localizedStringForKey:@"Please allow %@ to access your camera in \"Settings -> Privacy -> Camera\""],appName]; | |
| 718 | + if (iOS8Later) { | |
| 719 | + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[NSBundle tz_localizedStringForKey:@"Can not use camera"] message:message delegate:self cancelButtonTitle:[NSBundle tz_localizedStringForKey:@"Cancel"] otherButtonTitles:[NSBundle tz_localizedStringForKey:@"Setting"], nil]; | |
| 720 | + [alert show]; | |
| 721 | + } else { | |
| 722 | + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[NSBundle tz_localizedStringForKey:@"Can not use camera"] message:message delegate:self cancelButtonTitle:[NSBundle tz_localizedStringForKey:@"OK"] otherButtonTitles:nil]; | |
| 723 | + [alert show]; | |
| 724 | + } | |
| 725 | + } else if (authStatus == AVAuthorizationStatusNotDetermined) { | |
| 726 | + // fix issue 466, 防止用户首次拍照拒绝授权时相机页黑屏 | |
| 727 | + if (iOS7Later) { | |
| 728 | + [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) { | |
| 729 | + if (granted) { | |
| 730 | + dispatch_async(dispatch_get_main_queue(), ^{ | |
| 731 | + [self pushImagePickerController]; | |
| 732 | + }); | |
| 733 | + } | |
| 734 | + }]; | |
| 735 | + } else { | |
| 736 | + [self pushImagePickerController]; | |
| 737 | + } | |
| 738 | + } else { | |
| 739 | + [self pushImagePickerController]; | |
| 740 | + } | |
| 741 | +} | |
| 742 | + | |
| 743 | +// 调用相机 | |
| 744 | +- (void)pushImagePickerController { | |
| 745 | + // 提前定位 | |
| 746 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 747 | + if (tzImagePickerVc.allowCameraLocation) { | |
| 748 | + __weak typeof(self) weakSelf = self; | |
| 749 | + [[TZLocationManager manager] startLocationWithSuccessBlock:^(NSArray<CLLocation *> *locations) { | |
| 750 | + __strong typeof(weakSelf) strongSelf = weakSelf; | |
| 751 | + strongSelf.location = [locations firstObject]; | |
| 752 | + } failureBlock:^(NSError *error) { | |
| 753 | + __strong typeof(weakSelf) strongSelf = weakSelf; | |
| 754 | + strongSelf.location = nil; | |
| 755 | + }]; | |
| 756 | + } | |
| 757 | + | |
| 758 | + UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera; | |
| 759 | + if ([UIImagePickerController isSourceTypeAvailable: sourceType]) { | |
| 760 | + self.imagePickerVc.sourceType = sourceType; | |
| 761 | + NSMutableArray *mediaTypes = [NSMutableArray array]; | |
| 762 | + if (tzImagePickerVc.allowTakePicture) { | |
| 763 | + [mediaTypes addObject:(NSString *)kUTTypeImage]; | |
| 764 | + } | |
| 765 | + if (tzImagePickerVc.allowTakeVideo) { | |
| 766 | + [mediaTypes addObject:(NSString *)kUTTypeMovie]; | |
| 767 | + self.imagePickerVc.videoMaximumDuration = tzImagePickerVc.videoMaximumDuration; | |
| 768 | + } | |
| 769 | + self.imagePickerVc.mediaTypes= mediaTypes; | |
| 770 | + if (iOS8Later) { | |
| 771 | + _imagePickerVc.modalPresentationStyle = UIModalPresentationOverCurrentContext; | |
| 772 | + } | |
| 773 | + if (tzImagePickerVc.uiImagePickerControllerSettingBlock) { | |
| 774 | + tzImagePickerVc.uiImagePickerControllerSettingBlock(_imagePickerVc); | |
| 775 | + } | |
| 776 | + [self presentViewController:_imagePickerVc animated:YES completion:nil]; | |
| 777 | + } else { | |
| 778 | + NSLog(@"模拟器中无法打开照相机,请在真机中使用"); | |
| 779 | + } | |
| 780 | +} | |
| 781 | + | |
| 782 | +- (void)refreshBottomToolBarStatus { | |
| 783 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 784 | + | |
| 785 | + _previewButton.enabled = tzImagePickerVc.selectedModels.count > 0; | |
| 786 | + _doneButton.enabled = tzImagePickerVc.selectedModels.count > 0 || tzImagePickerVc.alwaysEnableDoneBtn; | |
| 787 | + | |
| 788 | + _numberImageView.hidden = tzImagePickerVc.selectedModels.count <= 0; | |
| 789 | + _numberLabel.hidden = tzImagePickerVc.selectedModels.count <= 0; | |
| 790 | + _numberLabel.text = [NSString stringWithFormat:@"%zd",tzImagePickerVc.selectedModels.count]; | |
| 791 | +#pragma mark - 添加: 选择图片个数显示(LXG) | |
| 792 | + _numberImageView.hidden = YES; | |
| 793 | + _numberLabel.hidden = YES; | |
| 794 | + [_doneButton setTitle:[NSString stringWithFormat:@"完成(%zd)",tzImagePickerVc.selectedModels.count] forState:UIControlStateNormal]; | |
| 795 | + | |
| 796 | + _originalPhotoButton.enabled = tzImagePickerVc.selectedModels.count > 0; | |
| 797 | + _originalPhotoButton.selected = (_isSelectOriginalPhoto && _originalPhotoButton.enabled); | |
| 798 | + _originalPhotoLabel.hidden = YES;///lxg添加 | |
| 799 | +// _originalPhotoLabel.hidden = (!_originalPhotoButton.isSelected);///lxg注释 | |
| 800 | + if (_isSelectOriginalPhoto) [self getSelectedPhotoBytes]; | |
| 801 | +} | |
| 802 | + | |
| 803 | +- (void)pushPhotoPrevireViewController:(TZPhotoPreviewController *)photoPreviewVc { | |
| 804 | + [self pushPhotoPrevireViewController:photoPreviewVc needCheckSelectedModels:NO]; | |
| 805 | +} | |
| 806 | + | |
| 807 | +- (void)pushPhotoPrevireViewController:(TZPhotoPreviewController *)photoPreviewVc needCheckSelectedModels:(BOOL)needCheckSelectedModels { | |
| 808 | + __weak typeof(self) weakSelf = self; | |
| 809 | + photoPreviewVc.isSelectOriginalPhoto = _isSelectOriginalPhoto; | |
| 810 | + [photoPreviewVc setBackButtonClickBlock:^(BOOL isSelectOriginalPhoto) { | |
| 811 | + __strong typeof(weakSelf) strongSelf = weakSelf; | |
| 812 | + strongSelf.isSelectOriginalPhoto = isSelectOriginalPhoto; | |
| 813 | + if (needCheckSelectedModels) { | |
| 814 | + [strongSelf checkSelectedModels]; | |
| 815 | + } | |
| 816 | + [strongSelf.collectionView reloadData]; | |
| 817 | + [strongSelf refreshBottomToolBarStatus]; | |
| 818 | + }]; | |
| 819 | + [photoPreviewVc setDoneButtonClickBlock:^(BOOL isSelectOriginalPhoto) { | |
| 820 | + __strong typeof(weakSelf) strongSelf = weakSelf; | |
| 821 | + strongSelf.isSelectOriginalPhoto = isSelectOriginalPhoto; | |
| 822 | + [strongSelf doneButtonClick]; | |
| 823 | + }]; | |
| 824 | + [photoPreviewVc setDoneButtonClickBlockCropMode:^(UIImage *cropedImage, id asset) { | |
| 825 | + __strong typeof(weakSelf) strongSelf = weakSelf; | |
| 826 | + [strongSelf didGetAllPhotos:@[cropedImage] assets:@[asset] infoArr:nil]; | |
| 827 | + }]; | |
| 828 | + [self.navigationController pushViewController:photoPreviewVc animated:YES]; | |
| 829 | +} | |
| 830 | + | |
| 831 | +- (void)getSelectedPhotoBytes { | |
| 832 | + // 越南语 && 5屏幕时会显示不下,暂时这样处理 | |
| 833 | + if ([[TZImagePickerConfig sharedInstance].preferredLanguage isEqualToString:@"vi"] && self.view.tz_width <= 320) { | |
| 834 | + return; | |
| 835 | + } | |
| 836 | + TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 837 | + [[TZImageManager manager] getPhotosBytesWithArray:imagePickerVc.selectedModels completion:^(NSString *totalBytes) { | |
| 838 | + self->_originalPhotoLabel.text = [NSString stringWithFormat:@"(%@)",totalBytes]; | |
| 839 | + self->_originalPhotoLabel.hidden = YES;///lxg添加 | |
| 840 | + }]; | |
| 841 | +} | |
| 842 | + | |
| 843 | +- (void)scrollCollectionViewToBottom { | |
| 844 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 845 | + if (_shouldScrollToBottom && _models.count > 0) { | |
| 846 | + NSInteger item = 0; | |
| 847 | + if (tzImagePickerVc.sortAscendingByModificationDate) { | |
| 848 | + item = _models.count - 1; | |
| 849 | + if (_showTakePhotoBtn) { | |
| 850 | + item += 1; | |
| 851 | + } | |
| 852 | + } | |
| 853 | + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | |
| 854 | + [self->_collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:item inSection:0] atScrollPosition:UICollectionViewScrollPositionBottom animated:NO]; | |
| 855 | + self->_shouldScrollToBottom = NO; | |
| 856 | + self->_collectionView.hidden = NO; | |
| 857 | + }); | |
| 858 | + } else { | |
| 859 | + _collectionView.hidden = NO; | |
| 860 | + } | |
| 861 | +} | |
| 862 | + | |
| 863 | +- (void)checkSelectedModels { | |
| 864 | + NSMutableArray *selectedAssets = [NSMutableArray array]; | |
| 865 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 866 | + for (TZAssetModel *model in tzImagePickerVc.selectedModels) { | |
| 867 | + [selectedAssets addObject:model.asset]; | |
| 868 | + } | |
| 869 | + for (TZAssetModel *model in _models) { | |
| 870 | + model.isSelected = NO; | |
| 871 | + if ([[TZImageManager manager] isAssetsArray:selectedAssets containAsset:model.asset]) { | |
| 872 | + model.isSelected = YES; | |
| 873 | + } | |
| 874 | + } | |
| 875 | +} | |
| 876 | + | |
| 877 | +#pragma mark - UIAlertViewDelegate | |
| 878 | + | |
| 879 | +- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { | |
| 880 | + if (buttonIndex == 1) { // 去设置界面,开启相机访问权限 | |
| 881 | + if (iOS8Later) { | |
| 882 | + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]]; | |
| 883 | + } | |
| 884 | + } | |
| 885 | +} | |
| 886 | + | |
| 887 | +#pragma mark - UIImagePickerControllerDelegate | |
| 888 | + | |
| 889 | +- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { | |
| 890 | + [picker dismissViewControllerAnimated:YES completion:nil]; | |
| 891 | + NSString *type = [info objectForKey:UIImagePickerControllerMediaType]; | |
| 892 | + if ([type isEqualToString:@"public.image"]) { | |
| 893 | + TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 894 | + [imagePickerVc showProgressHUD]; | |
| 895 | + UIImage *photo = [info objectForKey:UIImagePickerControllerOriginalImage]; | |
| 896 | + if (photo) { | |
| 897 | + [[TZImageManager manager] savePhotoWithImage:photo location:self.location completion:^(NSError *error){ | |
| 898 | + if (!error) { | |
| 899 | + [self reloadPhotoArrayWithMediaType:type]; | |
| 900 | + } | |
| 901 | + }]; | |
| 902 | + self.location = nil; | |
| 903 | + } | |
| 904 | + } else if ([type isEqualToString:@"public.movie"]) { | |
| 905 | + TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 906 | + [imagePickerVc showProgressHUD]; | |
| 907 | + NSURL *videoUrl = [info objectForKey:UIImagePickerControllerMediaURL]; | |
| 908 | + if (videoUrl) { | |
| 909 | + [[TZImageManager manager] saveVideoWithUrl:videoUrl location:self.location completion:^(NSError *error) { | |
| 910 | + if (!error) { | |
| 911 | + [self reloadPhotoArrayWithMediaType:type]; | |
| 912 | + } | |
| 913 | + }]; | |
| 914 | + self.location = nil; | |
| 915 | + } | |
| 916 | + } | |
| 917 | +} | |
| 918 | + | |
| 919 | +- (void)reloadPhotoArrayWithMediaType:(NSString *)mediaType { | |
| 920 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 921 | + [[TZImageManager manager] getCameraRollAlbum:tzImagePickerVc.allowPickingVideo allowPickingImage:tzImagePickerVc.allowPickingImage needFetchAssets:NO completion:^(TZAlbumModel *model) { | |
| 922 | + self->_model = model; | |
| 923 | + [[TZImageManager manager] getAssetsFromFetchResult:self->_model.result completion:^(NSArray<TZAssetModel *> *models) { | |
| 924 | + [tzImagePickerVc hideProgressHUD]; | |
| 925 | + | |
| 926 | + TZAssetModel *assetModel; | |
| 927 | + if (tzImagePickerVc.sortAscendingByModificationDate) { | |
| 928 | + assetModel = [models lastObject]; | |
| 929 | + [self->_models addObject:assetModel]; | |
| 930 | + } else { | |
| 931 | + assetModel = [models firstObject]; | |
| 932 | + [self->_models insertObject:assetModel atIndex:0]; | |
| 933 | + } | |
| 934 | + | |
| 935 | + if (tzImagePickerVc.maxImagesCount <= 1) { | |
| 936 | + if (tzImagePickerVc.allowCrop) { | |
| 937 | + TZPhotoPreviewController *photoPreviewVc = [[TZPhotoPreviewController alloc] init]; | |
| 938 | + if (tzImagePickerVc.sortAscendingByModificationDate) { | |
| 939 | + photoPreviewVc.currentIndex = self->_models.count - 1; | |
| 940 | + } else { | |
| 941 | + photoPreviewVc.currentIndex = 0; | |
| 942 | + } | |
| 943 | + photoPreviewVc.models = self->_models; | |
| 944 | + [self pushPhotoPrevireViewController:photoPreviewVc]; | |
| 945 | + } else { | |
| 946 | + [tzImagePickerVc addSelectedModel:assetModel]; | |
| 947 | + [self doneButtonClick]; | |
| 948 | + } | |
| 949 | + return; | |
| 950 | + } | |
| 951 | + | |
| 952 | + if (tzImagePickerVc.selectedModels.count < tzImagePickerVc.maxImagesCount) { | |
| 953 | + if ([mediaType isEqualToString:@"public.movie"] && !tzImagePickerVc.allowPickingMultipleVideo) { | |
| 954 | + // 不能多选视频的情况下,不选中拍摄的视频 | |
| 955 | + } else { | |
| 956 | + assetModel.isSelected = YES; | |
| 957 | + [tzImagePickerVc addSelectedModel:assetModel]; | |
| 958 | + [self refreshBottomToolBarStatus]; | |
| 959 | + } | |
| 960 | + } | |
| 961 | + self->_collectionView.hidden = YES; | |
| 962 | + [self->_collectionView reloadData]; | |
| 963 | + | |
| 964 | + self->_shouldScrollToBottom = YES; | |
| 965 | + [self scrollCollectionViewToBottom]; | |
| 966 | + }]; | |
| 967 | + }]; | |
| 968 | +} | |
| 969 | + | |
| 970 | +- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { | |
| 971 | + [picker dismissViewControllerAnimated:YES completion:nil]; | |
| 972 | +} | |
| 973 | + | |
| 974 | +- (void)dealloc { | |
| 975 | + // NSLog(@"%@ dealloc",NSStringFromClass(self.class)); | |
| 976 | +} | |
| 977 | + | |
| 978 | +#pragma mark - Asset Caching | |
| 979 | + | |
| 980 | +- (void)resetCachedAssets { | |
| 981 | + [[TZImageManager manager].cachingImageManager stopCachingImagesForAllAssets]; | |
| 982 | + self.previousPreheatRect = CGRectZero; | |
| 983 | +} | |
| 984 | + | |
| 985 | +- (void)updateCachedAssets { | |
| 986 | + BOOL isViewVisible = [self isViewLoaded] && [[self view] window] != nil; | |
| 987 | + if (!isViewVisible) { return; } | |
| 988 | + | |
| 989 | + // The preheat window is twice the height of the visible rect. | |
| 990 | + CGRect preheatRect = _collectionView.bounds; | |
| 991 | + preheatRect = CGRectInset(preheatRect, 0.0f, -0.5f * CGRectGetHeight(preheatRect)); | |
| 992 | + | |
| 993 | + /* | |
| 994 | + Check if the collection view is showing an area that is significantly | |
| 995 | + different to the last preheated area. | |
| 996 | + */ | |
| 997 | + CGFloat delta = ABS(CGRectGetMidY(preheatRect) - CGRectGetMidY(self.previousPreheatRect)); | |
| 998 | + if (delta > CGRectGetHeight(_collectionView.bounds) / 3.0f) { | |
| 999 | + | |
| 1000 | + // Compute the assets to start caching and to stop caching. | |
| 1001 | + NSMutableArray *addedIndexPaths = [NSMutableArray array]; | |
| 1002 | + NSMutableArray *removedIndexPaths = [NSMutableArray array]; | |
| 1003 | + | |
| 1004 | + [self computeDifferenceBetweenRect:self.previousPreheatRect andRect:preheatRect removedHandler:^(CGRect removedRect) { | |
| 1005 | + NSArray *indexPaths = [self aapl_indexPathsForElementsInRect:removedRect]; | |
| 1006 | + [removedIndexPaths addObjectsFromArray:indexPaths]; | |
| 1007 | + } addedHandler:^(CGRect addedRect) { | |
| 1008 | + NSArray *indexPaths = [self aapl_indexPathsForElementsInRect:addedRect]; | |
| 1009 | + [addedIndexPaths addObjectsFromArray:indexPaths]; | |
| 1010 | + }]; | |
| 1011 | + | |
| 1012 | + NSArray *assetsToStartCaching = [self assetsAtIndexPaths:addedIndexPaths]; | |
| 1013 | + NSArray *assetsToStopCaching = [self assetsAtIndexPaths:removedIndexPaths]; | |
| 1014 | + | |
| 1015 | + // Update the assets the PHCachingImageManager is caching. | |
| 1016 | + [[TZImageManager manager].cachingImageManager startCachingImagesForAssets:assetsToStartCaching | |
| 1017 | + targetSize:AssetGridThumbnailSize | |
| 1018 | + contentMode:PHImageContentModeAspectFill | |
| 1019 | + options:nil]; | |
| 1020 | + [[TZImageManager manager].cachingImageManager stopCachingImagesForAssets:assetsToStopCaching | |
| 1021 | + targetSize:AssetGridThumbnailSize | |
| 1022 | + contentMode:PHImageContentModeAspectFill | |
| 1023 | + options:nil]; | |
| 1024 | + | |
| 1025 | + // Store the preheat rect to compare against in the future. | |
| 1026 | + self.previousPreheatRect = preheatRect; | |
| 1027 | + } | |
| 1028 | +} | |
| 1029 | + | |
| 1030 | +- (void)computeDifferenceBetweenRect:(CGRect)oldRect andRect:(CGRect)newRect removedHandler:(void (^)(CGRect removedRect))removedHandler addedHandler:(void (^)(CGRect addedRect))addedHandler { | |
| 1031 | + if (CGRectIntersectsRect(newRect, oldRect)) { | |
| 1032 | + CGFloat oldMaxY = CGRectGetMaxY(oldRect); | |
| 1033 | + CGFloat oldMinY = CGRectGetMinY(oldRect); | |
| 1034 | + CGFloat newMaxY = CGRectGetMaxY(newRect); | |
| 1035 | + CGFloat newMinY = CGRectGetMinY(newRect); | |
| 1036 | + | |
| 1037 | + if (newMaxY > oldMaxY) { | |
| 1038 | + CGRect rectToAdd = CGRectMake(newRect.origin.x, oldMaxY, newRect.size.width, (newMaxY - oldMaxY)); | |
| 1039 | + addedHandler(rectToAdd); | |
| 1040 | + } | |
| 1041 | + | |
| 1042 | + if (oldMinY > newMinY) { | |
| 1043 | + CGRect rectToAdd = CGRectMake(newRect.origin.x, newMinY, newRect.size.width, (oldMinY - newMinY)); | |
| 1044 | + addedHandler(rectToAdd); | |
| 1045 | + } | |
| 1046 | + | |
| 1047 | + if (newMaxY < oldMaxY) { | |
| 1048 | + CGRect rectToRemove = CGRectMake(newRect.origin.x, newMaxY, newRect.size.width, (oldMaxY - newMaxY)); | |
| 1049 | + removedHandler(rectToRemove); | |
| 1050 | + } | |
| 1051 | + | |
| 1052 | + if (oldMinY < newMinY) { | |
| 1053 | + CGRect rectToRemove = CGRectMake(newRect.origin.x, oldMinY, newRect.size.width, (newMinY - oldMinY)); | |
| 1054 | + removedHandler(rectToRemove); | |
| 1055 | + } | |
| 1056 | + } else { | |
| 1057 | + addedHandler(newRect); | |
| 1058 | + removedHandler(oldRect); | |
| 1059 | + } | |
| 1060 | +} | |
| 1061 | + | |
| 1062 | +- (NSArray *)assetsAtIndexPaths:(NSArray *)indexPaths { | |
| 1063 | + if (indexPaths.count == 0) { return nil; } | |
| 1064 | + | |
| 1065 | + NSMutableArray *assets = [NSMutableArray arrayWithCapacity:indexPaths.count]; | |
| 1066 | + for (NSIndexPath *indexPath in indexPaths) { | |
| 1067 | + if (indexPath.item < _models.count) { | |
| 1068 | + TZAssetModel *model = _models[indexPath.item]; | |
| 1069 | + [assets addObject:model.asset]; | |
| 1070 | + } | |
| 1071 | + } | |
| 1072 | + | |
| 1073 | + return assets; | |
| 1074 | +} | |
| 1075 | + | |
| 1076 | +- (NSArray *)aapl_indexPathsForElementsInRect:(CGRect)rect { | |
| 1077 | + NSArray *allLayoutAttributes = [_collectionView.collectionViewLayout layoutAttributesForElementsInRect:rect]; | |
| 1078 | + if (allLayoutAttributes.count == 0) { return nil; } | |
| 1079 | + NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:allLayoutAttributes.count]; | |
| 1080 | + for (UICollectionViewLayoutAttributes *layoutAttributes in allLayoutAttributes) { | |
| 1081 | + NSIndexPath *indexPath = layoutAttributes.indexPath; | |
| 1082 | + [indexPaths addObject:indexPath]; | |
| 1083 | + } | |
| 1084 | + return indexPaths; | |
| 1085 | +} | |
| 1086 | +#pragma clang diagnostic pop | |
| 1087 | + | |
| 1088 | +@end | |
| 1089 | + | |
| 1090 | + | |
| 1091 | + | |
| 1092 | +@implementation TZCollectionView | |
| 1093 | + | |
| 1094 | +- (BOOL)touchesShouldCancelInContentView:(UIView *)view { | |
| 1095 | + if ([view isKindOfClass:[UIControl class]]) { | |
| 1096 | + return YES; | |
| 1097 | + } | |
| 1098 | + return [super touchesShouldCancelInContentView:view]; | |
| 1099 | +} | |
| 1100 | + | |
| 1101 | +@end | ... | ... |
CNLiveImagePickerController/TZPhotoPreviewCell.h
0 → 100755
| 1 | +// | |
| 2 | +// TZPhotoPreviewCell.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 | + | |
| 11 | +@class TZAssetModel; | |
| 12 | +@interface TZAssetPreviewCell : UICollectionViewCell | |
| 13 | +@property (nonatomic, strong) TZAssetModel *model; | |
| 14 | +@property (nonatomic, copy) void (^singleTapGestureBlock)(void); | |
| 15 | +- (void)configSubviews; | |
| 16 | +- (void)photoPreviewCollectionViewDidScroll; | |
| 17 | +@end | |
| 18 | + | |
| 19 | + | |
| 20 | +@class TZAssetModel,TZProgressView,TZPhotoPreviewView; | |
| 21 | +@interface TZPhotoPreviewCell : TZAssetPreviewCell | |
| 22 | + | |
| 23 | +@property (nonatomic, copy) void (^imageProgressUpdateBlock)(double progress); | |
| 24 | + | |
| 25 | +@property (nonatomic, strong) TZPhotoPreviewView *previewView; | |
| 26 | + | |
| 27 | +@property (nonatomic, assign) BOOL allowCrop; | |
| 28 | +@property (nonatomic, assign) CGRect cropRect; | |
| 29 | + | |
| 30 | +- (void)recoverSubviews; | |
| 31 | + | |
| 32 | +@end | |
| 33 | + | |
| 34 | + | |
| 35 | +@interface TZPhotoPreviewView : UIView | |
| 36 | +@property (nonatomic, strong) UIImageView *imageView; | |
| 37 | +@property (nonatomic, strong) UIScrollView *scrollView; | |
| 38 | +@property (nonatomic, strong) UIView *imageContainerView; | |
| 39 | +@property (nonatomic, strong) TZProgressView *progressView; | |
| 40 | + | |
| 41 | +@property (nonatomic, assign) BOOL allowCrop; | |
| 42 | +@property (nonatomic, assign) CGRect cropRect; | |
| 43 | + | |
| 44 | +@property (nonatomic, strong) TZAssetModel *model; | |
| 45 | +@property (nonatomic, strong) id asset; | |
| 46 | +@property (nonatomic, copy) void (^singleTapGestureBlock)(void); | |
| 47 | +@property (nonatomic, copy) void (^imageProgressUpdateBlock)(double progress); | |
| 48 | + | |
| 49 | +@property (nonatomic, assign) int32_t imageRequestID; | |
| 50 | + | |
| 51 | +- (void)recoverSubviews; | |
| 52 | +@end | |
| 53 | + | |
| 54 | + | |
| 55 | +@class AVPlayer, AVPlayerLayer; | |
| 56 | +@interface TZVideoPreviewCell : TZAssetPreviewCell | |
| 57 | +@property (strong, nonatomic) AVPlayer *player; | |
| 58 | +@property (strong, nonatomic) AVPlayerLayer *playerLayer; | |
| 59 | +@property (strong, nonatomic) UIButton *playButton; | |
| 60 | +@property (strong, nonatomic) UIImage *cover; | |
| 61 | +- (void)pausePlayerAndShowNaviBar; | |
| 62 | +@end | |
| 63 | + | |
| 64 | + | |
| 65 | +@interface TZGifPreviewCell : TZAssetPreviewCell | |
| 66 | +@property (strong, nonatomic) TZPhotoPreviewView *previewView; | |
| 67 | +@end | ... | ... |
CNLiveImagePickerController/TZPhotoPreviewCell.m
0 → 100755
| 1 | +// | |
| 2 | +// TZPhotoPreviewCell.m | |
| 3 | +// TZImagePickerController | |
| 4 | +// | |
| 5 | +// Created by 谭真 on 15/12/24. | |
| 6 | +// Copyright © 2015年 谭真. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "TZPhotoPreviewCell.h" | |
| 10 | +#import "TZAssetModel.h" | |
| 11 | +#import "UIView+Layout.h" | |
| 12 | +#import "TZImageManager.h" | |
| 13 | +#import "TZProgressView.h" | |
| 14 | +#import "TZImageCropManager.h" | |
| 15 | +#import <MediaPlayer/MediaPlayer.h> | |
| 16 | +#import "TZImagePickerController.h" | |
| 17 | + | |
| 18 | +@implementation TZAssetPreviewCell | |
| 19 | + | |
| 20 | +- (instancetype)initWithFrame:(CGRect)frame { | |
| 21 | + self = [super initWithFrame:frame]; | |
| 22 | + if (self) { | |
| 23 | + self.backgroundColor = [UIColor blackColor]; | |
| 24 | + [self configSubviews]; | |
| 25 | + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(photoPreviewCollectionViewDidScroll) name:@"photoPreviewCollectionViewDidScroll" object:nil]; | |
| 26 | + } | |
| 27 | + return self; | |
| 28 | +} | |
| 29 | + | |
| 30 | +- (void)configSubviews { | |
| 31 | + | |
| 32 | +} | |
| 33 | + | |
| 34 | +#pragma mark - Notification | |
| 35 | + | |
| 36 | +- (void)photoPreviewCollectionViewDidScroll { | |
| 37 | + | |
| 38 | +} | |
| 39 | + | |
| 40 | +- (void)dealloc { | |
| 41 | + [[NSNotificationCenter defaultCenter] removeObserver:self]; | |
| 42 | +} | |
| 43 | + | |
| 44 | +@end | |
| 45 | + | |
| 46 | + | |
| 47 | +@implementation TZPhotoPreviewCell | |
| 48 | + | |
| 49 | +- (void)configSubviews { | |
| 50 | + self.previewView = [[TZPhotoPreviewView alloc] initWithFrame:CGRectZero]; | |
| 51 | + __weak typeof(self) weakSelf = self; | |
| 52 | + [self.previewView setSingleTapGestureBlock:^{ | |
| 53 | + __strong typeof(weakSelf) strongSelf = weakSelf; | |
| 54 | + if (strongSelf.singleTapGestureBlock) { | |
| 55 | + strongSelf.singleTapGestureBlock(); | |
| 56 | + } | |
| 57 | + }]; | |
| 58 | + [self.previewView setImageProgressUpdateBlock:^(double progress) { | |
| 59 | + __strong typeof(weakSelf) strongSelf = weakSelf; | |
| 60 | + if (strongSelf.imageProgressUpdateBlock) { | |
| 61 | + strongSelf.imageProgressUpdateBlock(progress); | |
| 62 | + } | |
| 63 | + }]; | |
| 64 | + [self addSubview:self.previewView]; | |
| 65 | +} | |
| 66 | + | |
| 67 | +- (void)setModel:(TZAssetModel *)model { | |
| 68 | + [super setModel:model]; | |
| 69 | + _previewView.asset = model.asset; | |
| 70 | +} | |
| 71 | + | |
| 72 | +- (void)recoverSubviews { | |
| 73 | + [_previewView recoverSubviews]; | |
| 74 | +} | |
| 75 | + | |
| 76 | +- (void)setAllowCrop:(BOOL)allowCrop { | |
| 77 | + _allowCrop = allowCrop; | |
| 78 | + _previewView.allowCrop = allowCrop; | |
| 79 | +} | |
| 80 | + | |
| 81 | +- (void)setCropRect:(CGRect)cropRect { | |
| 82 | + _cropRect = cropRect; | |
| 83 | + _previewView.cropRect = cropRect; | |
| 84 | +} | |
| 85 | + | |
| 86 | +- (void)layoutSubviews { | |
| 87 | + [super layoutSubviews]; | |
| 88 | + self.previewView.frame = self.bounds; | |
| 89 | +} | |
| 90 | + | |
| 91 | +@end | |
| 92 | + | |
| 93 | + | |
| 94 | +@interface TZPhotoPreviewView ()<UIScrollViewDelegate> | |
| 95 | +@property (assign, nonatomic) BOOL isRequestingGIF; | |
| 96 | +@end | |
| 97 | + | |
| 98 | +@implementation TZPhotoPreviewView | |
| 99 | + | |
| 100 | +- (instancetype)initWithFrame:(CGRect)frame { | |
| 101 | + self = [super initWithFrame:frame]; | |
| 102 | + if (self) { | |
| 103 | + _scrollView = [[UIScrollView alloc] init]; | |
| 104 | + _scrollView.bouncesZoom = YES; | |
| 105 | + _scrollView.maximumZoomScale = 2.5; | |
| 106 | + _scrollView.minimumZoomScale = 1.0; | |
| 107 | + _scrollView.multipleTouchEnabled = YES; | |
| 108 | + _scrollView.delegate = self; | |
| 109 | + _scrollView.scrollsToTop = NO; | |
| 110 | + _scrollView.showsHorizontalScrollIndicator = NO; | |
| 111 | + _scrollView.showsVerticalScrollIndicator = YES; | |
| 112 | + _scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; | |
| 113 | + _scrollView.delaysContentTouches = NO; | |
| 114 | + _scrollView.canCancelContentTouches = YES; | |
| 115 | + _scrollView.alwaysBounceVertical = NO; | |
| 116 | + if (@available(iOS 11, *)) { | |
| 117 | + _scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; | |
| 118 | + } | |
| 119 | + [self addSubview:_scrollView]; | |
| 120 | + | |
| 121 | + _imageContainerView = [[UIView alloc] init]; | |
| 122 | + _imageContainerView.clipsToBounds = YES; | |
| 123 | + _imageContainerView.contentMode = UIViewContentModeScaleAspectFill; | |
| 124 | + [_scrollView addSubview:_imageContainerView]; | |
| 125 | + | |
| 126 | + _imageView = [[UIImageView alloc] init]; | |
| 127 | + _imageView.backgroundColor = [UIColor colorWithWhite:1.000 alpha:0.500]; | |
| 128 | + _imageView.contentMode = UIViewContentModeScaleAspectFill; | |
| 129 | + _imageView.clipsToBounds = YES; | |
| 130 | + [_imageContainerView addSubview:_imageView]; | |
| 131 | + | |
| 132 | + UITapGestureRecognizer *tap1 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTap:)]; | |
| 133 | + [self addGestureRecognizer:tap1]; | |
| 134 | + UITapGestureRecognizer *tap2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTap:)]; | |
| 135 | + tap2.numberOfTapsRequired = 2; | |
| 136 | + [tap1 requireGestureRecognizerToFail:tap2]; | |
| 137 | + [self addGestureRecognizer:tap2]; | |
| 138 | + | |
| 139 | + [self configProgressView]; | |
| 140 | + } | |
| 141 | + return self; | |
| 142 | +} | |
| 143 | + | |
| 144 | +- (void)configProgressView { | |
| 145 | + _progressView = [[TZProgressView alloc] init]; | |
| 146 | + _progressView.hidden = YES; | |
| 147 | + [self addSubview:_progressView]; | |
| 148 | +} | |
| 149 | + | |
| 150 | +- (void)setModel:(TZAssetModel *)model { | |
| 151 | + _model = model; | |
| 152 | + self.isRequestingGIF = NO; | |
| 153 | + [_scrollView setZoomScale:1.0 animated:NO]; | |
| 154 | + if (model.type == TZAssetModelMediaTypePhotoGif) { | |
| 155 | + // 先显示缩略图 | |
| 156 | + [[TZImageManager manager] getPhotoWithAsset:model.asset completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) { | |
| 157 | + self.imageView.image = photo; | |
| 158 | + [self resizeSubviews]; | |
| 159 | + if (self.isRequestingGIF) { | |
| 160 | + return; | |
| 161 | + } | |
| 162 | + // 再显示gif动图 | |
| 163 | + self.isRequestingGIF = YES; | |
| 164 | + [[TZImageManager manager] getOriginalPhotoDataWithAsset:model.asset progressHandler:^(double progress, NSError *error, BOOL *stop, NSDictionary *info) { | |
| 165 | + progress = progress > 0.02 ? progress : 0.02; | |
| 166 | + dispatch_async(dispatch_get_main_queue(), ^{ | |
| 167 | + self.progressView.progress = progress; | |
| 168 | + if (progress >= 1) { | |
| 169 | + self.progressView.hidden = YES; | |
| 170 | + } else { | |
| 171 | + self.progressView.hidden = NO; | |
| 172 | + } | |
| 173 | + }); | |
| 174 | +#ifdef DEBUG | |
| 175 | + NSLog(@"[TZImagePickerController] getOriginalPhotoDataWithAsset:%f error:%@", progress, error); | |
| 176 | +#endif | |
| 177 | + } completion:^(NSData *data, NSDictionary *info, BOOL isDegraded) { | |
| 178 | + if (!isDegraded) { | |
| 179 | + self.isRequestingGIF = NO; | |
| 180 | + self.progressView.hidden = YES; | |
| 181 | + self.imageView.image = [UIImage sd_tz_animatedGIFWithData:data]; | |
| 182 | + [self resizeSubviews]; | |
| 183 | + } | |
| 184 | + }]; | |
| 185 | + } progressHandler:nil networkAccessAllowed:NO]; | |
| 186 | + } else { | |
| 187 | + self.asset = model.asset; | |
| 188 | + } | |
| 189 | +} | |
| 190 | + | |
| 191 | +- (void)setAsset:(id)asset { | |
| 192 | + if (_asset && self.imageRequestID) { | |
| 193 | + [[PHImageManager defaultManager] cancelImageRequest:self.imageRequestID]; | |
| 194 | + } | |
| 195 | + | |
| 196 | + _asset = asset; | |
| 197 | + self.imageRequestID = [[TZImageManager manager] getPhotoWithAsset:asset completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) { | |
| 198 | + if (![asset isEqual:self->_asset]) return; | |
| 199 | + self.imageView.image = photo; | |
| 200 | + [self resizeSubviews]; | |
| 201 | + self->_progressView.hidden = YES; | |
| 202 | + if (self.imageProgressUpdateBlock) { | |
| 203 | + self.imageProgressUpdateBlock(1); | |
| 204 | + } | |
| 205 | + if (!isDegraded) { | |
| 206 | + self.imageRequestID = 0; | |
| 207 | + } | |
| 208 | + } progressHandler:^(double progress, NSError *error, BOOL *stop, NSDictionary *info) { | |
| 209 | + if (![asset isEqual:self->_asset]) return; | |
| 210 | + self->_progressView.hidden = NO; | |
| 211 | + [self bringSubviewToFront:self->_progressView]; | |
| 212 | + progress = progress > 0.02 ? progress : 0.02; | |
| 213 | + self->_progressView.progress = progress; | |
| 214 | + if (self.imageProgressUpdateBlock && progress < 1) { | |
| 215 | + self.imageProgressUpdateBlock(progress); | |
| 216 | + } | |
| 217 | + | |
| 218 | + if (progress >= 1) { | |
| 219 | + self->_progressView.hidden = YES; | |
| 220 | + self.imageRequestID = 0; | |
| 221 | + } | |
| 222 | + } networkAccessAllowed:YES]; | |
| 223 | +} | |
| 224 | + | |
| 225 | +- (void)recoverSubviews { | |
| 226 | + [_scrollView setZoomScale:1.0 animated:NO]; | |
| 227 | + [self resizeSubviews]; | |
| 228 | +} | |
| 229 | + | |
| 230 | +- (void)resizeSubviews { | |
| 231 | + _imageContainerView.tz_origin = CGPointZero; | |
| 232 | + _imageContainerView.tz_width = self.scrollView.tz_width; | |
| 233 | + | |
| 234 | + UIImage *image = _imageView.image; | |
| 235 | + if (image.size.height / image.size.width > self.tz_height / self.scrollView.tz_width) { | |
| 236 | + _imageContainerView.tz_height = floor(image.size.height / (image.size.width / self.scrollView.tz_width)); | |
| 237 | + } else { | |
| 238 | + CGFloat height = image.size.height / image.size.width * self.scrollView.tz_width; | |
| 239 | + if (height < 1 || isnan(height)) height = self.tz_height; | |
| 240 | + height = floor(height); | |
| 241 | + _imageContainerView.tz_height = height; | |
| 242 | + _imageContainerView.tz_centerY = self.tz_height / 2; | |
| 243 | + } | |
| 244 | + if (_imageContainerView.tz_height > self.tz_height && _imageContainerView.tz_height - self.tz_height <= 1) { | |
| 245 | + _imageContainerView.tz_height = self.tz_height; | |
| 246 | + } | |
| 247 | + CGFloat contentSizeH = MAX(_imageContainerView.tz_height, self.tz_height); | |
| 248 | + _scrollView.contentSize = CGSizeMake(self.scrollView.tz_width, contentSizeH); | |
| 249 | + [_scrollView scrollRectToVisible:self.bounds animated:NO]; | |
| 250 | + _scrollView.alwaysBounceVertical = _imageContainerView.tz_height <= self.tz_height ? NO : YES; | |
| 251 | + _imageView.frame = _imageContainerView.bounds; | |
| 252 | + | |
| 253 | + [self refreshScrollViewContentSize]; | |
| 254 | +} | |
| 255 | + | |
| 256 | +- (void)setAllowCrop:(BOOL)allowCrop { | |
| 257 | + _allowCrop = allowCrop; | |
| 258 | + _scrollView.maximumZoomScale = allowCrop ? 4.0 : 2.5; | |
| 259 | + | |
| 260 | + if ([self.asset isKindOfClass:[PHAsset class]]) { | |
| 261 | + PHAsset *phAsset = (PHAsset *)self.asset; | |
| 262 | + CGFloat aspectRatio = phAsset.pixelWidth / (CGFloat)phAsset.pixelHeight; | |
| 263 | + // 优化超宽图片的显示 | |
| 264 | + if (aspectRatio > 1.5) { | |
| 265 | + self.scrollView.maximumZoomScale *= aspectRatio / 1.5; | |
| 266 | + } | |
| 267 | + } | |
| 268 | +} | |
| 269 | + | |
| 270 | +- (void)refreshScrollViewContentSize { | |
| 271 | + if (_allowCrop) { | |
| 272 | + // 1.7.2 如果允许裁剪,需要让图片的任意部分都能在裁剪框内,于是对_scrollView做了如下处理: | |
| 273 | + // 1.让contentSize增大(裁剪框右下角的图片部分) | |
| 274 | + CGFloat contentWidthAdd = self.scrollView.tz_width - CGRectGetMaxX(_cropRect); | |
| 275 | + CGFloat contentHeightAdd = (MIN(_imageContainerView.tz_height, self.tz_height) - self.cropRect.size.height) / 2; | |
| 276 | + CGFloat newSizeW = self.scrollView.contentSize.width + contentWidthAdd; | |
| 277 | + CGFloat newSizeH = MAX(self.scrollView.contentSize.height, self.tz_height) + contentHeightAdd; | |
| 278 | + _scrollView.contentSize = CGSizeMake(newSizeW, newSizeH); | |
| 279 | + _scrollView.alwaysBounceVertical = YES; | |
| 280 | + // 2.让scrollView新增滑动区域(裁剪框左上角的图片部分) | |
| 281 | + if (contentHeightAdd > 0 || contentWidthAdd > 0) { | |
| 282 | + _scrollView.contentInset = UIEdgeInsetsMake(contentHeightAdd, _cropRect.origin.x, 0, 0); | |
| 283 | + } else { | |
| 284 | + _scrollView.contentInset = UIEdgeInsetsZero; | |
| 285 | + } | |
| 286 | + } | |
| 287 | +} | |
| 288 | + | |
| 289 | +- (void)layoutSubviews { | |
| 290 | + [super layoutSubviews]; | |
| 291 | + _scrollView.frame = CGRectMake(10, 0, self.tz_width - 20, self.tz_height); | |
| 292 | + static CGFloat progressWH = 40; | |
| 293 | + CGFloat progressX = (self.tz_width - progressWH) / 2; | |
| 294 | + CGFloat progressY = (self.tz_height - progressWH) / 2; | |
| 295 | + _progressView.frame = CGRectMake(progressX, progressY, progressWH, progressWH); | |
| 296 | + | |
| 297 | + [self recoverSubviews]; | |
| 298 | +} | |
| 299 | + | |
| 300 | +#pragma mark - UITapGestureRecognizer Event | |
| 301 | + | |
| 302 | +- (void)doubleTap:(UITapGestureRecognizer *)tap { | |
| 303 | + if (_scrollView.zoomScale > 1.0) { | |
| 304 | + _scrollView.contentInset = UIEdgeInsetsZero; | |
| 305 | + [_scrollView setZoomScale:1.0 animated:YES]; | |
| 306 | + } else { | |
| 307 | + CGPoint touchPoint = [tap locationInView:self.imageView]; | |
| 308 | + CGFloat newZoomScale = _scrollView.maximumZoomScale; | |
| 309 | + CGFloat xsize = self.frame.size.width / newZoomScale; | |
| 310 | + CGFloat ysize = self.frame.size.height / newZoomScale; | |
| 311 | + [_scrollView zoomToRect:CGRectMake(touchPoint.x - xsize/2, touchPoint.y - ysize/2, xsize, ysize) animated:YES]; | |
| 312 | + } | |
| 313 | +} | |
| 314 | + | |
| 315 | +- (void)singleTap:(UITapGestureRecognizer *)tap { | |
| 316 | + if (self.singleTapGestureBlock) { | |
| 317 | + self.singleTapGestureBlock(); | |
| 318 | + } | |
| 319 | +} | |
| 320 | + | |
| 321 | +#pragma mark - UIScrollViewDelegate | |
| 322 | + | |
| 323 | +- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { | |
| 324 | + return _imageContainerView; | |
| 325 | +} | |
| 326 | + | |
| 327 | +- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view { | |
| 328 | + scrollView.contentInset = UIEdgeInsetsZero; | |
| 329 | +} | |
| 330 | + | |
| 331 | +- (void)scrollViewDidZoom:(UIScrollView *)scrollView { | |
| 332 | + [self refreshImageContainerViewCenter]; | |
| 333 | +} | |
| 334 | + | |
| 335 | +- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale { | |
| 336 | + [self refreshScrollViewContentSize]; | |
| 337 | +} | |
| 338 | + | |
| 339 | +#pragma mark - Private | |
| 340 | + | |
| 341 | +- (void)refreshImageContainerViewCenter { | |
| 342 | + CGFloat offsetX = (_scrollView.tz_width > _scrollView.contentSize.width) ? ((_scrollView.tz_width - _scrollView.contentSize.width) * 0.5) : 0.0; | |
| 343 | + CGFloat offsetY = (_scrollView.tz_height > _scrollView.contentSize.height) ? ((_scrollView.tz_height - _scrollView.contentSize.height) * 0.5) : 0.0; | |
| 344 | + self.imageContainerView.center = CGPointMake(_scrollView.contentSize.width * 0.5 + offsetX, _scrollView.contentSize.height * 0.5 + offsetY); | |
| 345 | +} | |
| 346 | + | |
| 347 | +@end | |
| 348 | + | |
| 349 | + | |
| 350 | +@implementation TZVideoPreviewCell | |
| 351 | + | |
| 352 | +- (void)configSubviews { | |
| 353 | + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pausePlayerAndShowNaviBar) name:UIApplicationWillResignActiveNotification object:nil]; | |
| 354 | +} | |
| 355 | + | |
| 356 | +- (void)configPlayButton { | |
| 357 | + if (_playButton) { | |
| 358 | + [_playButton removeFromSuperview]; | |
| 359 | + } | |
| 360 | + _playButton = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 361 | + [_playButton setImage:[UIImage imageNamedFromMyBundle:@"MMVideoPreviewPlay"] forState:UIControlStateNormal]; | |
| 362 | + [_playButton setImage:[UIImage imageNamedFromMyBundle:@"MMVideoPreviewPlayHL"] forState:UIControlStateHighlighted]; | |
| 363 | + [_playButton addTarget:self action:@selector(playButtonClick) forControlEvents:UIControlEventTouchUpInside]; | |
| 364 | + [self addSubview:_playButton]; | |
| 365 | +} | |
| 366 | + | |
| 367 | +- (void)setModel:(TZAssetModel *)model { | |
| 368 | + [super setModel:model]; | |
| 369 | + [self configMoviePlayer]; | |
| 370 | +} | |
| 371 | + | |
| 372 | +- (void)configMoviePlayer { | |
| 373 | + if (_player) { | |
| 374 | + [_playerLayer removeFromSuperlayer]; | |
| 375 | + _playerLayer = nil; | |
| 376 | + [_player pause]; | |
| 377 | + _player = nil; | |
| 378 | + } | |
| 379 | + | |
| 380 | + [[TZImageManager manager] getPhotoWithAsset:self.model.asset completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) { | |
| 381 | + self->_cover = photo; | |
| 382 | + }]; | |
| 383 | + [[TZImageManager manager] getVideoWithAsset:self.model.asset completion:^(AVPlayerItem *playerItem, NSDictionary *info) { | |
| 384 | + dispatch_async(dispatch_get_main_queue(), ^{ | |
| 385 | + self->_player = [AVPlayer playerWithPlayerItem:playerItem]; | |
| 386 | + self->_playerLayer = [AVPlayerLayer playerLayerWithPlayer:self->_player]; | |
| 387 | + self->_playerLayer.backgroundColor = [UIColor blackColor].CGColor; | |
| 388 | + self->_playerLayer.frame = self.bounds; | |
| 389 | + [self.layer addSublayer:self->_playerLayer]; | |
| 390 | + [self configPlayButton]; | |
| 391 | + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pausePlayerAndShowNaviBar) name:AVPlayerItemDidPlayToEndTimeNotification object:self->_player.currentItem]; | |
| 392 | + }); | |
| 393 | + }]; | |
| 394 | +} | |
| 395 | + | |
| 396 | +- (void)layoutSubviews { | |
| 397 | + [super layoutSubviews]; | |
| 398 | + _playerLayer.frame = self.bounds; | |
| 399 | + _playButton.frame = CGRectMake(0, 64, self.tz_width, self.tz_height - 64 - 44); | |
| 400 | +} | |
| 401 | + | |
| 402 | +- (void)photoPreviewCollectionViewDidScroll { | |
| 403 | + [self pausePlayerAndShowNaviBar]; | |
| 404 | +} | |
| 405 | + | |
| 406 | +#pragma mark - Click Event | |
| 407 | + | |
| 408 | +- (void)playButtonClick { | |
| 409 | + CMTime currentTime = _player.currentItem.currentTime; | |
| 410 | + CMTime durationTime = _player.currentItem.duration; | |
| 411 | + if (_player.rate == 0.0f) { | |
| 412 | + if (currentTime.value == durationTime.value) [_player.currentItem seekToTime:CMTimeMake(0, 1)]; | |
| 413 | + [_player play]; | |
| 414 | + [_playButton setImage:nil forState:UIControlStateNormal]; | |
| 415 | + if (iOS7Later) [UIApplication sharedApplication].statusBarHidden = YES; | |
| 416 | + if (self.singleTapGestureBlock) { | |
| 417 | + self.singleTapGestureBlock(); | |
| 418 | + } | |
| 419 | + } else { | |
| 420 | + [self pausePlayerAndShowNaviBar]; | |
| 421 | + } | |
| 422 | +} | |
| 423 | + | |
| 424 | +- (void)pausePlayerAndShowNaviBar { | |
| 425 | + if (_player.rate != 0.0) { | |
| 426 | + [_player pause]; | |
| 427 | + [_playButton setImage:[UIImage imageNamedFromMyBundle:@"MMVideoPreviewPlay"] forState:UIControlStateNormal]; | |
| 428 | + if (self.singleTapGestureBlock) { | |
| 429 | + self.singleTapGestureBlock(); | |
| 430 | + } | |
| 431 | + } | |
| 432 | +} | |
| 433 | + | |
| 434 | +@end | |
| 435 | + | |
| 436 | + | |
| 437 | +@implementation TZGifPreviewCell | |
| 438 | + | |
| 439 | +- (void)configSubviews { | |
| 440 | + [self configPreviewView]; | |
| 441 | +} | |
| 442 | + | |
| 443 | +- (void)configPreviewView { | |
| 444 | + _previewView = [[TZPhotoPreviewView alloc] initWithFrame:CGRectZero]; | |
| 445 | + __weak typeof(self) weakSelf = self; | |
| 446 | + [_previewView setSingleTapGestureBlock:^{ | |
| 447 | + __strong typeof(weakSelf) strongSelf = weakSelf; | |
| 448 | + [strongSelf signleTapAction]; | |
| 449 | + }]; | |
| 450 | + [self addSubview:_previewView]; | |
| 451 | +} | |
| 452 | + | |
| 453 | +- (void)setModel:(TZAssetModel *)model { | |
| 454 | + [super setModel:model]; | |
| 455 | + _previewView.model = self.model; | |
| 456 | +} | |
| 457 | + | |
| 458 | +- (void)layoutSubviews { | |
| 459 | + [super layoutSubviews]; | |
| 460 | + _previewView.frame = self.bounds; | |
| 461 | +} | |
| 462 | + | |
| 463 | +#pragma mark - Click Event | |
| 464 | + | |
| 465 | +- (void)signleTapAction { | |
| 466 | + if (self.singleTapGestureBlock) { | |
| 467 | + self.singleTapGestureBlock(); | |
| 468 | + } | |
| 469 | +} | |
| 470 | + | |
| 471 | +@end | ... | ... |
CNLiveImagePickerController/TZPhotoPreviewController.h
0 → 100755
| 1 | +// | |
| 2 | +// TZPhotoPreviewController.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 | + | |
| 11 | +@interface TZPhotoPreviewController : UIViewController | |
| 12 | + | |
| 13 | +@property (nonatomic, strong) NSMutableArray *models; ///< All photo models / 所有图片模型数组 | |
| 14 | +@property (nonatomic, strong) NSMutableArray *photos; ///< All photos / 所有图片数组 | |
| 15 | +@property (nonatomic, assign) NSInteger currentIndex; ///< Index of the photo user click / 用户点击的图片的索引 | |
| 16 | +@property (nonatomic, assign) BOOL isSelectOriginalPhoto; ///< If YES,return original photo / 是否返回原图 | |
| 17 | +@property (nonatomic, assign) BOOL isCropImage; | |
| 18 | + | |
| 19 | +/// Return the new selected photos / 返回最新的选中图片数组 | |
| 20 | +@property (nonatomic, copy) void (^backButtonClickBlock)(BOOL isSelectOriginalPhoto); | |
| 21 | +@property (nonatomic, copy) void (^doneButtonClickBlock)(BOOL isSelectOriginalPhoto); | |
| 22 | +@property (nonatomic, copy) void (^doneButtonClickBlockCropMode)(UIImage *cropedImage,id asset); | |
| 23 | +@property (nonatomic, copy) void (^doneButtonClickBlockWithPreviewType)(NSArray<UIImage *> *photos,NSArray *assets,BOOL isSelectOriginalPhoto); | |
| 24 | + | |
| 25 | +@end | ... | ... |
CNLiveImagePickerController/TZPhotoPreviewController.m
0 → 100755
| 1 | +// | |
| 2 | +// TZPhotoPreviewController.m | |
| 3 | +// TZImagePickerController | |
| 4 | +// | |
| 5 | +// Created by 谭真 on 15/12/24. | |
| 6 | +// Copyright © 2015年 谭真. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "TZPhotoPreviewController.h" | |
| 10 | +#import "TZPhotoPreviewCell.h" | |
| 11 | +#import "TZAssetModel.h" | |
| 12 | +#import "UIView+Layout.h" | |
| 13 | +#import "TZImagePickerController.h" | |
| 14 | +#import "TZImageManager.h" | |
| 15 | +#import "TZImageCropManager.h" | |
| 16 | +#import <AssetsLibrary/AssetsLibrary.h>///lxg | |
| 17 | +#import "CNEditVideoController.h" | |
| 18 | + | |
| 19 | +@interface TZPhotoPreviewController ()<UICollectionViewDataSource,UICollectionViewDelegate,UIScrollViewDelegate> { | |
| 20 | + UICollectionView *_collectionView; | |
| 21 | + UICollectionViewFlowLayout *_layout; | |
| 22 | + NSArray *_photosTemp; | |
| 23 | + NSArray *_assetsTemp; | |
| 24 | + | |
| 25 | + UIView *_naviBar; | |
| 26 | + UIButton *_backButton; | |
| 27 | + UIButton *_selectButton; | |
| 28 | + UILabel *_indexLabel; | |
| 29 | + UIView *_navBarLine;///LXG线 | |
| 30 | + | |
| 31 | + UIView *_tipBar;///LXG添加提示视图 | |
| 32 | + UILabel *_tipContentLabel;///LXG添加提示内容 | |
| 33 | + | |
| 34 | + | |
| 35 | + UIView *_toolBar; | |
| 36 | + UIButton *_doneButton; | |
| 37 | + UIImageView *_numberImageView; | |
| 38 | + UILabel *_numberLabel; | |
| 39 | + UIButton *_originalPhotoButton; | |
| 40 | + UILabel *_originalPhotoLabel; | |
| 41 | + UILabel *_editTipLable;//LXG底部提示 | |
| 42 | + UIButton *_editTipButton;//LXG编辑按钮 | |
| 43 | + | |
| 44 | + | |
| 45 | + CGFloat _offsetItemCount; | |
| 46 | + | |
| 47 | + BOOL _didSetIsSelectOriginalPhoto; | |
| 48 | +} | |
| 49 | +@property (nonatomic, assign) BOOL isHideNaviBar; | |
| 50 | +@property (nonatomic, strong) UIView *cropBgView; | |
| 51 | +@property (nonatomic, strong) UIView *cropView; | |
| 52 | + | |
| 53 | +@property (nonatomic, assign) double progress; | |
| 54 | +@property (strong, nonatomic) id alertView; | |
| 55 | +@end | |
| 56 | + | |
| 57 | +@implementation TZPhotoPreviewController | |
| 58 | + | |
| 59 | +- (void)viewDidLoad { | |
| 60 | + [super viewDidLoad]; | |
| 61 | + [TZImageManager manager].shouldFixOrientation = YES; | |
| 62 | + TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 63 | + if (!_didSetIsSelectOriginalPhoto) { | |
| 64 | + _isSelectOriginalPhoto = _tzImagePickerVc.isSelectOriginalPhoto; | |
| 65 | + } | |
| 66 | + if (!self.models.count) { | |
| 67 | + self.models = [NSMutableArray arrayWithArray:_tzImagePickerVc.selectedModels]; | |
| 68 | + _assetsTemp = [NSMutableArray arrayWithArray:_tzImagePickerVc.selectedAssets]; | |
| 69 | + } | |
| 70 | + [self configCollectionView]; | |
| 71 | + [self configCustomNaviBar]; | |
| 72 | + [self configCustomTipBar];///lxg 添加提示 | |
| 73 | + [self configBottomToolBar]; | |
| 74 | + self.view.clipsToBounds = YES; | |
| 75 | + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didChangeStatusBarOrientationNotification:) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; | |
| 76 | +} | |
| 77 | + | |
| 78 | +- (void)setIsSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto { | |
| 79 | + _isSelectOriginalPhoto = isSelectOriginalPhoto; | |
| 80 | + _didSetIsSelectOriginalPhoto = YES; | |
| 81 | +} | |
| 82 | + | |
| 83 | +- (void)setPhotos:(NSMutableArray *)photos { | |
| 84 | + _photos = photos; | |
| 85 | + _photosTemp = [NSArray arrayWithArray:photos]; | |
| 86 | +} | |
| 87 | + | |
| 88 | +- (void)viewWillAppear:(BOOL)animated { | |
| 89 | + [super viewWillAppear:animated]; | |
| 90 | + [self.navigationController setNavigationBarHidden:YES animated:YES]; | |
| 91 | + if (iOS7Later) [UIApplication sharedApplication].statusBarHidden = YES; | |
| 92 | + if (_currentIndex) [_collectionView setContentOffset:CGPointMake((self.view.tz_width + 20) * _currentIndex, 0) animated:NO]; | |
| 93 | + [self refreshNaviBarAndBottomBarState]; | |
| 94 | +} | |
| 95 | + | |
| 96 | +- (void)viewWillDisappear:(BOOL)animated { | |
| 97 | + [super viewWillDisappear:animated]; | |
| 98 | + [self.navigationController setNavigationBarHidden:NO animated:YES]; | |
| 99 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 100 | + if (tzImagePickerVc.needShowStatusBar && iOS7Later) { | |
| 101 | + [UIApplication sharedApplication].statusBarHidden = NO; | |
| 102 | + } | |
| 103 | + [TZImageManager manager].shouldFixOrientation = NO; | |
| 104 | +} | |
| 105 | + | |
| 106 | +- (BOOL)prefersStatusBarHidden { | |
| 107 | + return YES; | |
| 108 | +} | |
| 109 | + | |
| 110 | +- (void)configCustomNaviBar { | |
| 111 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 112 | + | |
| 113 | + _naviBar = [[UIView alloc] initWithFrame:CGRectZero]; | |
| 114 | + _naviBar.backgroundColor = [UIColor colorWithRed:(34/255.0) green:(34/255.0) blue:(34/255.0) alpha:0.7]; | |
| 115 | + | |
| 116 | + _backButton = [[UIButton alloc] initWithFrame:CGRectZero]; | |
| 117 | + [_backButton setImage:[UIImage imageNamedFromMyBundle:@"navi_back"] forState:UIControlStateNormal]; | |
| 118 | + [_backButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; | |
| 119 | + [_backButton addTarget:self action:@selector(backButtonClick) forControlEvents:UIControlEventTouchUpInside]; | |
| 120 | + | |
| 121 | + _selectButton = [[UIButton alloc] initWithFrame:CGRectZero]; | |
| 122 | + [_selectButton setImage:tzImagePickerVc.photoDefImage forState:UIControlStateNormal]; | |
| 123 | + [_selectButton setImage:tzImagePickerVc.photoSelImage forState:UIControlStateSelected]; | |
| 124 | + _selectButton.imageView.clipsToBounds = YES; | |
| 125 | + _selectButton.imageEdgeInsets = UIEdgeInsetsMake(10, 0, 10, 0); | |
| 126 | + _selectButton.imageView.contentMode = UIViewContentModeScaleAspectFit; | |
| 127 | + [_selectButton addTarget:self action:@selector(select:) forControlEvents:UIControlEventTouchUpInside]; | |
| 128 | + _selectButton.hidden = !tzImagePickerVc.showSelectBtn; | |
| 129 | + | |
| 130 | + _indexLabel = [[UILabel alloc] init]; | |
| 131 | + _indexLabel.font = [UIFont systemFontOfSize:14]; | |
| 132 | + _indexLabel.textColor = [UIColor whiteColor]; | |
| 133 | + _indexLabel.textAlignment = NSTextAlignmentCenter; | |
| 134 | + ///lxg 导航栏线 | |
| 135 | + _navBarLine = [[UIView alloc]init]; | |
| 136 | + _navBarLine.backgroundColor = [UIColor colorWithWhite:0.5 alpha:1]; | |
| 137 | + _navBarLine.hidden = YES; | |
| 138 | + | |
| 139 | + [_naviBar addSubview:_selectButton]; | |
| 140 | + [_naviBar addSubview:_indexLabel]; | |
| 141 | + [_naviBar addSubview:_backButton]; | |
| 142 | + [_naviBar addSubview:_navBarLine];///lxg 添加线 | |
| 143 | + [self.view addSubview:_naviBar]; | |
| 144 | +} | |
| 145 | + | |
| 146 | +/// 添加提示条(LXG) | |
| 147 | +- (void)configCustomTipBar { | |
| 148 | + _tipBar = [[UIView alloc] initWithFrame:CGRectZero]; | |
| 149 | + static CGFloat rgb = 34 / 255.0; | |
| 150 | + _tipBar.backgroundColor = [UIColor colorWithRed:rgb green:rgb blue:rgb alpha:0.7]; | |
| 151 | + _tipBar.hidden = YES; | |
| 152 | + | |
| 153 | + _tipContentLabel = [[UILabel alloc] init]; | |
| 154 | + _tipContentLabel.font = [UIFont systemFontOfSize:15]; | |
| 155 | + _tipContentLabel.textColor = [UIColor whiteColor]; | |
| 156 | + _tipContentLabel.textAlignment = NSTextAlignmentLeft; | |
| 157 | + _tipContentLabel.backgroundColor = [UIColor clearColor]; | |
| 158 | + | |
| 159 | + [_tipBar addSubview:_tipContentLabel]; | |
| 160 | + [self.view addSubview:_tipBar]; | |
| 161 | + | |
| 162 | + | |
| 163 | +} | |
| 164 | + | |
| 165 | +- (void)configBottomToolBar { | |
| 166 | + _toolBar = [[UIView alloc] initWithFrame:CGRectZero]; | |
| 167 | + static CGFloat rgb = 34 / 255.0; | |
| 168 | + _toolBar.backgroundColor = [UIColor colorWithRed:rgb green:rgb blue:rgb alpha:0.7]; | |
| 169 | + | |
| 170 | + TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 171 | + if (_tzImagePickerVc.allowPickingOriginalPhoto) { | |
| 172 | + _originalPhotoButton = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 173 | + _originalPhotoButton.imageEdgeInsets = UIEdgeInsetsMake(0, -10, 0, 0); | |
| 174 | + _originalPhotoButton.backgroundColor = [UIColor clearColor]; | |
| 175 | + [_originalPhotoButton addTarget:self action:@selector(originalPhotoButtonClick) forControlEvents:UIControlEventTouchUpInside]; | |
| 176 | + _originalPhotoButton.titleLabel.font = [UIFont systemFontOfSize:13]; | |
| 177 | + [_originalPhotoButton setTitle:_tzImagePickerVc.fullImageBtnTitleStr forState:UIControlStateNormal]; | |
| 178 | + [_originalPhotoButton setTitle:_tzImagePickerVc.fullImageBtnTitleStr forState:UIControlStateSelected]; | |
| 179 | + [_originalPhotoButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal]; | |
| 180 | + [_originalPhotoButton setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected]; | |
| 181 | + [_originalPhotoButton setImage:_tzImagePickerVc.photoPreviewOriginDefImage forState:UIControlStateNormal]; | |
| 182 | + [_originalPhotoButton setImage:_tzImagePickerVc.photoOriginSelImage forState:UIControlStateSelected]; | |
| 183 | + | |
| 184 | + _originalPhotoLabel = [[UILabel alloc] init]; | |
| 185 | + _originalPhotoLabel.textAlignment = NSTextAlignmentLeft; | |
| 186 | + _originalPhotoLabel.font = [UIFont systemFontOfSize:13]; | |
| 187 | + _originalPhotoLabel.textColor = [UIColor whiteColor]; | |
| 188 | + _originalPhotoLabel.backgroundColor = [UIColor clearColor]; | |
| 189 | + if (_isSelectOriginalPhoto) [self showPhotoBytes]; | |
| 190 | + } | |
| 191 | + | |
| 192 | + _doneButton = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 193 | + _doneButton.titleLabel.font = [UIFont systemFontOfSize:16]; | |
| 194 | + [_doneButton addTarget:self action:@selector(doneButtonClick) forControlEvents:UIControlEventTouchUpInside]; | |
| 195 | + [_doneButton setTitle:_tzImagePickerVc.doneBtnTitleStr forState:UIControlStateNormal]; | |
| 196 | + [_doneButton setTitleColor:_tzImagePickerVc.oKButtonTitleColorNormal forState:UIControlStateNormal]; | |
| 197 | + | |
| 198 | + _numberImageView = [[UIImageView alloc] initWithImage:_tzImagePickerVc.photoNumberIconImage]; | |
| 199 | + _numberImageView.backgroundColor = [UIColor clearColor]; | |
| 200 | + _numberImageView.clipsToBounds = YES; | |
| 201 | + _numberImageView.contentMode = UIViewContentModeScaleAspectFit; | |
| 202 | + _numberImageView.hidden = _tzImagePickerVc.selectedModels.count <= 0; | |
| 203 | + | |
| 204 | + _numberLabel = [[UILabel alloc] init]; | |
| 205 | + _numberLabel.font = [UIFont systemFontOfSize:15]; | |
| 206 | + _numberLabel.textColor = [UIColor whiteColor]; | |
| 207 | + _numberLabel.textAlignment = NSTextAlignmentCenter; | |
| 208 | + _numberLabel.text = [NSString stringWithFormat:@"%zd",_tzImagePickerVc.selectedModels.count]; | |
| 209 | + _numberLabel.hidden = _tzImagePickerVc.selectedModels.count <= 0; | |
| 210 | + _numberLabel.backgroundColor = [UIColor clearColor]; | |
| 211 | +#pragma mark - 添加: 选择图片个数显示(LXG) | |
| 212 | + _numberImageView.hidden = YES; | |
| 213 | + _numberLabel.hidden = YES; | |
| 214 | + NSString *titleStr = _tzImagePickerVc.selectedModels.count > 0 ? [NSString stringWithFormat:@"完成(%zd)",_tzImagePickerVc.selectedModels.count] : @"完成"; | |
| 215 | + [_doneButton setTitle:titleStr forState:UIControlStateNormal]; | |
| 216 | + | |
| 217 | + ///LXG 添加视频编辑 | |
| 218 | + _editTipLable = [[UILabel alloc] init]; | |
| 219 | + _editTipLable.font = [UIFont systemFontOfSize:15]; | |
| 220 | + _editTipLable.textAlignment = NSTextAlignmentLeft; | |
| 221 | + _editTipLable.hidden = YES; | |
| 222 | + _editTipLable.textColor = [UIColor whiteColor]; | |
| 223 | + _editTipLable.backgroundColor = [UIColor clearColor]; | |
| 224 | + _editTipButton = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 225 | + _editTipButton.titleLabel.font = [UIFont systemFontOfSize:16]; | |
| 226 | + [_editTipButton addTarget:self action:@selector(editVideoButtonClick) forControlEvents:UIControlEventTouchUpInside]; | |
| 227 | + _editTipButton.hidden = YES; | |
| 228 | + [_editTipButton setTitle:_tzImagePickerVc.editBtnTitleStr forState:UIControlStateNormal]; | |
| 229 | + [_editTipButton setTitleColor:_tzImagePickerVc.editBtnTitleColor forState:UIControlStateNormal]; | |
| 230 | + | |
| 231 | + [_originalPhotoButton addSubview:_originalPhotoLabel]; | |
| 232 | + [_toolBar addSubview:_doneButton]; | |
| 233 | + [_toolBar addSubview:_originalPhotoButton]; | |
| 234 | + [_toolBar addSubview:_numberImageView]; | |
| 235 | + [_toolBar addSubview:_numberLabel]; | |
| 236 | + [_toolBar addSubview:_editTipLable];///LXG | |
| 237 | + [_toolBar addSubview:_editTipButton];///LXG | |
| 238 | + [self.view addSubview:_toolBar]; | |
| 239 | + | |
| 240 | + if (_tzImagePickerVc.photoPreviewPageUIConfigBlock) { | |
| 241 | + _tzImagePickerVc.photoPreviewPageUIConfigBlock(_collectionView, _naviBar, _backButton, _selectButton, _indexLabel, _toolBar, _originalPhotoButton, _originalPhotoLabel, _doneButton, _numberImageView, _numberLabel); | |
| 242 | + } | |
| 243 | +} | |
| 244 | + | |
| 245 | +- (void)configCollectionView { | |
| 246 | + _layout = [[UICollectionViewFlowLayout alloc] init]; | |
| 247 | + _layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; | |
| 248 | + _collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:_layout]; | |
| 249 | + _collectionView.backgroundColor = [UIColor blackColor]; | |
| 250 | + _collectionView.dataSource = self; | |
| 251 | + _collectionView.delegate = self; | |
| 252 | + _collectionView.pagingEnabled = YES; | |
| 253 | + _collectionView.scrollsToTop = NO; | |
| 254 | + _collectionView.showsHorizontalScrollIndicator = NO; | |
| 255 | + _collectionView.contentOffset = CGPointMake(0, 0); | |
| 256 | + _collectionView.contentSize = CGSizeMake(self.models.count * (self.view.tz_width + 20), 0); | |
| 257 | + [self.view addSubview:_collectionView]; | |
| 258 | + [_collectionView registerClass:[TZPhotoPreviewCell class] forCellWithReuseIdentifier:@"TZPhotoPreviewCell"]; | |
| 259 | + [_collectionView registerClass:[TZVideoPreviewCell class] forCellWithReuseIdentifier:@"TZVideoPreviewCell"]; | |
| 260 | + [_collectionView registerClass:[TZGifPreviewCell class] forCellWithReuseIdentifier:@"TZGifPreviewCell"]; | |
| 261 | +} | |
| 262 | + | |
| 263 | +- (void)configCropView { | |
| 264 | + TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 265 | + if (_tzImagePickerVc.maxImagesCount <= 1 && _tzImagePickerVc.allowCrop) { | |
| 266 | + [_cropView removeFromSuperview]; | |
| 267 | + [_cropBgView removeFromSuperview]; | |
| 268 | + | |
| 269 | + _cropBgView = [UIView new]; | |
| 270 | + _cropBgView.userInteractionEnabled = NO; | |
| 271 | + _cropBgView.frame = self.view.bounds; | |
| 272 | + _cropBgView.backgroundColor = [UIColor clearColor]; | |
| 273 | + [self.view addSubview:_cropBgView]; | |
| 274 | + [TZImageCropManager overlayClippingWithView:_cropBgView cropRect:_tzImagePickerVc.cropRect containerView:self.view needCircleCrop:_tzImagePickerVc.needCircleCrop]; | |
| 275 | + | |
| 276 | + _cropView = [UIView new]; | |
| 277 | + _cropView.userInteractionEnabled = NO; | |
| 278 | + _cropView.frame = _tzImagePickerVc.cropRect; | |
| 279 | + _cropView.backgroundColor = [UIColor clearColor]; | |
| 280 | + _cropView.layer.borderColor = [UIColor whiteColor].CGColor; | |
| 281 | + _cropView.layer.borderWidth = 1.0; | |
| 282 | + if (_tzImagePickerVc.needCircleCrop) { | |
| 283 | + _cropView.layer.cornerRadius = _tzImagePickerVc.cropRect.size.width / 2; | |
| 284 | + _cropView.clipsToBounds = YES; | |
| 285 | + } | |
| 286 | + [self.view addSubview:_cropView]; | |
| 287 | + if (_tzImagePickerVc.cropViewSettingBlock) { | |
| 288 | + _tzImagePickerVc.cropViewSettingBlock(_cropView); | |
| 289 | + } | |
| 290 | + | |
| 291 | + [self.view bringSubviewToFront:_naviBar]; | |
| 292 | + [self.view bringSubviewToFront:_tipBar]; | |
| 293 | + [self.view bringSubviewToFront:_toolBar]; | |
| 294 | + } | |
| 295 | +} | |
| 296 | + | |
| 297 | +#pragma mark - Layout | |
| 298 | + | |
| 299 | +- (void)viewDidLayoutSubviews { | |
| 300 | + [super viewDidLayoutSubviews]; | |
| 301 | + TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 302 | + | |
| 303 | + CGFloat statusBarHeight = [TZCommonTools tz_statusBarHeight]; | |
| 304 | + CGFloat statusBarHeightInterval = statusBarHeight - 20; | |
| 305 | + CGFloat naviBarHeight = statusBarHeight + _tzImagePickerVc.navigationBar.tz_height; | |
| 306 | + _naviBar.frame = CGRectMake(0, 0, self.view.tz_width, naviBarHeight); | |
| 307 | + _backButton.frame = CGRectMake(10, 10 + statusBarHeightInterval, 44, 44); | |
| 308 | + _selectButton.frame = CGRectMake(self.view.tz_width - 56, 10 + statusBarHeightInterval, 44, 44); | |
| 309 | + _indexLabel.frame = _selectButton.frame; | |
| 310 | + _navBarLine.frame = CGRectMake(0, _naviBar.tz_height - 0.5, _naviBar.tz_width, 0.5); | |
| 311 | + | |
| 312 | + ///Lxg 提示视图约束 | |
| 313 | + CGFloat tipBarHeight = _tzImagePickerVc.navigationBar.tz_height; | |
| 314 | + _tipBar.frame = CGRectMake(0, _naviBar.tz_bottom, _naviBar.tz_width, tipBarHeight); | |
| 315 | + _tipContentLabel.frame = CGRectMake(12, 0, _tipBar.tz_width - 24, tipBarHeight); | |
| 316 | + | |
| 317 | + _layout.itemSize = CGSizeMake(self.view.tz_width + 20, self.view.tz_height); | |
| 318 | + _layout.minimumInteritemSpacing = 0; | |
| 319 | + _layout.minimumLineSpacing = 0; | |
| 320 | + _collectionView.frame = CGRectMake(-10, 0, self.view.tz_width + 20, self.view.tz_height); | |
| 321 | + [_collectionView setCollectionViewLayout:_layout]; | |
| 322 | + if (_offsetItemCount > 0) { | |
| 323 | + CGFloat offsetX = _offsetItemCount * _layout.itemSize.width; | |
| 324 | + [_collectionView setContentOffset:CGPointMake(offsetX, 0)]; | |
| 325 | + } | |
| 326 | + if (_tzImagePickerVc.allowCrop) { | |
| 327 | + [_collectionView reloadData]; | |
| 328 | + } | |
| 329 | + | |
| 330 | + CGFloat toolBarHeight = [TZCommonTools tz_isIPhoneX] ? 44 + (83 - 49) : 44; | |
| 331 | + CGFloat toolBarTop = self.view.tz_height - toolBarHeight; | |
| 332 | + _toolBar.frame = CGRectMake(0, toolBarTop, self.view.tz_width, toolBarHeight); | |
| 333 | + if (_tzImagePickerVc.allowPickingOriginalPhoto) { | |
| 334 | + CGFloat fullImageWidth = [_tzImagePickerVc.fullImageBtnTitleStr tz_calculateSizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:13]} maxSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)].width; | |
| 335 | + _originalPhotoButton.frame = CGRectMake(0, 0, fullImageWidth + 56, 44); | |
| 336 | + _originalPhotoLabel.frame = CGRectMake(fullImageWidth + 42, 0, 80, 44); | |
| 337 | + } | |
| 338 | + [_doneButton sizeToFit]; | |
| 339 | + _doneButton.frame = CGRectMake(self.view.tz_width - _doneButton.tz_width - 12, 0, _doneButton.tz_width, 44); | |
| 340 | + _numberImageView.frame = CGRectMake(_doneButton.tz_left - 24 - 5, 10, 24, 24); | |
| 341 | + _numberLabel.frame = _numberImageView.frame; | |
| 342 | + ///LXG | |
| 343 | + [_editTipButton sizeToFit]; | |
| 344 | + _editTipButton.frame = CGRectMake(self.view.tz_width - 44 - 12, 0, 44, 44); | |
| 345 | + _editTipLable.frame = CGRectMake(12, 10, self.view.tz_width - 24 - _editTipButton.tz_width, 24); | |
| 346 | + | |
| 347 | + [self configCropView]; | |
| 348 | + | |
| 349 | + if (_tzImagePickerVc.photoPreviewPageDidLayoutSubviewsBlock) { | |
| 350 | + _tzImagePickerVc.photoPreviewPageDidLayoutSubviewsBlock(_collectionView, _naviBar, _backButton, _selectButton, _indexLabel, _toolBar, _originalPhotoButton, _originalPhotoLabel, _doneButton, _numberImageView, _numberLabel); | |
| 351 | + } | |
| 352 | +} | |
| 353 | + | |
| 354 | +#pragma mark - Notification | |
| 355 | + | |
| 356 | +- (void)didChangeStatusBarOrientationNotification:(NSNotification *)noti { | |
| 357 | + _offsetItemCount = _collectionView.contentOffset.x / _layout.itemSize.width; | |
| 358 | +} | |
| 359 | + | |
| 360 | +#pragma mark - Click Event | |
| 361 | + | |
| 362 | +- (void)select:(UIButton *)selectButton { | |
| 363 | + TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 364 | + TZAssetModel *model = _models[_currentIndex]; | |
| 365 | + ///LXG:加入当点击选择时 滑动到另一个页面 选中为当前页面判断是否可选 | |
| 366 | + if (!selectButton.enabled) { | |
| 367 | + return; | |
| 368 | + } | |
| 369 | + | |
| 370 | + if (!selectButton.isSelected) { | |
| 371 | + // 1. select:check if over the maxImagesCount / 选择照片,检查是否超过了最大个数的限制 | |
| 372 | + if (_tzImagePickerVc.selectedModels.count >= _tzImagePickerVc.maxImagesCount) { | |
| 373 | + NSString *title = [NSString stringWithFormat:[NSBundle tz_localizedStringForKey:@"Select a maximum of %zd photos"], _tzImagePickerVc.maxImagesCount]; | |
| 374 | + [_tzImagePickerVc showAlertWithTitle:title]; | |
| 375 | + return; | |
| 376 | + // 2. if not over the maxImagesCount / 如果没有超过最大个数限制 | |
| 377 | + } else { | |
| 378 | + [_tzImagePickerVc addSelectedModel:model]; | |
| 379 | + if (self.photos) { | |
| 380 | + [_tzImagePickerVc.selectedAssets addObject:_assetsTemp[_currentIndex]]; | |
| 381 | + [self.photos addObject:_photosTemp[_currentIndex]]; | |
| 382 | + } | |
| 383 | + if (model.type == TZAssetModelMediaTypeVideo && !_tzImagePickerVc.allowPickingMultipleVideo) { | |
| 384 | + [_tzImagePickerVc showAlertWithTitle:[NSBundle tz_localizedStringForKey:@"Select the video when in multi state, we will handle the video as a photo"]]; | |
| 385 | + } | |
| 386 | + } | |
| 387 | + } else { | |
| 388 | + NSArray *selectedModels = [NSArray arrayWithArray:_tzImagePickerVc.selectedModels]; | |
| 389 | + for (TZAssetModel *model_item in selectedModels) { | |
| 390 | + if ([[[TZImageManager manager] getAssetIdentifier:model.asset] isEqualToString:[[TZImageManager manager] getAssetIdentifier:model_item.asset]]) { | |
| 391 | + // 1.6.7版本更新:防止有多个一样的model,一次性被移除了 | |
| 392 | + NSArray *selectedModelsTmp = [NSArray arrayWithArray:_tzImagePickerVc.selectedModels]; | |
| 393 | + for (NSInteger i = 0; i < selectedModelsTmp.count; i++) { | |
| 394 | + TZAssetModel *model = selectedModelsTmp[i]; | |
| 395 | + if ([model isEqual:model_item]) { | |
| 396 | + [_tzImagePickerVc removeSelectedModel:model]; | |
| 397 | + // [_tzImagePickerVc.selectedModels removeObjectAtIndex:i]; | |
| 398 | + break; | |
| 399 | + } | |
| 400 | + } | |
| 401 | + if (self.photos) { | |
| 402 | + // 1.6.7版本更新:防止有多个一样的asset,一次性被移除了 | |
| 403 | + NSArray *selectedAssetsTmp = [NSArray arrayWithArray:_tzImagePickerVc.selectedAssets]; | |
| 404 | + for (NSInteger i = 0; i < selectedAssetsTmp.count; i++) { | |
| 405 | + id asset = selectedAssetsTmp[i]; | |
| 406 | + if ([asset isEqual:_assetsTemp[_currentIndex]]) { | |
| 407 | + [_tzImagePickerVc.selectedAssets removeObjectAtIndex:i]; | |
| 408 | + break; | |
| 409 | + } | |
| 410 | + } | |
| 411 | + // [_tzImagePickerVc.selectedAssets removeObject:_assetsTemp[_currentIndex]]; | |
| 412 | + [self.photos removeObject:_photosTemp[_currentIndex]]; | |
| 413 | + } | |
| 414 | + break; | |
| 415 | + } | |
| 416 | + } | |
| 417 | + } | |
| 418 | + | |
| 419 | + model.isSelected = !selectButton.isSelected; | |
| 420 | + [self refreshNaviBarAndBottomBarState]; | |
| 421 | + if (model.isSelected) { | |
| 422 | + [UIView showOscillatoryAnimationWithLayer:selectButton.imageView.layer type:TZOscillatoryAnimationToBigger]; | |
| 423 | + } | |
| 424 | + [UIView showOscillatoryAnimationWithLayer:_numberImageView.layer type:TZOscillatoryAnimationToSmaller]; | |
| 425 | +} | |
| 426 | + | |
| 427 | +- (void)backButtonClick { | |
| 428 | + if (self.navigationController.childViewControllers.count < 2) { | |
| 429 | + [self.navigationController dismissViewControllerAnimated:YES completion:nil]; | |
| 430 | + if ([self.navigationController isKindOfClass: [TZImagePickerController class]]) { | |
| 431 | + TZImagePickerController *nav = (TZImagePickerController *)self.navigationController; | |
| 432 | + if (nav.imagePickerControllerDidCancelHandle) { | |
| 433 | + nav.imagePickerControllerDidCancelHandle(); | |
| 434 | + } | |
| 435 | + } | |
| 436 | + return; | |
| 437 | + } | |
| 438 | + [self.navigationController popViewControllerAnimated:YES]; | |
| 439 | + if (self.backButtonClickBlock) { | |
| 440 | + self.backButtonClickBlock(_isSelectOriginalPhoto); | |
| 441 | + } | |
| 442 | +} | |
| 443 | + | |
| 444 | +- (void)doneButtonClick { | |
| 445 | + TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 446 | + // 如果图片正在从iCloud同步中,提醒用户 | |
| 447 | + if (_progress > 0 && _progress < 1 && (_selectButton.isSelected || !_tzImagePickerVc.selectedModels.count )) { | |
| 448 | + _alertView = [_tzImagePickerVc showAlertWithTitle:[NSBundle tz_localizedStringForKey:@"Synchronizing photos from iCloud"]]; | |
| 449 | + return; | |
| 450 | + } | |
| 451 | + | |
| 452 | + // 如果没有选中过照片 点击确定时选中当前预览的照片 | |
| 453 | + if (_tzImagePickerVc.selectedModels.count == 0 && _tzImagePickerVc.minImagesCount <= 0) { | |
| 454 | + TZAssetModel *model = _models[_currentIndex]; | |
| 455 | + [_tzImagePickerVc addSelectedModel:model]; | |
| 456 | + } | |
| 457 | + if (_tzImagePickerVc.allowCrop) { // 裁剪状态 | |
| 458 | + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:_currentIndex inSection:0]; | |
| 459 | + TZPhotoPreviewCell *cell = (TZPhotoPreviewCell *)[_collectionView cellForItemAtIndexPath:indexPath]; | |
| 460 | + UIImage *cropedImage = [TZImageCropManager cropImageView:cell.previewView.imageView toRect:_tzImagePickerVc.cropRect zoomScale:cell.previewView.scrollView.zoomScale containerView:self.view]; | |
| 461 | + if (_tzImagePickerVc.needCircleCrop) { | |
| 462 | + cropedImage = [TZImageCropManager circularClipImage:cropedImage]; | |
| 463 | + } | |
| 464 | + if (self.doneButtonClickBlockCropMode) { | |
| 465 | + TZAssetModel *model = _models[_currentIndex]; | |
| 466 | + self.doneButtonClickBlockCropMode(cropedImage,model.asset); | |
| 467 | + } | |
| 468 | + } else if (self.doneButtonClickBlock) { // 非裁剪状态 | |
| 469 | + self.doneButtonClickBlock(_isSelectOriginalPhoto); | |
| 470 | + } | |
| 471 | + if (self.doneButtonClickBlockWithPreviewType) { | |
| 472 | + self.doneButtonClickBlockWithPreviewType(self.photos,_tzImagePickerVc.selectedAssets,self.isSelectOriginalPhoto); | |
| 473 | + } | |
| 474 | +} | |
| 475 | + | |
| 476 | +///lxg 编辑按钮实现 | |
| 477 | +- (void)editVideoButtonClick{ | |
| 478 | + | |
| 479 | + NSLog(@"开始编辑视频了"); | |
| 480 | + TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 481 | + CNEditVideoController *editVideoVC = [[CNEditVideoController alloc]init]; | |
| 482 | + TZAssetModel *model = _models[_currentIndex]; | |
| 483 | + editVideoVC.model = model; | |
| 484 | + editVideoVC.models = _models; | |
| 485 | + editVideoVC.currentIndex = _currentIndex; | |
| 486 | + editVideoVC.isCropImage = _isCropImage; | |
| 487 | + __weak typeof(self) weakSelf = self; | |
| 488 | + | |
| 489 | + editVideoVC.doneButtonEditClickBlock = ^(BOOL isSelectOriginalPhoto) { | |
| 490 | + __strong typeof(weakSelf) strongSelf = weakSelf; | |
| 491 | + strongSelf.doneButtonClickBlock(isSelectOriginalPhoto); | |
| 492 | + | |
| 493 | + }; | |
| 494 | + | |
| 495 | + [_tzImagePickerVc pushViewController:editVideoVC animated:NO]; | |
| 496 | +} | |
| 497 | + | |
| 498 | +- (void)originalPhotoButtonClick { | |
| 499 | + _originalPhotoButton.selected = !_originalPhotoButton.isSelected; | |
| 500 | + _isSelectOriginalPhoto = _originalPhotoButton.isSelected; | |
| 501 | + _originalPhotoLabel.hidden = !_originalPhotoButton.isSelected; | |
| 502 | + if (_isSelectOriginalPhoto) { | |
| 503 | + [self showPhotoBytes]; | |
| 504 | + if (!_selectButton.isSelected) { | |
| 505 | + // 如果当前已选择照片张数 < 最大可选张数 && 最大可选张数大于1,就选中该张图 | |
| 506 | + TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 507 | + if (_tzImagePickerVc.selectedModels.count < _tzImagePickerVc.maxImagesCount && _tzImagePickerVc.showSelectBtn) { | |
| 508 | + [self select:_selectButton]; | |
| 509 | + } | |
| 510 | + } | |
| 511 | + } | |
| 512 | +} | |
| 513 | + | |
| 514 | +- (void)didTapPreviewCell { | |
| 515 | + self.isHideNaviBar = !self.isHideNaviBar; | |
| 516 | + _naviBar.hidden = self.isHideNaviBar; | |
| 517 | + _toolBar.hidden = self.isHideNaviBar; | |
| 518 | + | |
| 519 | + if (_naviBar.hidden && _toolBar.hidden) { | |
| 520 | + _tipBar.hidden = YES; | |
| 521 | + }else{ | |
| 522 | + [self refreshNaviBarAndBottomBarState]; | |
| 523 | + } | |
| 524 | + | |
| 525 | +} | |
| 526 | + | |
| 527 | +#pragma mark - UIScrollViewDelegate | |
| 528 | + | |
| 529 | +- (void)scrollViewDidScroll:(UIScrollView *)scrollView { | |
| 530 | + CGFloat offSetWidth = scrollView.contentOffset.x; | |
| 531 | + offSetWidth = offSetWidth + ((self.view.tz_width + 20) * 0.5); | |
| 532 | + | |
| 533 | + NSInteger currentIndex = offSetWidth / (self.view.tz_width + 20); | |
| 534 | + | |
| 535 | + if (currentIndex < _models.count && _currentIndex != currentIndex) { | |
| 536 | + _currentIndex = currentIndex; | |
| 537 | + [self refreshNaviBarAndBottomBarState]; | |
| 538 | + } | |
| 539 | + | |
| 540 | + [[NSNotificationCenter defaultCenter] postNotificationName:@"photoPreviewCollectionViewDidScroll" object:nil]; | |
| 541 | +} | |
| 542 | + | |
| 543 | +#pragma mark - UICollectionViewDataSource && Delegate | |
| 544 | + | |
| 545 | +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { | |
| 546 | + return _models.count; | |
| 547 | +} | |
| 548 | + | |
| 549 | +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { | |
| 550 | + TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 551 | + TZAssetModel *model = _models[indexPath.item]; | |
| 552 | + | |
| 553 | + TZAssetPreviewCell *cell; | |
| 554 | + __weak typeof(self) weakSelf = self; | |
| 555 | +#pragma mark - 更改视频,GIF,显示 | |
| 556 | + if (model.type == TZAssetModelMediaTypeVideo) { | |
| 557 | + cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TZVideoPreviewCell" forIndexPath:indexPath]; | |
| 558 | + } else if (model.type == TZAssetModelMediaTypePhotoGif && _tzImagePickerVc.allowPickingGif) { | |
| 559 | + cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TZGifPreviewCell" forIndexPath:indexPath]; | |
| 560 | + } else { | |
| 561 | + cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TZPhotoPreviewCell" forIndexPath:indexPath]; | |
| 562 | + TZPhotoPreviewCell *photoPreviewCell = (TZPhotoPreviewCell *)cell; | |
| 563 | + photoPreviewCell.cropRect = _tzImagePickerVc.cropRect; | |
| 564 | + photoPreviewCell.allowCrop = _tzImagePickerVc.allowCrop; | |
| 565 | + __weak typeof(_tzImagePickerVc) weakTzImagePickerVc = _tzImagePickerVc; | |
| 566 | + __weak typeof(_collectionView) weakCollectionView = _collectionView; | |
| 567 | + __weak typeof(photoPreviewCell) weakCell = photoPreviewCell; | |
| 568 | + [photoPreviewCell setImageProgressUpdateBlock:^(double progress) { | |
| 569 | + __strong typeof(weakSelf) strongSelf = weakSelf; | |
| 570 | + __strong typeof(weakTzImagePickerVc) strongTzImagePickerVc = weakTzImagePickerVc; | |
| 571 | + __strong typeof(weakCollectionView) strongCollectionView = weakCollectionView; | |
| 572 | + __strong typeof(weakCell) strongCell = weakCell; | |
| 573 | + strongSelf.progress = progress; | |
| 574 | + if (progress >= 1) { | |
| 575 | + if (strongSelf.isSelectOriginalPhoto) [strongSelf showPhotoBytes]; | |
| 576 | + if (strongSelf.alertView && [strongCollectionView.visibleCells containsObject:strongCell]) { | |
| 577 | + [strongTzImagePickerVc hideAlertView:strongSelf.alertView]; | |
| 578 | + strongSelf.alertView = nil; | |
| 579 | + [strongSelf doneButtonClick]; | |
| 580 | + } | |
| 581 | + } | |
| 582 | + }]; | |
| 583 | + } | |
| 584 | + | |
| 585 | + cell.model = model; | |
| 586 | + [cell setSingleTapGestureBlock:^{ | |
| 587 | + __strong typeof(weakSelf) strongSelf = weakSelf; | |
| 588 | + [strongSelf didTapPreviewCell]; | |
| 589 | + }]; | |
| 590 | + return cell; | |
| 591 | +} | |
| 592 | + | |
| 593 | +- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath { | |
| 594 | + if ([cell isKindOfClass:[TZPhotoPreviewCell class]]) { | |
| 595 | + [(TZPhotoPreviewCell *)cell recoverSubviews]; | |
| 596 | + } | |
| 597 | +} | |
| 598 | + | |
| 599 | +- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath { | |
| 600 | + if ([cell isKindOfClass:[TZPhotoPreviewCell class]]) { | |
| 601 | + [(TZPhotoPreviewCell *)cell recoverSubviews]; | |
| 602 | + } else if ([cell isKindOfClass:[TZVideoPreviewCell class]]) { | |
| 603 | + [(TZVideoPreviewCell *)cell pausePlayerAndShowNaviBar]; | |
| 604 | + } | |
| 605 | +} | |
| 606 | + | |
| 607 | +#pragma mark - Private Method | |
| 608 | + | |
| 609 | +- (void)dealloc { | |
| 610 | + // NSLog(@"%@ dealloc",NSStringFromClass(self.class)); | |
| 611 | +} | |
| 612 | + | |
| 613 | +- (void)refreshNaviBarAndBottomBarState { | |
| 614 | + | |
| 615 | + TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 616 | + TZAssetModel *model = _models[_currentIndex]; | |
| 617 | + _selectButton.selected = model.isSelected; | |
| 618 | + [self refreshSelectButtonImageViewContentMode]; | |
| 619 | + if (_selectButton.isSelected && _tzImagePickerVc.showSelectedIndex && _tzImagePickerVc.showSelectBtn) { | |
| 620 | + NSString *assetId = [[TZImageManager manager] getAssetIdentifier:model.asset]; | |
| 621 | + NSString *index = [NSString stringWithFormat:@"%zd", [_tzImagePickerVc.selectedAssetIds indexOfObject:assetId] + 1]; | |
| 622 | + _indexLabel.text = index; | |
| 623 | + _indexLabel.hidden = NO; | |
| 624 | + } else { | |
| 625 | + _indexLabel.hidden = YES; | |
| 626 | + } | |
| 627 | + _numberLabel.text = [NSString stringWithFormat:@"%zd",_tzImagePickerVc.selectedModels.count]; | |
| 628 | + _numberImageView.hidden = (_tzImagePickerVc.selectedModels.count <= 0 || _isHideNaviBar || _isCropImage); | |
| 629 | + _numberLabel.hidden = (_tzImagePickerVc.selectedModels.count <= 0 || _isHideNaviBar || _isCropImage); | |
| 630 | +#pragma mark - 添加: 选择图片个数显示(LXG) | |
| 631 | + //隐藏图标 | |
| 632 | + _numberLabel.hidden = YES; | |
| 633 | + _numberImageView.hidden = YES; | |
| 634 | + NSString *titleStr = _tzImagePickerVc.selectedModels.count > 0 ? [NSString stringWithFormat:@"完成(%zd)",_tzImagePickerVc.selectedModels.count] : @"完成"; | |
| 635 | + [_doneButton setTitle:titleStr forState:UIControlStateNormal]; | |
| 636 | + _originalPhotoButton.selected = _isSelectOriginalPhoto; | |
| 637 | + _originalPhotoLabel.hidden = !_originalPhotoButton.isSelected; | |
| 638 | + if (_isSelectOriginalPhoto) [self showPhotoBytes]; | |
| 639 | + | |
| 640 | + // If is previewing video, hide original photo button | |
| 641 | + // 如果正在预览的是视频,隐藏原图按钮 | |
| 642 | + if (!_isHideNaviBar) { | |
| 643 | + if (model.type == TZAssetModelMediaTypeVideo) { | |
| 644 | + _originalPhotoButton.hidden = YES; | |
| 645 | + _originalPhotoLabel.hidden = YES; | |
| 646 | + } else { | |
| 647 | + _originalPhotoButton.hidden = NO; | |
| 648 | + if (_isSelectOriginalPhoto) _originalPhotoLabel.hidden = NO; | |
| 649 | + } | |
| 650 | + } | |
| 651 | + | |
| 652 | + _doneButton.hidden = NO; | |
| 653 | + _selectButton.hidden = !_tzImagePickerVc.showSelectBtn; | |
| 654 | + // 让宽度/高度小于 最小可选照片尺寸 的图片不能选中 | |
| 655 | + if (![[TZImageManager manager] isPhotoSelectableWithAsset:model.asset]) { | |
| 656 | + _numberLabel.hidden = YES; | |
| 657 | + _numberImageView.hidden = YES; | |
| 658 | + _selectButton.hidden = YES; | |
| 659 | + _originalPhotoButton.hidden = YES; | |
| 660 | + _originalPhotoLabel.hidden = YES; | |
| 661 | + _doneButton.hidden = YES; | |
| 662 | + } | |
| 663 | + | |
| 664 | +#pragma mark - 添加: 视频显示操作(LXG) | |
| 665 | + //多选状态下**不允许**图片和视频混选 | |
| 666 | + if (!_tzImagePickerVc.allowPickingMultipleVideo) { | |
| 667 | + | |
| 668 | + if (model.type == TZAssetModelMediaTypeVideo) {//多选状态下**不允许**图片和视频混选 -> 视频 | |
| 669 | + | |
| 670 | + TZImagePickerController *_tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 671 | + if (_tzImagePickerVc.selectedModels.count > 0) {//当选择图片大于0时 | |
| 672 | + TZAssetModel *model = _tzImagePickerVc.selectedModels[0]; | |
| 673 | + if (model.type == TZAssetModelMediaTypePhoto | |
| 674 | + || model.type == TZAssetModelMediaTypePhotoGif | |
| 675 | + ||model.type == TZAssetModelMediaTypeLivePhoto) {//选择为图片时 | |
| 676 | + | |
| 677 | + _selectButton.hidden = YES; | |
| 678 | + _selectButton.enabled = NO; | |
| 679 | + _navBarLine.hidden = NO; | |
| 680 | + _tipBar.hidden = NO; | |
| 681 | + _tipContentLabel.text = @"选择照片时不能选择视频"; | |
| 682 | + _doneButton.hidden = NO; | |
| 683 | + _doneButton.enabled = YES; | |
| 684 | + _editTipButton.hidden = YES; | |
| 685 | + _editTipLable.hidden = YES; | |
| 686 | + | |
| 687 | + }else{//未来可能是语音 | |
| 688 | + | |
| 689 | + _selectButton.hidden = YES; | |
| 690 | + _selectButton.enabled = NO; | |
| 691 | + _navBarLine.hidden = NO; | |
| 692 | + _tipBar.hidden = NO; | |
| 693 | + _tipContentLabel.text = @"选择照片时不能选择音频"; | |
| 694 | + _doneButton.hidden = NO; | |
| 695 | + _doneButton.enabled = YES; | |
| 696 | + _editTipButton.hidden = YES; | |
| 697 | + _editTipLable.hidden = YES; | |
| 698 | + | |
| 699 | + } | |
| 700 | + }else{//当选择图片==0时 | |
| 701 | + | |
| 702 | + NSTimeInterval duration = 0.0; | |
| 703 | + if ([model.asset isKindOfClass:[PHAsset class]]) { | |
| 704 | + PHAsset *asset = model.asset; | |
| 705 | + duration = asset.duration; | |
| 706 | + }else if ([model.asset isKindOfClass:[ALAsset class]]){ | |
| 707 | + duration = [[model.asset valueForProperty:ALAssetPropertyDuration] doubleValue]; | |
| 708 | + } | |
| 709 | + | |
| 710 | + if (duration > _tzImagePickerVc.maxVideoDuration) {//视频长度>最大限制长度 | |
| 711 | + | |
| 712 | + NSInteger minutes = _tzImagePickerVc.maxVideoDuration / 60; | |
| 713 | + NSInteger seconds = _tzImagePickerVc.maxVideoDuration % 60; | |
| 714 | + NSString *timeStr = [NSString string]; | |
| 715 | + if (minutes == 0) { | |
| 716 | + timeStr = [NSString stringWithFormat:@"不能分享超过%li秒视频",(long)seconds]; | |
| 717 | + }else if (seconds == 0){ | |
| 718 | + timeStr = [NSString stringWithFormat:@"不能分享超过%li分视频",(long)minutes]; | |
| 719 | + }else{ | |
| 720 | + timeStr = [NSString stringWithFormat:@"不能分享超过%li分%li秒视频",(long)minutes,(long)seconds]; | |
| 721 | + } | |
| 722 | + _tipContentLabel.text = timeStr; | |
| 723 | + | |
| 724 | + //当选择图片==0时,视频长度>最大限制长度 | |
| 725 | + _selectButton.hidden = YES; | |
| 726 | + _selectButton.enabled = NO; | |
| 727 | + _navBarLine.hidden = NO; | |
| 728 | + _tipBar.hidden = NO; | |
| 729 | + _tipContentLabel.text = timeStr; | |
| 730 | + _doneButton.hidden = NO; | |
| 731 | + if (_tzImagePickerVc.selectedModels.count > 0) { | |
| 732 | + _doneButton.enabled = YES; | |
| 733 | + }else{ | |
| 734 | + _doneButton.enabled = NO; | |
| 735 | + } | |
| 736 | + _editTipButton.hidden = YES; | |
| 737 | + _editTipLable.hidden = YES; | |
| 738 | + | |
| 739 | + }else if(duration <= _tzImagePickerVc.maxVideoDuration && duration > _tzImagePickerVc.maxEditVideoTime){//视频长度<=最大限制长度,视频长度>=最小限制长度 | |
| 740 | + | |
| 741 | + NSInteger minutes = _tzImagePickerVc.maxEditVideoTime / 60; | |
| 742 | + NSInteger seconds = _tzImagePickerVc.maxEditVideoTime % 60; | |
| 743 | + NSString *timeStr = [NSString string]; | |
| 744 | + if (minutes == 0) { | |
| 745 | + timeStr = [NSString stringWithFormat:@"朋友圈只能分享%li秒限制的视频,需进行编辑",(long)seconds]; | |
| 746 | + }else if (seconds == 0){ | |
| 747 | + timeStr = [NSString stringWithFormat:@"朋友圈只能分享%li分限制的视频,需进行编辑",(long)minutes]; | |
| 748 | + }else{ | |
| 749 | + timeStr = [NSString stringWithFormat:@"朋友圈只能分享%li分%li秒限制的视频,需进行编辑",(long)minutes,(long)seconds]; | |
| 750 | + } | |
| 751 | + _editTipLable.text = timeStr; | |
| 752 | + //当选择图片==0时,视频长度<=最大限制长度,视频长度>最大分享长度 | |
| 753 | + _selectButton.hidden = YES; | |
| 754 | + _selectButton.enabled = NO; | |
| 755 | + _navBarLine.hidden = YES; | |
| 756 | + _tipBar.hidden = YES; | |
| 757 | + _tipContentLabel.text = timeStr; | |
| 758 | + _doneButton.hidden = YES; | |
| 759 | + _doneButton.enabled = YES; | |
| 760 | + _editTipButton.hidden = NO; | |
| 761 | + _editTipLable.hidden = NO; | |
| 762 | + | |
| 763 | + }else{//视频长度<=最大分享长度 | |
| 764 | + //当选择图片==0时,视频长度<=最大分享长度 | |
| 765 | + _selectButton.hidden = YES; | |
| 766 | + _selectButton.enabled = NO; | |
| 767 | + _navBarLine.hidden = YES; | |
| 768 | + _tipBar.hidden = YES; | |
| 769 | + _doneButton.hidden = NO; | |
| 770 | + _doneButton.enabled = YES; | |
| 771 | + _editTipButton.hidden = YES; | |
| 772 | + _editTipLable.hidden = YES; | |
| 773 | + } | |
| 774 | + } | |
| 775 | + | |
| 776 | + } else {//滑动到不是视频时 | |
| 777 | + //滑动到不是视频时 | |
| 778 | + _selectButton.hidden = NO; | |
| 779 | + _selectButton.enabled = YES; | |
| 780 | + _navBarLine.hidden = YES; | |
| 781 | + _tipBar.hidden = YES; | |
| 782 | + _doneButton.hidden = NO; | |
| 783 | + _doneButton.enabled = YES; | |
| 784 | + _editTipButton.hidden = YES; | |
| 785 | + _editTipLable.hidden = YES; | |
| 786 | + | |
| 787 | + } | |
| 788 | + }else{///多选状态下**允许**图片和视频混选(lxg) | |
| 789 | + NSTimeInterval duration = 0.0; | |
| 790 | + if ([model.asset isKindOfClass:[PHAsset class]]) { | |
| 791 | + PHAsset *asset = model.asset; | |
| 792 | + duration = asset.duration; | |
| 793 | + }else if([model.asset isKindOfClass:[ALAsset class]]) { | |
| 794 | + duration = [[model.asset valueForProperty:ALAssetPropertyDuration] doubleValue]; | |
| 795 | + } | |
| 796 | + if (model.type == TZAssetModelMediaTypeVideo | |
| 797 | + && duration > _tzImagePickerVc.maxVideoDuration) {//视频,支持混选,时长超过最大时长限制 | |
| 798 | + NSInteger minutes = _tzImagePickerVc.maxVideoDuration / 60; | |
| 799 | + NSInteger seconds = _tzImagePickerVc.maxVideoDuration % 60; | |
| 800 | + NSString *timeStr = [NSString string]; | |
| 801 | + if (minutes == 0) { | |
| 802 | + timeStr = [NSString stringWithFormat:@"不能选择超过%li秒视频",(long)seconds]; | |
| 803 | + }else if (seconds == 0){ | |
| 804 | + timeStr = [NSString stringWithFormat:@"不能选择超过%li分视频",(long)minutes]; | |
| 805 | + }else{ | |
| 806 | + timeStr = [NSString stringWithFormat:@"不能选择超过%li分%li秒视频",(long)minutes,(long)seconds]; | |
| 807 | + } | |
| 808 | + | |
| 809 | + _selectButton.hidden = YES; | |
| 810 | + _selectButton.enabled = NO; | |
| 811 | + _selectButton.enabled = NO; | |
| 812 | + _navBarLine.hidden = NO; | |
| 813 | + _tipBar.hidden = NO; | |
| 814 | + _tipContentLabel.text = timeStr; | |
| 815 | + _doneButton.hidden = NO; | |
| 816 | + if (_tzImagePickerVc.selectedModels.count > 0) { | |
| 817 | + _doneButton.enabled = YES; | |
| 818 | + }else{ | |
| 819 | + _doneButton.enabled = NO; | |
| 820 | + } | |
| 821 | + _editTipButton.hidden = YES; | |
| 822 | + _editTipLable.hidden = YES; | |
| 823 | + | |
| 824 | + | |
| 825 | + }else{///多选状态下**允许**图片和视频混选(lxg) - 不是视频 || 视频<= _tzImagePickerVc.maxVideoDuration 最大时长限制 | |
| 826 | + | |
| 827 | + _selectButton.hidden = NO; | |
| 828 | + _selectButton.enabled = YES; | |
| 829 | + _navBarLine.hidden = YES; | |
| 830 | + _tipBar.hidden = YES; | |
| 831 | + _doneButton.hidden = NO; | |
| 832 | + _doneButton.enabled = YES; | |
| 833 | + _editTipButton.hidden = YES; | |
| 834 | + _editTipLable.hidden = YES; | |
| 835 | + | |
| 836 | + } | |
| 837 | + } | |
| 838 | + | |
| 839 | +} | |
| 840 | + | |
| 841 | +- (void)refreshSelectButtonImageViewContentMode { | |
| 842 | + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ | |
| 843 | + if (self->_selectButton.imageView.image.size.width <= 27) { | |
| 844 | + self->_selectButton.imageView.contentMode = UIViewContentModeCenter; | |
| 845 | + } else { | |
| 846 | + self->_selectButton.imageView.contentMode = UIViewContentModeScaleAspectFit; | |
| 847 | + } | |
| 848 | + }); | |
| 849 | +} | |
| 850 | + | |
| 851 | +- (void)showPhotoBytes { | |
| 852 | + [[TZImageManager manager] getPhotosBytesWithArray:@[_models[_currentIndex]] completion:^(NSString *totalBytes) { | |
| 853 | + self->_originalPhotoLabel.text = [NSString stringWithFormat:@"(%@)",totalBytes]; | |
| 854 | + }]; | |
| 855 | +} | |
| 856 | + | |
| 857 | +@end | ... | ... |
CNLiveImagePickerController/TZProgressView.h
0 → 100755
| 1 | +// | |
| 2 | +// TZProgressView.h | |
| 3 | +// TZImagePickerController | |
| 4 | +// | |
| 5 | +// Created by ttouch on 2016/12/6. | |
| 6 | +// Copyright © 2016年 谭真. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import <UIKit/UIKit.h> | |
| 10 | + | |
| 11 | +@interface TZProgressView : UIView | |
| 12 | + | |
| 13 | +@property (nonatomic, assign) double progress; | |
| 14 | + | |
| 15 | +@end | ... | ... |
CNLiveImagePickerController/TZProgressView.m
0 → 100755
| 1 | +// | |
| 2 | +// TZProgressView.m | |
| 3 | +// TZImagePickerController | |
| 4 | +// | |
| 5 | +// Created by ttouch on 2016/12/6. | |
| 6 | +// Copyright © 2016年 谭真. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "TZProgressView.h" | |
| 10 | + | |
| 11 | +@interface TZProgressView () | |
| 12 | +@property (nonatomic, strong) CAShapeLayer *progressLayer; | |
| 13 | +@end | |
| 14 | + | |
| 15 | +@implementation TZProgressView | |
| 16 | + | |
| 17 | +- (instancetype)init { | |
| 18 | + self = [super init]; | |
| 19 | + if (self) { | |
| 20 | + self.backgroundColor = [UIColor clearColor]; | |
| 21 | + | |
| 22 | + _progressLayer = [CAShapeLayer layer]; | |
| 23 | + _progressLayer.fillColor = [[UIColor clearColor] CGColor]; | |
| 24 | + _progressLayer.strokeColor = [[UIColor whiteColor] CGColor]; | |
| 25 | + _progressLayer.opacity = 1; | |
| 26 | + _progressLayer.lineCap = kCALineCapRound; | |
| 27 | + _progressLayer.lineWidth = 5; | |
| 28 | + | |
| 29 | + [_progressLayer setShadowColor:[UIColor blackColor].CGColor]; | |
| 30 | + [_progressLayer setShadowOffset:CGSizeMake(1, 1)]; | |
| 31 | + [_progressLayer setShadowOpacity:0.5]; | |
| 32 | + [_progressLayer setShadowRadius:2]; | |
| 33 | + } | |
| 34 | + return self; | |
| 35 | +} | |
| 36 | + | |
| 37 | +- (void)drawRect:(CGRect)rect { | |
| 38 | + CGPoint center = CGPointMake(rect.size.width / 2, rect.size.height / 2); | |
| 39 | + CGFloat radius = rect.size.width / 2; | |
| 40 | + CGFloat startA = - M_PI_2; | |
| 41 | + CGFloat endA = - M_PI_2 + M_PI * 2 * _progress; | |
| 42 | + _progressLayer.frame = self.bounds; | |
| 43 | + UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startA endAngle:endA clockwise:YES]; | |
| 44 | + _progressLayer.path =[path CGPath]; | |
| 45 | + | |
| 46 | + [_progressLayer removeFromSuperlayer]; | |
| 47 | + [self.layer addSublayer:_progressLayer]; | |
| 48 | +} | |
| 49 | + | |
| 50 | +- (void)setProgress:(double)progress { | |
| 51 | + _progress = progress; | |
| 52 | + [self setNeedsDisplay]; | |
| 53 | +} | |
| 54 | + | |
| 55 | +@end | ... | ... |
CNLiveImagePickerController/TZVideoPlayerController.h
0 → 100755
| 1 | +// | |
| 2 | +// TZVideoPlayerController.h | |
| 3 | +// TZImagePickerController | |
| 4 | +// | |
| 5 | +// Created by 谭真 on 16/1/5. | |
| 6 | +// Copyright © 2016年 谭真. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import <UIKit/UIKit.h> | |
| 10 | + | |
| 11 | +@class TZAssetModel; | |
| 12 | +@interface TZVideoPlayerController : UIViewController | |
| 13 | + | |
| 14 | +@property (nonatomic, strong) TZAssetModel *model; | |
| 15 | +///其他进入和相册进入(lxg) | |
| 16 | +@property (nonatomic, assign) BOOL isOther; | |
| 17 | + | |
| 18 | +@end | ... | ... |
CNLiveImagePickerController/TZVideoPlayerController.m
0 → 100755
| 1 | +// | |
| 2 | +// TZVideoPlayerController.m | |
| 3 | +// TZImagePickerController | |
| 4 | +// | |
| 5 | +// Created by 谭真 on 16/1/5. | |
| 6 | +// Copyright © 2016年 谭真. All rights reserved. | |
| 7 | +// | |
| 8 | + | |
| 9 | +#import "TZVideoPlayerController.h" | |
| 10 | +#import <MediaPlayer/MediaPlayer.h> | |
| 11 | +#import "UIView+Layout.h" | |
| 12 | +#import "TZImageManager.h" | |
| 13 | +#import "TZAssetModel.h" | |
| 14 | +#import "TZImagePickerController.h" | |
| 15 | +#import "TZPhotoPreviewController.h" | |
| 16 | + | |
| 17 | +@interface TZVideoPlayerController () { | |
| 18 | + AVPlayer *_player; | |
| 19 | + AVPlayerLayer *_playerLayer; | |
| 20 | + UIButton *_playButton; | |
| 21 | + UIImage *_cover; | |
| 22 | + ///LXG nav属性 | |
| 23 | + UIView *_naviBar; | |
| 24 | + UIButton *_backButton; | |
| 25 | + | |
| 26 | + UIView *_toolBar; | |
| 27 | + UIButton *_doneButton; | |
| 28 | + UIProgressView *_progress; | |
| 29 | + | |
| 30 | + UIStatusBarStyle _originStatusBarStyle; | |
| 31 | +} | |
| 32 | +@property (assign, nonatomic) BOOL needShowStatusBar; | |
| 33 | +@end | |
| 34 | + | |
| 35 | +#pragma clang diagnostic push | |
| 36 | +#pragma clang diagnostic ignored "-Wdeprecated-declarations" | |
| 37 | + | |
| 38 | +@implementation TZVideoPlayerController | |
| 39 | + | |
| 40 | +- (void)viewDidLoad { | |
| 41 | + [super viewDidLoad]; | |
| 42 | + ///LXG 注释 | |
| 43 | +// self.needShowStatusBar = ![UIApplication sharedApplication].statusBarHidden; | |
| 44 | + self.view.backgroundColor = [UIColor blackColor]; | |
| 45 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 46 | + if (tzImagePickerVc) { | |
| 47 | + self.navigationItem.title = tzImagePickerVc.previewBtnTitleStr; | |
| 48 | + } | |
| 49 | + [self configMoviePlayer]; | |
| 50 | + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pausePlayerAndShowNaviBar) name:UIApplicationWillResignActiveNotification object:nil]; | |
| 51 | +} | |
| 52 | +#pragma mark - 添加: 导航栏(LXG) | |
| 53 | +- (void)viewWillAppear:(BOOL)animated { | |
| 54 | + [super viewWillAppear:animated]; | |
| 55 | + _originStatusBarStyle = [UIApplication sharedApplication].statusBarStyle; | |
| 56 | + ///LXG 导航栏隐藏 | |
| 57 | + [UIApplication sharedApplication].statusBarStyle = iOS7Later ? UIStatusBarStyleLightContent : UIStatusBarStyleBlackOpaque; | |
| 58 | + [self.navigationController setNavigationBarHidden:YES animated:YES]; | |
| 59 | + if (iOS7Later) [UIApplication sharedApplication].statusBarHidden = YES; | |
| 60 | +} | |
| 61 | + | |
| 62 | +- (void)viewWillDisappear:(BOOL)animated { | |
| 63 | + [super viewWillDisappear:animated]; | |
| 64 | + ///LXG 导航栏显示 | |
| 65 | + [UIApplication sharedApplication].statusBarStyle = _originStatusBarStyle; | |
| 66 | + [self.navigationController setNavigationBarHidden:NO animated:YES]; | |
| 67 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 68 | + if (tzImagePickerVc.needShowStatusBar && iOS7Later) { | |
| 69 | + [UIApplication sharedApplication].statusBarHidden = NO; | |
| 70 | + } | |
| 71 | + [TZImageManager manager].shouldFixOrientation = NO; | |
| 72 | +} | |
| 73 | +///LXG iOS7之后使用 | |
| 74 | +- (BOOL)prefersStatusBarHidden { | |
| 75 | + return YES; | |
| 76 | +} | |
| 77 | + | |
| 78 | +- (void)configMoviePlayer { | |
| 79 | + [[TZImageManager manager] getPhotoWithAsset:_model.asset completion:^(UIImage *photo, NSDictionary *info, BOOL isDegraded) { | |
| 80 | + if (!isDegraded && photo) { | |
| 81 | + self->_cover = photo; | |
| 82 | + self->_doneButton.enabled = YES; | |
| 83 | + } | |
| 84 | + }]; | |
| 85 | + [[TZImageManager manager] getVideoWithAsset:_model.asset completion:^(AVPlayerItem *playerItem, NSDictionary *info) { | |
| 86 | + dispatch_async(dispatch_get_main_queue(), ^{ | |
| 87 | + self->_player = [AVPlayer playerWithPlayerItem:playerItem]; | |
| 88 | + self->_playerLayer = [AVPlayerLayer playerLayerWithPlayer:self->_player]; | |
| 89 | + self->_playerLayer.frame = self.view.bounds; | |
| 90 | + [self.view.layer addSublayer:self->_playerLayer]; | |
| 91 | + [self addProgressObserver]; | |
| 92 | + [self configCustomNaviBar]; | |
| 93 | + [self configPlayButton]; | |
| 94 | + [self configBottomToolBar]; | |
| 95 | + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pausePlayerAndShowNaviBar) name:AVPlayerItemDidPlayToEndTimeNotification object:self->_player.currentItem]; | |
| 96 | + }); | |
| 97 | + }]; | |
| 98 | +} | |
| 99 | + | |
| 100 | +/// Show progress,do it next time / 给播放器添加进度更新,下次加上 | |
| 101 | +- (void)addProgressObserver{ | |
| 102 | + AVPlayerItem *playerItem = _player.currentItem; | |
| 103 | + UIProgressView *progress = _progress; | |
| 104 | + [_player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) { | |
| 105 | + float current = CMTimeGetSeconds(time); | |
| 106 | + float total = CMTimeGetSeconds([playerItem duration]); | |
| 107 | + if (current) { | |
| 108 | + [progress setProgress:(current/total) animated:YES]; | |
| 109 | + } | |
| 110 | + }]; | |
| 111 | +} | |
| 112 | + | |
| 113 | +- (void)configPlayButton { | |
| 114 | + _playButton = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 115 | + [_playButton setImage:[UIImage imageNamedFromMyBundle:@"MMVideoPreviewPlay"] forState:UIControlStateNormal]; | |
| 116 | + [_playButton setImage:[UIImage imageNamedFromMyBundle:@"MMVideoPreviewPlayHL"] forState:UIControlStateHighlighted]; | |
| 117 | + [_playButton addTarget:self action:@selector(playButtonClick) forControlEvents:UIControlEventTouchUpInside]; | |
| 118 | + [self.view addSubview:_playButton]; | |
| 119 | +} | |
| 120 | +///XLG 自定义NAV | |
| 121 | +- (void)configCustomNaviBar { | |
| 122 | + | |
| 123 | + _naviBar = [[UIView alloc] initWithFrame:CGRectZero]; | |
| 124 | + _naviBar.backgroundColor = [UIColor colorWithRed:(34/255.0) green:(34/255.0) blue:(34/255.0) alpha:0.7]; | |
| 125 | + | |
| 126 | + _backButton = [[UIButton alloc] initWithFrame:CGRectZero]; | |
| 127 | + [_backButton setImage:[UIImage imageNamedFromMyBundle:@"navi_back"] forState:UIControlStateNormal]; | |
| 128 | + [_backButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; | |
| 129 | + [_backButton addTarget:self action:@selector(backButtonClick) forControlEvents:UIControlEventTouchUpInside]; | |
| 130 | + | |
| 131 | + [_naviBar addSubview:_backButton]; | |
| 132 | + [self.view addSubview:_naviBar]; | |
| 133 | +} | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | +- (void)configBottomToolBar { | |
| 138 | + _toolBar = [[UIView alloc] initWithFrame:CGRectZero]; | |
| 139 | + CGFloat rgb = 34 / 255.0; | |
| 140 | + _toolBar.backgroundColor = [UIColor colorWithRed:rgb green:rgb blue:rgb alpha:0.7]; | |
| 141 | + | |
| 142 | + _doneButton = [UIButton buttonWithType:UIButtonTypeCustom]; | |
| 143 | + _doneButton.titleLabel.font = [UIFont systemFontOfSize:16]; | |
| 144 | + if (!_cover) { | |
| 145 | + _doneButton.enabled = NO; | |
| 146 | + } | |
| 147 | + [_doneButton addTarget:self action:@selector(doneButtonClick) forControlEvents:UIControlEventTouchUpInside]; | |
| 148 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 149 | + if (tzImagePickerVc) { | |
| 150 | + [_doneButton setTitle:tzImagePickerVc.doneBtnTitleStr forState:UIControlStateNormal]; | |
| 151 | + [_doneButton setTitleColor:tzImagePickerVc.oKButtonTitleColorNormal forState:UIControlStateNormal]; | |
| 152 | + } else { | |
| 153 | + [_doneButton setTitle:[NSBundle tz_localizedStringForKey:@"Done"] forState:UIControlStateNormal]; | |
| 154 | + [_doneButton setTitleColor:[UIColor colorWithRed:(83/255.0) green:(179/255.0) blue:(17/255.0) alpha:1.0] forState:UIControlStateNormal]; | |
| 155 | + } | |
| 156 | + [_doneButton setTitleColor:tzImagePickerVc.oKButtonTitleColorDisabled forState:UIControlStateDisabled]; | |
| 157 | + [_toolBar addSubview:_doneButton]; | |
| 158 | + [self.view addSubview:_toolBar]; | |
| 159 | + | |
| 160 | + if (tzImagePickerVc.videoPreviewPageUIConfigBlock) { | |
| 161 | + tzImagePickerVc.videoPreviewPageUIConfigBlock(_playButton, _toolBar, _doneButton); | |
| 162 | + } | |
| 163 | +} | |
| 164 | + | |
| 165 | +#pragma mark - Layout | |
| 166 | + | |
| 167 | +- (void)viewDidLayoutSubviews { | |
| 168 | + [super viewDidLayoutSubviews]; | |
| 169 | + | |
| 170 | + CGFloat statusBarHeight = [TZCommonTools tz_statusBarHeight]; | |
| 171 | + CGFloat statusBarAndNaviBarHeight = statusBarHeight + self.navigationController.navigationBar.tz_height; | |
| 172 | + _playerLayer.frame = self.view.bounds; | |
| 173 | + CGFloat toolBarHeight = [TZCommonTools tz_isIPhoneX] ? 44 + (83 - 49) : 44; | |
| 174 | + _toolBar.frame = CGRectMake(0, self.view.tz_height - toolBarHeight, self.view.tz_width, toolBarHeight); | |
| 175 | + _doneButton.frame = CGRectMake(self.view.tz_width - 44 - 12, 0, 44, 44); | |
| 176 | + _playButton.frame = CGRectMake(0, statusBarAndNaviBarHeight, self.view.tz_width, self.view.tz_height - statusBarAndNaviBarHeight - toolBarHeight); | |
| 177 | + | |
| 178 | + TZImagePickerController *tzImagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 179 | + ///LXG NAV布局 | |
| 180 | + if (_isOther) { | |
| 181 | + CGFloat statusBarHeightInterval = statusBarHeight - 20; | |
| 182 | + CGFloat naviBarHeight = statusBarHeight + 44; | |
| 183 | + _naviBar.frame = CGRectMake(0, 0, self.view.tz_width, naviBarHeight); | |
| 184 | + _backButton.frame = CGRectMake(10, 10 + statusBarHeightInterval, 44, 44); | |
| 185 | + //完成按钮 | |
| 186 | + _doneButton.titleLabel.font = [UIFont systemFontOfSize:13]; | |
| 187 | +// [_doneButton setTitle:@"完成" forState:UIControlStateDisabled]; | |
| 188 | +// [_doneButton setTitleColor:RGBOF(0xFFFFFF) forState:UIControlStateDisabled]; | |
| 189 | +// [_doneButton setTitleColor:RGBOF(0xFFFFFF) forState:UIControlStateNormal]; | |
| 190 | +// | |
| 191 | +// //颜色绘制成图片 | |
| 192 | +// UIImage *dis_image = [UIImage qmui_imageWithColor:RGBOF(0x0BBE06, 0.5)]; | |
| 193 | +// [_doneButton setBackgroundImage:dis_image forState:UIControlStateSelected]; | |
| 194 | +// UIImage *nor_image = [UIImage qmui_imageWithColor:RGBOF(0x0BBE06)]; | |
| 195 | +// [_doneButton setBackgroundImage:nor_image forState:UIControlStateNormal]; | |
| 196 | + _doneButton.layer.masksToBounds = YES; | |
| 197 | + _doneButton.layer.cornerRadius = 5; | |
| 198 | + CGRect frame = _toolBar.frame; | |
| 199 | + _doneButton.frame = CGRectMake(frame.size.width - 60 - 13, frame.size.height /2 - 15, 60, 30); | |
| 200 | + | |
| 201 | + }else{ | |
| 202 | + CGFloat statusBarHeightInterval = statusBarHeight - 20; | |
| 203 | + CGFloat naviBarHeight = statusBarHeight + tzImagePickerVc.navigationBar.tz_height; | |
| 204 | + _naviBar.frame = CGRectMake(0, 0, self.view.tz_width, naviBarHeight); | |
| 205 | + _backButton.frame = CGRectMake(10, 10 + statusBarHeightInterval, 44, 44); | |
| 206 | + } | |
| 207 | + [self.view bringSubviewToFront:_naviBar]; | |
| 208 | + if (tzImagePickerVc.videoPreviewPageDidLayoutSubviewsBlock) { | |
| 209 | + tzImagePickerVc.videoPreviewPageDidLayoutSubviewsBlock(_playButton, _toolBar, _doneButton); | |
| 210 | + } | |
| 211 | +} | |
| 212 | + | |
| 213 | +#pragma mark - Click Event | |
| 214 | + | |
| 215 | +- (void)playButtonClick { | |
| 216 | + CMTime currentTime = _player.currentItem.currentTime; | |
| 217 | + CMTime durationTime = _player.currentItem.duration; | |
| 218 | + if (_player.rate == 0.0f) { | |
| 219 | + if (currentTime.value == durationTime.value) [_player.currentItem seekToTime:CMTimeMake(0, 1)]; | |
| 220 | + [_player play]; | |
| 221 | + [self.navigationController setNavigationBarHidden:YES]; | |
| 222 | + _toolBar.hidden = YES; | |
| 223 | + ///LXG 隐藏NAV | |
| 224 | + _naviBar.hidden = YES; | |
| 225 | + [_playButton setImage:nil forState:UIControlStateNormal]; | |
| 226 | + if (iOS7Later) [UIApplication sharedApplication].statusBarHidden = YES; | |
| 227 | + } else { | |
| 228 | + [self pausePlayerAndShowNaviBar]; | |
| 229 | + } | |
| 230 | +} | |
| 231 | + | |
| 232 | +- (void)doneButtonClick { | |
| 233 | + if (self.navigationController) { | |
| 234 | + TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 235 | + if (imagePickerVc.autoDismiss) { | |
| 236 | + [self.navigationController dismissViewControllerAnimated:YES completion:^{ | |
| 237 | + [self callDelegateMethod]; | |
| 238 | + }]; | |
| 239 | + } else { | |
| 240 | + [self callDelegateMethod]; | |
| 241 | + } | |
| 242 | + } else { | |
| 243 | + [self dismissViewControllerAnimated:YES completion:^{ | |
| 244 | + [self callDelegateMethod]; | |
| 245 | + }]; | |
| 246 | + } | |
| 247 | +} | |
| 248 | + | |
| 249 | +- (void)callDelegateMethod { | |
| 250 | + TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 251 | + if ([imagePickerVc.pickerDelegate respondsToSelector:@selector(imagePickerController:didFinishPickingVideo:sourceAssets:)]) { | |
| 252 | + [imagePickerVc.pickerDelegate imagePickerController:imagePickerVc didFinishPickingVideo:_cover sourceAssets:_model.asset]; | |
| 253 | + } | |
| 254 | + if (imagePickerVc.didFinishPickingVideoHandle) { | |
| 255 | + imagePickerVc.didFinishPickingVideoHandle(_cover,_model.asset); | |
| 256 | + } | |
| 257 | +} | |
| 258 | + | |
| 259 | +///LXG 返回实现 | |
| 260 | +- (void)backButtonClick { | |
| 261 | + if (self.navigationController) { | |
| 262 | + TZImagePickerController *imagePickerVc = (TZImagePickerController *)self.navigationController; | |
| 263 | + [imagePickerVc popViewControllerAnimated:YES]; | |
| 264 | + } else { | |
| 265 | + [self dismissViewControllerAnimated:YES completion:^{ | |
| 266 | + | |
| 267 | + }]; | |
| 268 | + } | |
| 269 | +} | |
| 270 | + | |
| 271 | + | |
| 272 | +#pragma mark - Notification Method | |
| 273 | + | |
| 274 | +- (void)pausePlayerAndShowNaviBar { | |
| 275 | + [_player pause]; | |
| 276 | + _toolBar.hidden = NO; | |
| 277 | + _naviBar.hidden = NO; | |
| 278 | +// [self.navigationController setNavigationBarHidden:NO]; | |
| 279 | + [_playButton setImage:[UIImage imageNamedFromMyBundle:@"MMVideoPreviewPlay"] forState:UIControlStateNormal]; | |
| 280 | + ///LXG 注释 | |
| 281 | +// if (self.needShowStatusBar && iOS7Later) { | |
| 282 | +// [UIApplication sharedApplication].statusBarHidden = NO; | |
| 283 | +// } | |
| 284 | +} | |
| 285 | + | |
| 286 | +- (void)dealloc { | |
| 287 | + [[NSNotificationCenter defaultCenter] removeObserver:self]; | |
| 288 | +} | |
| 289 | + | |
| 290 | +#pragma clang diagnostic pop | |
| 291 | + | |
| 292 | +@end | ... | ... |
CNLiveImagePickerController/UIView+Layout.h
0 → 100755
| 1 | +// | |
| 2 | +// UIView+Layout.h | |
| 3 | +// | |
| 4 | +// Created by 谭真 on 15/2/24. | |
| 5 | +// Copyright © 2015年 谭真. All rights reserved. | |
| 6 | +// | |
| 7 | + | |
| 8 | +#import <UIKit/UIKit.h> | |
| 9 | + | |
| 10 | +typedef enum : NSUInteger { | |
| 11 | + TZOscillatoryAnimationToBigger, | |
| 12 | + TZOscillatoryAnimationToSmaller, | |
| 13 | +} TZOscillatoryAnimationType; | |
| 14 | + | |
| 15 | +@interface UIView (Layout) | |
| 16 | + | |
| 17 | +@property (nonatomic) CGFloat tz_left; ///< Shortcut for frame.origin.x. | |
| 18 | +@property (nonatomic) CGFloat tz_top; ///< Shortcut for frame.origin.y | |
| 19 | +@property (nonatomic) CGFloat tz_right; ///< Shortcut for frame.origin.x + frame.size.width | |
| 20 | +@property (nonatomic) CGFloat tz_bottom; ///< Shortcut for frame.origin.y + frame.size.height | |
| 21 | +@property (nonatomic) CGFloat tz_width; ///< Shortcut for frame.size.width. | |
| 22 | +@property (nonatomic) CGFloat tz_height; ///< Shortcut for frame.size.height. | |
| 23 | +@property (nonatomic) CGFloat tz_centerX; ///< Shortcut for center.x | |
| 24 | +@property (nonatomic) CGFloat tz_centerY; ///< Shortcut for center.y | |
| 25 | +@property (nonatomic) CGPoint tz_origin; ///< Shortcut for frame.origin. | |
| 26 | +@property (nonatomic) CGSize tz_size; ///< Shortcut for frame.size. | |
| 27 | + | |
| 28 | ++ (void)showOscillatoryAnimationWithLayer:(CALayer *)layer type:(TZOscillatoryAnimationType)type; | |
| 29 | + | |
| 30 | +@end | ... | ... |
CNLiveImagePickerController/UIView+Layout.m
0 → 100755
| 1 | +// | |
| 2 | +// UIView+Layout.m | |
| 3 | +// | |
| 4 | +// Created by 谭真 on 15/2/24. | |
| 5 | +// Copyright © 2015年 谭真. All rights reserved. | |
| 6 | +// | |
| 7 | + | |
| 8 | +#import "UIView+Layout.h" | |
| 9 | + | |
| 10 | +@implementation UIView (Layout) | |
| 11 | + | |
| 12 | +- (CGFloat)tz_left { | |
| 13 | + return self.frame.origin.x; | |
| 14 | +} | |
| 15 | + | |
| 16 | +- (void)setTz_left:(CGFloat)x { | |
| 17 | + CGRect frame = self.frame; | |
| 18 | + frame.origin.x = x; | |
| 19 | + self.frame = frame; | |
| 20 | +} | |
| 21 | + | |
| 22 | +- (CGFloat)tz_top { | |
| 23 | + return self.frame.origin.y; | |
| 24 | +} | |
| 25 | + | |
| 26 | +- (void)setTz_top:(CGFloat)y { | |
| 27 | + CGRect frame = self.frame; | |
| 28 | + frame.origin.y = y; | |
| 29 | + self.frame = frame; | |
| 30 | +} | |
| 31 | + | |
| 32 | +- (CGFloat)tz_right { | |
| 33 | + return self.frame.origin.x + self.frame.size.width; | |
| 34 | +} | |
| 35 | + | |
| 36 | +- (void)setTz_right:(CGFloat)right { | |
| 37 | + CGRect frame = self.frame; | |
| 38 | + frame.origin.x = right - frame.size.width; | |
| 39 | + self.frame = frame; | |
| 40 | +} | |
| 41 | + | |
| 42 | +- (CGFloat)tz_bottom { | |
| 43 | + return self.frame.origin.y + self.frame.size.height; | |
| 44 | +} | |
| 45 | + | |
| 46 | +- (void)setTz_bottom:(CGFloat)bottom { | |
| 47 | + CGRect frame = self.frame; | |
| 48 | + frame.origin.y = bottom - frame.size.height; | |
| 49 | + self.frame = frame; | |
| 50 | +} | |
| 51 | + | |
| 52 | +- (CGFloat)tz_width { | |
| 53 | + return self.frame.size.width; | |
| 54 | +} | |
| 55 | + | |
| 56 | +- (void)setTz_width:(CGFloat)width { | |
| 57 | + CGRect frame = self.frame; | |
| 58 | + frame.size.width = width; | |
| 59 | + self.frame = frame; | |
| 60 | +} | |
| 61 | + | |
| 62 | +- (CGFloat)tz_height { | |
| 63 | + return self.frame.size.height; | |
| 64 | +} | |
| 65 | + | |
| 66 | +- (void)setTz_height:(CGFloat)height { | |
| 67 | + CGRect frame = self.frame; | |
| 68 | + frame.size.height = height; | |
| 69 | + self.frame = frame; | |
| 70 | +} | |
| 71 | + | |
| 72 | +- (CGFloat)tz_centerX { | |
| 73 | + return self.center.x; | |
| 74 | +} | |
| 75 | + | |
| 76 | +- (void)setTz_centerX:(CGFloat)centerX { | |
| 77 | + self.center = CGPointMake(centerX, self.center.y); | |
| 78 | +} | |
| 79 | + | |
| 80 | +- (CGFloat)tz_centerY { | |
| 81 | + return self.center.y; | |
| 82 | +} | |
| 83 | + | |
| 84 | +- (void)setTz_centerY:(CGFloat)centerY { | |
| 85 | + self.center = CGPointMake(self.center.x, centerY); | |
| 86 | +} | |
| 87 | + | |
| 88 | +- (CGPoint)tz_origin { | |
| 89 | + return self.frame.origin; | |
| 90 | +} | |
| 91 | + | |
| 92 | +- (void)setTz_origin:(CGPoint)origin { | |
| 93 | + CGRect frame = self.frame; | |
| 94 | + frame.origin = origin; | |
| 95 | + self.frame = frame; | |
| 96 | +} | |
| 97 | + | |
| 98 | +- (CGSize)tz_size { | |
| 99 | + return self.frame.size; | |
| 100 | +} | |
| 101 | + | |
| 102 | +- (void)setTz_size:(CGSize)size { | |
| 103 | + CGRect frame = self.frame; | |
| 104 | + frame.size = size; | |
| 105 | + self.frame = frame; | |
| 106 | +} | |
| 107 | + | |
| 108 | ++ (void)showOscillatoryAnimationWithLayer:(CALayer *)layer type:(TZOscillatoryAnimationType)type{ | |
| 109 | + NSNumber *animationScale1 = type == TZOscillatoryAnimationToBigger ? @(1.15) : @(0.5); | |
| 110 | + NSNumber *animationScale2 = type == TZOscillatoryAnimationToBigger ? @(0.92) : @(1.15); | |
| 111 | + | |
| 112 | + [UIView animateWithDuration:0.15 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut animations:^{ | |
| 113 | + [layer setValue:animationScale1 forKeyPath:@"transform.scale"]; | |
| 114 | + } completion:^(BOOL finished) { | |
| 115 | + [UIView animateWithDuration:0.15 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut animations:^{ | |
| 116 | + [layer setValue:animationScale2 forKeyPath:@"transform.scale"]; | |
| 117 | + } completion:^(BOOL finished) { | |
| 118 | + [UIView animateWithDuration:0.1 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut animations:^{ | |
| 119 | + [layer setValue:@(1.0) forKeyPath:@"transform.scale"]; | |
| 120 | + } completion:nil]; | |
| 121 | + }]; | |
| 122 | + }]; | |
| 123 | +} | |
| 124 | + | |
| 125 | +@end | ... | ... |