Swift.自定制相册,实现首个cell是拍照功能
前言:
过去有个项目有了这个需求.当时选择从git上找了个框架直接来用,但是其中很多功能是我并不需要的,以及想要了解一下这一块的具体实现方式,就选择自己参考其重写了一下,主要运用的知识就是photos框架以及图片编辑那一块对view的操作逻辑.
如果对这些感兴趣的可以仔细看下项目参考下.如果只是有类似需求可以直接拉到最下方使用方式.
实现功能:
自定制相册.
实现相片展示页首个cell是拍照功能.
实现相片选中裁切功能.
实现思路:
1.使用photos框架获取手机内所有图片.
2.使用CollectionView将图片展示,并实现首个cell是相机按钮功能.
3.新建控制器实现选中照片编辑功能.
4.使用代理将编辑完照片回调.
实现方式:
0.在info.plist文件中添加相机与相册调用权限.
1.创建EWPickerManager类,用于获取以及存储数据.
2.获取手机内所有相片,PHFetchResult<PHAsset>()格式
3.将相片转换成image格式,并将其存储为一个Array.
4.创建相册展示页ViewController.为其添加CollectionView.并将获取的相片数据展示,同时实现首个cell是相机展示.
5.创建相片选中编辑页.为其添加背景展示图片View,上层覆盖半透明View,下方取消确认按钮View.以及中间透明裁切区域View.
6.添加相片方向校正方法,保证相片正常展示.
7.为编辑页添加缩放手势以及拖拽手势.
8.添加frame验证方法,保证相片在缩放拖拽后展示效果.
9.添加相片裁切方法.
10.创建EWImageCropperDelegate将编辑完成的相片回调.
11.添加调用相机功能.
12.为相册展示页CollectionView.Cell添加点击方法.
13.使用自定制navigationController将交互串联.
0.在info.plist文件中添加相机与相册调用权限.
所有与相机相册相关的功能都需要添加权限说明,重要!
<key>NSPhotoLibraryUsageDescription</key>
<string>相册权限</string>
<key>NSCameraUsageDescription</key>
<string>相机权限</string>
1.创建EWPickerManager类,用于获取以及存储数据.
class EWPickerManager: NSObject {
private(set) var photoAlbum = PHFetchResult<PHAsset>()
private var photoManage = PHCachingImageManager()
/// 照片获取偏好设置
private let photoOption = PHImageRequestOptions()
//pickerCell照片的size
class public var pickerPhotoSize: CGSize {
let sreenBounds = ScreenInfo.Frame
let screenWidth = sreenBounds.width > sreenBounds.height ? sreenBounds.height : sreenBounds.width
let width = (screenWidth - CGFloat(9)) / CGFloat(4)
return CGSize(width: width, height: width)
}
override init() {
super.init()
// 如何调整所请求的图像大小。
self.photoOption.resizeMode = .fast //最快速的调整图像大小,有可能比给定大小略大
// 请求的图像质量和交付优先级。
self.photoOption.deliveryMode = .opportunistic //平衡图像质量和响应速度
// 是否同步处理一个图像请求.
self.photoOption.isSynchronous = true
getPhotoAlbums()
}
}
2.获取手机内所有相片,PHFetchResult<PHAsset>()格式
/// 获取手机中所有相册照片源
private func getPhotoAlbums(){
//创建一个PHFetchOptions对象检索照片
let options = PHFetchOptions()
//通过创建时间来检索
options.sortDescriptors = [NSSortDescriptor.init(key: "creationDate", ascending: false)]
//通过数据类型来检索,这里为只检索照片
options.predicate = NSPredicate.init(format: "mediaType in %@", [PHAssetMediaType.image.rawValue])
//通过检索条件检索出符合检索条件的所有数据,也就是所有的照片
let allResult = PHAsset.fetchAssets(with: options)
//将获取的相片加入到相片的数组中
photoAlbum = allResult
}
3.将相片转换成image格式,并将其存储为一个Array.
/// 获取手机相册内所有照片
///
/// - Returns: 手机相册内所有照片
public func getAllPhoto() -> [UIImage]{
var imageArray = [UIImage]()
let scale = UIScreen.main.scale
/// 重要,不对size进行重置会使显示效果变差
let photoScaleSize = CGSize(width: EWPickerManager.pickerPhotoSize.width * scale, height: EWPickerManager.pickerPhotoSize.height * scale)
/// 将图片添加到数组
for i in 0 ..< self.photoAlbum.count {
/// 按顺序获取图片
self.photoManage.requestImage(for: self.photoAlbum[i], targetSize: photoScaleSize, contentMode: .aspectFill, options: self.photoOption) { (image, infoDic) in
if image != nil{
imageArray.append(image!)
}
}
}
return imageArray
}
4.创建相册展示页ViewController.为其添加CollectionView.并将获取的相片数据展示,同时实现首个cell是相机展示.
import UIKit
class EWPhotoCollectionViewController: UIViewController {
public var delegate: EWImageCropperDelegate?
private let manager = EWPickerManager()
private var photoArray = [UIImage]()
private let collectionView: UICollectionView = {
let defaultLayout = UICollectionViewFlowLayout()
defaultLayout.scrollDirection = UICollectionViewScrollDirection.vertical//设置垂直显示
defaultLayout.minimumLineSpacing = 3 //每个相邻的layout的上下间隔
defaultLayout.minimumInteritemSpacing = 3.0 //每个相邻layout的左右间隔
let collectionView = UICollectionView(frame:CGRect(x: 0, y: 88, width: ScreenInfo.Width, height: ScreenInfo.Height - ScreenInfo.navigationHeight), collectionViewLayout: defaultLayout)
collectionView.backgroundColor = UIColor.white
return collectionView
}()
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
self.title = "相册"
getPhotoData()
drawMyView()
drawMyNavigationBar()
}
private func drawMyView(){
self.view.backgroundColor = UIColor.white
collectionView.delegate = self
collectionView.dataSource = self
collectionView.alwaysBounceVertical = true
/// 使用动态注册阻止collectionView重用
for i in 0 ..< photoArray.count+1{
collectionView.register(EWPhotoCollectionViewCell.self, forCellWithReuseIdentifier: "EWPhotoCollectionViewCell\(i)")
}
self.view.addSubview(collectionView)
}
private func drawMyNavigationBar(){
let button = UIBarButtonItem(image: EWBundle.imageFromBundle("image_back"), style: .plain, target: self, action: #selector(onClickBackButton))
self.navigationItem.leftBarButtonItem = button
}
/// 获取所有照片
private func getPhotoData(){
self.photoArray = manager.getAllPhoto()
}
@objc private func onClickBackButton(){
self.dismiss(animated: true, completion: nil)
}
}
//MARK: - CollectionViewDelegate
extension EWPhotoCollectionViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
/// 返回数据数组.count 加一个新建按钮
return self.photoArray.count + 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "EWPhotoCollectionViewCell\(indexPath.row)", for: indexPath) as? EWPhotoCollectionViewCell else {
return EWPhotoCollectionViewCell()
}
guard indexPath.row > 0 else {
cell.setData()
return cell
}
cell.setData(image: photoArray[indexPath.row - 1])
return cell
}
internal func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return EWPickerManager.pickerPhotoSize
}
}
5.创建相片选中编辑页.为其添加背景展示图片View,上层覆盖半透明View,下方取消确认按钮View.以及中间透明裁切区域View.
/// 选中图片后裁切控制器
class EWPhotoCropViewController: UIViewController {
/// 初始frame
private var oldFrame: CGRect?
/// 最大frame
private var largeFrame: CGRect?
/// 裁切区域frame
private var cropFrame: CGRect?
/// 最后结果frame
private var latestFrame: CGRect?
/// 选中的照片
private var selectedPhoto: UIImage = UIImage()
public var delegate: EWImageCropperDelegate?
/// 遮挡在选中imageView上层的半透明View
private let overlayView: UIView = {
let view = UIImageView(frame: CGRect(x: 0, y: 0, width: ScreenInfo.Width, height: ScreenInfo.Height))
view.backgroundColor = UIColor.black
view.alpha = 0.5
view.isUserInteractionEnabled = false
return view
}()
/// 裁切区域View
private let cropView: UIView = {
let view = UIView(frame: CGRect(x: 0 , y: (ScreenInfo.Height - ScreenInfo.Width) / 2, width: ScreenInfo.Width, height: ScreenInfo.Width))
view.layer.borderWidth = 1
view.layer.borderColor = UIColor.yellow.cgColor
return view
}()
/// 展示图片背景View
private let backImageView: UIImageView = {
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: ScreenInfo.Width, height: ScreenInfo.Height))
imageView.isMultipleTouchEnabled = true
imageView.isUserInteractionEnabled = true
imageView.contentMode = .scaleAspectFit
return imageView
}()
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
convenience init(image: UIImage) {
self.init(nibName: nil, bundle: nil)
/// 保证图片方向
self.selectedPhoto = self.fixOrientation(image)
self.backImageView.image = selectedPhoto
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
drawMyView()
drawMyNavigationBar()
}
private func drawMyView(){
self.view.backgroundColor = UIColor.black
self.view.addSubview(backImageView)
initViewsFrame()
self.view.addSubview(overlayView)
self.view.addSubview(cropView)
overlayClipping()
drawBottomButtonView()
addGestureRecognizers()
}
private func drawMyNavigationBar(){
let button = UIBarButtonItem(image: EWBundle.imageFromBundle("image_back"), style: .plain, target: self, action: #selector(onClickCancelbuton))
self.navigationItem.leftBarButtonItem = button
}
private func initViewsFrame(){
/// 裁切区域Width
let cropWidth = UIScreen.main.bounds.width
/// 获取选中图片width等于裁切区域时的height
let photoOldHeight = selectedPhoto.size.height / selectedPhoto.size.width * cropWidth
// 判断选中图片OldHeight
if photoOldHeight > ScreenInfo.Height {
// 大于屏幕尺寸,则正常展示
self.backImageView.frame = CGRect(x: 0, y: 0, width: cropWidth, height: photoOldHeight)
}else{
// 小于屏幕尺寸,将其置于中心展示
self.backImageView.frame = CGRect(x: 0, y: (ScreenInfo.Height - photoOldHeight) / 2 , width: cropWidth, height: photoOldHeight)
}
/// 获取照片展示oldFrame
oldFrame = self.backImageView.frame
/// 初始化lastestFrame,使其为oldFrame
latestFrame = self.oldFrame
/// 初始化最大frame.使其为oldFrame.size*3 也就是设定图片放大比例不能超过3倍
largeFrame = CGRect(x: 0, y: 0, width: (oldFrame?.size.width)! * 3, height: (oldFrame?.size.height)! * 3)
/// 裁切frame就是裁切View.frame
cropFrame = self.cropView.frame
}
/// 添加下方取消与完成按钮
private func drawBottomButtonView(){
let centerView = UIView(frame:CGRect(x: 100, y: self.view.frame.size.height - 50.0, width: self.view.frame.size.width-200, height: 50))
centerView.backgroundColor = UIColor.black
centerView.alpha = 0.5
self.view.addSubview(centerView)
let cancelBtn = UIButton(frame: CGRect(x: 0, y: self.view.frame.size.height - 50.0, width: 100, height: 50))
cancelBtn.backgroundColor = UIColor.black
cancelBtn.alpha = 0.5
cancelBtn.titleLabel?.textColor = UIColor.white
cancelBtn.setTitle("取消", for: UIControlState())
cancelBtn.titleLabel?.font = UIFont.systemFont(ofSize: 18.0)
cancelBtn.titleLabel?.textAlignment = NSTextAlignment.center
cancelBtn.titleLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping
cancelBtn.titleLabel?.numberOfLines = 0
cancelBtn.titleEdgeInsets = UIEdgeInsetsMake(5.0, 5.0, 5.0, 5.0)
cancelBtn.addTarget(self, action:#selector(onClickCancelbuton), for: UIControlEvents.touchUpInside)
self.view.addSubview(cancelBtn)
let confirmBtn:UIButton = UIButton(frame: CGRect(x: self.view.frame.size.width - 100.0, y: self.view.frame.size.height - 50.0, width: 100, height: 50))
confirmBtn.backgroundColor = UIColor.black
confirmBtn.alpha = 0.5
confirmBtn.titleLabel?.textColor = UIColor.white
confirmBtn.setTitle("确定", for: UIControlState())
confirmBtn.titleLabel?.font = UIFont.systemFont(ofSize: 18.0)
confirmBtn.titleLabel?.textAlignment = NSTextAlignment.center
confirmBtn.titleLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping
confirmBtn.titleLabel?.numberOfLines = 0
confirmBtn.titleEdgeInsets = UIEdgeInsetsMake(5.0, 5.0, 5.0, 5.0)
confirmBtn.addTarget(self, action:#selector(onClickConfirmButton), for: UIControlEvents.touchUpInside)
self.view.addSubview(confirmBtn)
}
/// 修改overlayView.layer.使cropView不被遮挡
private func overlayClipping() {
let maskLayer = CAShapeLayer()
let path = CGMutablePath()
// 裁切View左侧side
path.addRect(CGRect(x: 0, y: 0, width: self.cropView.frame.origin.x, height: self.overlayView.frame.size.height))
// 裁切View右侧side
path.addRect(CGRect(
x: self.cropView.frame.origin.x + self.cropView.frame.size.width, y: 0, width: self.overlayView.frame.size.width - self.cropView.frame.origin.x - self.cropView.frame.size.width, height: self.overlayView.frame.size.height))
// 裁切View上方side
path.addRect(CGRect(x: 0, y: 0, width: self.overlayView.frame.size.width, height: self.cropView.frame.origin.y))
// 裁切View下方side
path.addRect(CGRect(x: 0, y: self.cropView.frame.origin.y + self.cropView.frame.size.height, width: self.overlayView.frame.size.width, height: self.overlayView.frame.size.height - self.cropView.frame.origin.y + self.cropView.frame.size.height))
maskLayer.path = path
/// 修改overlayView.将裁切View区域空白出来
self.overlayView.layer.mask = maskLayer
path.closeSubpath()
}
}
6.添加相片方向校正方法,保证相片正常展示.
// 保证图片方向
func fixOrientation(_ srcImg:UIImage) -> UIImage {
if srcImg.imageOrientation == UIImageOrientation.up {
return srcImg
}
var transform = CGAffineTransform.identity
switch srcImg.imageOrientation {
case UIImageOrientation.down, UIImageOrientation.downMirrored:
transform = transform.translatedBy(x: srcImg.size.width, y: srcImg.size.height)
transform = transform.rotated(by: .pi)
case UIImageOrientation.left, UIImageOrientation.leftMirrored:
transform = transform.translatedBy(x: srcImg.size.width, y: 0)
transform = transform.rotated(by: .pi/2)
case UIImageOrientation.right, UIImageOrientation.rightMirrored:
transform = transform.translatedBy(x: 0, y: srcImg.size.height)
transform = transform.rotated(by: -.pi/2)
case UIImageOrientation.up, UIImageOrientation.upMirrored: break
}
switch srcImg.imageOrientation {
case UIImageOrientation.upMirrored, UIImageOrientation.downMirrored:
transform = transform.translatedBy(x: srcImg.size.width, y: 0)
transform = transform.scaledBy(x: -1, y: 1)
case UIImageOrientation.leftMirrored, UIImageOrientation.rightMirrored:
transform = transform.translatedBy(x: srcImg.size.height, y: 0)
transform = transform.scaledBy(x: -1, y: 1)
case UIImageOrientation.up, UIImageOrientation.down, UIImageOrientation.left, UIImageOrientation.right:break
}
// 上下文
let ctx:CGContext = CGContext(data: nil, width: Int(srcImg.size.width), height: Int(srcImg.size.height), bitsPerComponent: srcImg.cgImage!.bitsPerComponent, bytesPerRow: 0, space: srcImg.cgImage!.colorSpace!, bitmapInfo: srcImg.cgImage!.bitmapInfo.rawValue)!
ctx.concatenate(transform)
switch srcImg.imageOrientation {
case UIImageOrientation.left, UIImageOrientation.leftMirrored, UIImageOrientation.right, UIImageOrientation.rightMirrored:
ctx.draw(srcImg.cgImage!, in: CGRect(x: 0, y: 0, width: srcImg.size.height, height: srcImg.size.width))
default:
ctx.draw(srcImg.cgImage!, in: CGRect(x: 0, y: 0, width: srcImg.size.width, height: srcImg.size.height))
}
let cgImg:CGImage = ctx.makeImage()!
let img:UIImage = UIImage(cgImage: cgImg)
ctx.closePath()
return img
}
7.为编辑页添加缩放手势以及拖拽手势.
/// 添加缩放,以及拖拽手势
private func addGestureRecognizers() {
// 缩放手势
let pinchGestureRecognizer:UIPinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(pinchView(_:)))
self.view.addGestureRecognizer(pinchGestureRecognizer)
// 拖拽手势
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(panView(_:)))
self.view.addGestureRecognizer(panGestureRecognizer)
}
/// 缩放手势方法
@objc private func pinchView(_ pinchGestureRecognizer:UIPinchGestureRecognizer) {
if pinchGestureRecognizer.state == .began || pinchGestureRecognizer.state == .changed{
/// 当缩放手势开始,以及正在进行中时,根据手势缩放比例对应将展示照片的backImageView通过transform进行等比例缩放
self.backImageView.transform = backImageView.transform.scaledBy(x: pinchGestureRecognizer.scale, y: pinchGestureRecognizer.scale)
pinchGestureRecognizer.scale = 1
}else if pinchGestureRecognizer.state == .ended{
/// 获取手势结束后backImageView.frame,并通过验证方法对其修正
var newFrame = self.backImageView.frame
/// 修正比例
newFrame = handleScaleOverflow(newFrame)
/// 修正位置
newFrame = handleBorderOverflow(newFrame)
/// 使backImageView.frame变为修正后的frame,并添加动画效果
UIView.animate(withDuration: 0.3) {
self.backImageView.frame = newFrame
self.latestFrame = newFrame
}
}
}
//拖拽手势方法
@objc private func panView(_ panGestureRecognizer:UIPanGestureRecognizer) {
let view = self.backImageView
if panGestureRecognizer.state == .began || panGestureRecognizer.state == .changed{
/// 当拖拽手势开始以及正在进行中时,根据拖拽位移以及图片比例,通过修改view.center实现拖拽效果
let absCenterX = self.cropFrame!.origin.x + self.cropFrame!.size.width / 2
let absCenterY = self.cropFrame!.origin.y + self.cropFrame!.size.height / 2
let scaleRatio = self.backImageView.frame.size.width / self.cropFrame!.size.width
let acceleratorX = 1 - abs(absCenterX - view.center.x) / (scaleRatio * absCenterX)
let acceleratorY = 1 - abs(absCenterY - view.center.y) / (scaleRatio * absCenterY)
let translation = panGestureRecognizer.translation(in: view.superview)
view.center = CGPoint(x:view.center.x + translation.x * acceleratorX, y: view.center.y + translation.y * acceleratorY)
panGestureRecognizer.setTranslation(CGPoint.zero, in: view.superview)
}else if panGestureRecognizer.state == .ended{
/// 获取手势结束后backImageView.frame,并通过验证方法对其修正
var newFrame = self.backImageView.frame
/// 修正位置
newFrame = self.handleBorderOverflow(newFrame)
UIView.animate(withDuration: 0.3) {
/// 使backImageView.frame变为修正后的frame,并添加动画效果
self.backImageView.frame = newFrame
self.latestFrame = newFrame
}
}
}
8.添加frame验证方法,保证相片在缩放拖拽后展示效果.
/// 修正size不小于初始值,不大于最大值
///
/// - Parameter newFrame: 变更过的frame
/// - Returns: 修正后frame
private func handleScaleOverflow(_ newFrame:CGRect) -> CGRect {
var newFrame = newFrame
let oriCenter = CGPoint(x: newFrame.origin.x + newFrame.size.width / 2, y: newFrame.origin.y + newFrame.size
.height / 2)
/// 如果frame.size小于最小值则使其等于最小值
if newFrame.size.width < self.oldFrame!.size.width {
newFrame = self.oldFrame!
}
/// 如果frame.size大于最大值则使其等于最大值
if newFrame.size.width > self.largeFrame!.size.width {
newFrame = self.largeFrame!
}
newFrame.origin.x = oriCenter.x - newFrame.size.width / 2
newFrame.origin.y = oriCenter.y - newFrame.size.height / 2
return newFrame
}
/// 修正frame保证view展示不超过裁切区域
private func handleBorderOverflow(_ newFrame:CGRect) -> CGRect {
var newFrame = newFrame
if newFrame.origin.x > self.cropFrame!.origin.x {
newFrame.origin.x = self.cropFrame!.origin.x
}
if newFrame.maxX < self.cropFrame!.size.width {
newFrame.origin.x = self.cropFrame!.size.width - newFrame.size.width
}
if newFrame.origin.y > self.cropFrame!.origin.y {
newFrame.origin.y = self.cropFrame!.origin.y
}
if newFrame.maxY < self.cropFrame!.origin.y + self.cropFrame!.size.height {
newFrame.origin.y = self.cropFrame!.origin.y + self.cropFrame!.size.height - newFrame.size.height
}
if self.backImageView.frame.size.width > self.backImageView.frame.size.height && newFrame.size.height <= self.cropFrame!.size.height {
newFrame.origin.y = self.cropFrame!.origin.y + (self.cropFrame!.size.height - newFrame.size.height) / 2
}
return newFrame
}
9.添加相片裁切方法.
/// 获取截取图片
private func getSubImage() -> UIImage {
/// 获取截取位置Frame
let squareFrame = self.cropFrame!
/// 获取图片缩放比例
let scaleRatio = self.latestFrame!.size.width / self.selectedPhoto.size.width
var x = (squareFrame.origin.x - self.latestFrame!.origin.x) / scaleRatio
var y = (squareFrame.origin.y - self.latestFrame!.origin.y) / scaleRatio
var w = squareFrame.size.width / scaleRatio
var h = squareFrame.size.height / scaleRatio
if self.latestFrame!.size.width < self.cropFrame!.size.width {
let newW = self.selectedPhoto.size.width
let newH = newW * (self.cropFrame!.size.height / self.cropFrame!.size.width)
x = 0;
y = y + (h - newH) / 2
w = newH
h = newH
}
if self.latestFrame!.size.height < self.cropFrame!.size.height {
let newH = self.selectedPhoto.size.height
let newW = newH * (self.cropFrame!.size.width / self.cropFrame!.size.height)
x = x + (w - newW) / 2
y = 0
w = newH
h = newH
}
/// 获取截取图片的frame
let myImageRect = CGRect(x: x, y: y, width: w, height: h)
let imageRef = self.selectedPhoto.cgImage
let subImageRef = imageRef?.cropping(to: myImageRect)
let size:CGSize = CGSize(width: myImageRect.size.width, height: myImageRect.size.height)
UIGraphicsBeginImageContext(size)
let context:CGContext = UIGraphicsGetCurrentContext()!
context.draw(subImageRef!, in: myImageRect)
let smallImage = UIImage(cgImage: subImageRef!)
UIGraphicsEndImageContext()
return smallImage
}
10.创建EWImageCropperDelegate将编辑完成的相片回调.
/// 裁切后的照片返回协议
@objc protocol EWImageCropperDelegate : NSObjectProtocol {
func imageCropper(_ cropperViewController:EWPhotoCropViewController, didFinished editImg:UIImage)
}
11.添加调用相机功能.
// MARK: - UIImagePickerControllerDelegate & UINavigationControllerDelegate
extension EWPhotoCollectionViewController:UIImagePickerControllerDelegate& UINavigationControllerDelegate{
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
picker.dismiss(animated: true, completion: { () -> Void in
})
//相册中还可能是视频,所以这里需要判断选择的是不是图片
let type: String = (info[UIImagePickerControllerMediaType] as! String)
//当选择的类型是图片
if type == "public.image" {
let image:UIImage = info[UIImagePickerControllerOriginalImage] as! UIImage
//先把图片转成NSData
let data = UIImageJPEGRepresentation(image, 0.4)
//图片保存的路径 //这里将图片放在沙盒的documents文件夹中
let DocumentsPath:String = NSHomeDirectory()+"/Documents"
//文件管理器
let fileManager = FileManager.default
//把刚刚图片转换的data对象拷贝至沙盒中 并保存为image.png
try! fileManager.createDirectory(atPath: DocumentsPath, withIntermediateDirectories: true, attributes: nil)
fileManager.createFile(atPath: DocumentsPath + "/image.png", contents: data, attributes: nil)
//得到选择后沙盒中图片的完整路径
let filePath = DocumentsPath + "/image.png"
let previewImage = UIImage(contentsOfFile: filePath)
let pcvc = EWPhotoCropViewController(image: previewImage!)
pcvc.delegate = self.delegate
self.navigationController?.pushViewController(pcvc, animated: true)
}
}
}
/// 调用相机
private func cameraShow(){
if UIImagePickerController.isSourceTypeAvailable(.camera) {
let picker = UIImagePickerController()
picker.sourceType = .camera
picker.delegate = self
picker.allowsEditing = false
self.present(picker, animated: true, completion: nil)
} else {
print("模拟器中无法打开照相机,请在真机中使用")
}
}
12.为相册展示页CollectionView.Cell添加点击方法.
/// cell点击方法
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard indexPath.row != 0 else {
cameraShow()
return
}
manager.getPhotoData(index: indexPath.row - 1) { (data, infoDic) in
guard data != nil else { return }
let image = UIImage(data: data!)
let VC = EWPhotoCropViewController(image: image!)
VC.delegate = self.delegate
self.navigationController?.pushViewController(VC, animated: true)
}
}
13.使用自定制navigationController将交互串联.
import UIKit
/// 通用navigationController
class EWPhotoPickerViewController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
/// 将delegate传入,可自定制tintColor,默认黑色
init(photoDelegate: EWImageCropperDelegate, tintColor: UIColor = UIColor.black) {
let vc = EWPhotoCollectionViewController()
vc.delegate = photoDelegate
super.init(rootViewController: vc)
self.navigationBar.tintColor = tintColor
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
添加方法:
1.使用CocoaPods加载.在podfile文件中添加
pod 'EWPhotoPicker'
2.直接从github下载项目将EWPhotoPicker文件夹拖入项目.
调用方法:
1.首先给项目info.plist文件中添加相机与相册权限,权限提示自行修改
<key>NSPhotoLibraryUsageDescription</key>
<string>相册权限</string>
<key>NSCameraUsageDescription</key>
<string>相机权限</string>
2.调用相册时:
先让弹出相册的控制器遵循EWImageCropperDelegate.并在代理方法中获取image.
当需要弹出相册时直接初始化EWPhotoPickerViewController,调用present方法.
/// 弹出控制器时时直接present就可以
@objc private func onClickPhotoButton(){
let nnvc = EWPhotoPickerViewController(photoDelegate: self)
/// 可以传入navigationBar.tintColor以保证与项目相同展示效果.默认不传为UIColor.black
// let PPVC = EWPhotoPickerViewController(photoDelegate: self, tintColor: UIColor.gray)
self.present(nnvc, animated: true, completion: nil)
}
/// 调用控制器遵循EWImageCropperDelegate,实现唯一的方法.
extension ViewController: EWImageCropperDelegate{
func imageCropper(_ cropperViewController: EWPhotoCropViewController, didFinished editImg: UIImage) {
cropperViewController.navigationController?.dismiss(animated: true, completion: nil)
///对选取并编辑后的图片直接使用
self.imageView.image = editImg
}
}
demo地址: EWPhotoPicker
有问题欢迎探讨.