最近要做一个iOS相机及相册图片上传,其中遇到了这些问题:1、图片增删在UICollectionView里的变化;2、获取相机拍摄的照片和相册的照片;3、将PHAsset对象转为UIImage对象;


具体实现过程:
首先整个控件结构如图所示:

storyboard长这样:

拖动三个UIViewController如图所示,其中:AlbumImageCollectionViewController(id为ImageCollectionVC)中拖入一个UICollectionView、一个UIToolBar,UICollectionView中的cell(id为cell)拖入两个UIImageView;AlbumImagePickerViewController(id为imagePickerVC)中拖入一个UITableView,UITableView的cell(id为cell)拖入两个UILabel。


PhotoCollectionViewCell:

import UIKit
// 已选择的图片缩略图cell
class PhotoCollectionViewCell: UICollectionViewCell {// 已选择的图片var imageView: UIImageView!// 删除按钮var button = UIButton()// MARK: - 初始化方法override init(frame: CGRect) {super.init(frame: frame)// 设置缩略图大小imageView = UIImageView(frame: self.bounds)// 设置按钮属性button = UIButton(type: UIButtonType.custom)button.setImage(UIImage.init(named: "delete"), for: UIControlState.normal)button.frame = CGRect(x: frame.size.width - 21, y: 1, width: 20, height: 20)self.addSubview(imageView)self.addSubview(button)}required init?(coder aDecoder: NSCoder) {fatalError("init(coder:) has not been implemented")}
}

AlbumImagePickerTableViewCell:

import UIKit
// 相簿列表单元格
class AlbumImagePickerTableViewCell: UITableViewCell {// 相簿名称标签@IBOutlet weak var titleLabel: UILabel!// 照片数量标签@IBOutlet weak var countLabel: UILabel!override func awakeFromNib() {super.awakeFromNib()self.layoutMargins = UIEdgeInsets.zero}override func setSelected(_ selected: Bool, animated: Bool) {super.setSelected(selected, animated: animated)}}

AlbumImageCollectionViewCell:

import UIKit
// 图片缩略图集合页单元格
class AlbumImageCollectionViewCell: UICollectionViewCell {// 显示缩略图@IBOutlet weak var imageView: UIImageView!// 显示选中状态的图标@IBOutlet weak var selectedIcon: UIImageView!// 设置是否选中open override var isSelected: Bool {didSet{if isSelected {selectedIcon.image = UIImage(named: "image_selected")}else{selectedIcon.image = UIImage(named: "image_not_selected")}}}// 播放动画,是否选中的图标改变时使用func playAnimate() {// 图标先缩小,再放大UIView.animateKeyframes(withDuration: 0.4, delay: 0, options: .allowUserInteraction, animations: {UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.2, animations: {self.selectedIcon.transform = CGAffineTransform(scaleX: 0.7, y: 0.7)})UIView.addKeyframe(withRelativeStartTime: 0.2, relativeDuration: 0.4, animations: {self.selectedIcon.transform = CGAffineTransform.identity})}, completion: nil)}override func awakeFromNib() {super.awakeFromNib()imageView.contentMode = .scaleAspectFillimageView.clipsToBounds = true}
}

AlbumImageCompleteButton:

import UIKit
// 照片选择页下方工具栏的“完成”按钮
class AlbumImageCompleteButton: UIView {// 已选照片数量标签var numLabel: UILabel!// 按钮标题标签“完成”var titleLabel: UILabel!// 按钮的默认尺寸let defaultFrame = CGRect(x: 0, y: 0, width: 70, height: 20)// 文字颜色(同时也是数字背景颜色)let titleColor = UIColor(red: 0x09/255, green: 0xbb/255, blue: 0x07/255, alpha: 1)// 点击点击手势var tapSingle: UITapGestureRecognizer?// 设置数量var num:Int = 0{didSet{if num == 0{numLabel.isHidden = true}else{numLabel.isHidden = falsenumLabel.text = "\(num)"playAnimate()}}}//是否可用var isEnabled:Bool = true {didSet{if isEnabled {titleLabel.textColor = titleColortapSingle?.isEnabled = true}else{titleLabel.textColor = UIColor.graytapSingle?.isEnabled = false}}}init(){super.init(frame:defaultFrame)// 已选照片数量标签初始化numLabel = UILabel(frame:CGRect(x: 0 , y: 0 , width: 20, height: 20))numLabel.backgroundColor = titleColornumLabel.layer.cornerRadius = 10numLabel.layer.masksToBounds = truenumLabel.textAlignment = .centernumLabel.font = UIFont.systemFont(ofSize: 15)numLabel.textColor = UIColor.whitenumLabel.isHidden = trueself.addSubview(numLabel)// 按钮标题标签初始化titleLabel = UILabel(frame:CGRect(x: 20 , y: 0 ,width: defaultFrame.width - 20,height: 20))titleLabel.text = "完成"titleLabel.textAlignment = .centertitleLabel.font = UIFont.systemFont(ofSize: 15)titleLabel.textColor = titleColorself.addSubview(titleLabel)}required init?(coder aDecoder: NSCoder) {fatalError("init(coder:) has not been implemented")}// 添加事件响应func addTarget(target: Any?, action: Selector?) {// 单击监听tapSingle = UITapGestureRecognizer(target:target,action:action)tapSingle!.numberOfTapsRequired = 1tapSingle!.numberOfTouchesRequired = 1self.addGestureRecognizer(tapSingle!)}// 用户数字改变时播放的动画func playAnimate() {// 从小变大,且有弹性效果self.numLabel.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5,initialSpringVelocity: 0.5, options: UIViewAnimationOptions(),animations: {self.numLabel.transform = CGAffineTransform.identity}, completion: nil)}
}

AlbumImagePickerViewController:

import UIKit
import Photos// 相簿列表项
struct AlbumItem {// 相簿名称var title:String?// 相簿内的资源var fetchResult:PHFetchResult<PHAsset>
}
// 相簿列表页控制器
class AlbumImagePickerViewController: UIViewController {// 显示相簿列表项的表格@IBOutlet weak var tableView: UITableView!// 相簿列表项集合var items: [AlbumItem] = []// 每次最多可选择的照片数量var maxSelected: Int = Int.max// 照片选择完毕后的回调var completeHandler: ((_ assets:[PHAsset])->())?// 从xib或者storyboard加载完毕就会调用override func awakeFromNib() {super.awakeFromNib()// 申请权限PHPhotoLibrary.requestAuthorization({ (status) inif status != .authorized {return}// 列出所有系统的智能相册let smartOptions = PHFetchOptions()let smartAlbums = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .albumRegular, options: smartOptions)self.convertCollection(collection: smartAlbums)// 列出所有用户创建的相册let userCollections = PHCollectionList.fetchTopLevelUserCollections(with: nil)self.convertCollection(collection: userCollections as! PHFetchResult<PHAssetCollection>)// 相册按包含的照片数量排序(降序)self.items.sort { (item1, item2) -> Bool inreturn item1.fetchResult.count > item2.fetchResult.count}// 异步加载表格数据,需要在主线程中调用reloadData() 方法DispatchQueue.main.async{self.tableView?.reloadData()// 首次进来后直接进入第一个相册图片展示页面(相机胶卷)if let imageCollectionVC = self.storyboard?.instantiateViewController(withIdentifier: "ImageCollectionVC")as? AlbumImageCollectionViewController{imageCollectionVC.title = self.items.first?.titleimageCollectionVC.assetsFetchResults = self.items.first?.fetchResultimageCollectionVC.completeHandler = self.completeHandlerimageCollectionVC.maxSelected = self.maxSelectedself.navigationController?.pushViewController(imageCollectionVC, animated: false)}}})}// 页面加载完毕override func viewDidLoad() {super.viewDidLoad()// 设置标题title = "相簿"// 设置表格相关样式属性self.tableView.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)self.tableView.rowHeight = 55// 添加导航栏右侧的取消按钮let rightBarItem = UIBarButtonItem(title: "取消", style: .plain, target: self, action:#selector(cancelBtnClicked) )self.navigationItem.rightBarButtonItem = rightBarItemself.tableView.delegate = selfself.tableView.dataSource = self}// 转化处理获取到的相簿private func convertCollection(collection:PHFetchResult<PHAssetCollection>){for i in 0..<collection.count{// 获取出但前相簿内的图片let resultsOptions = PHFetchOptions()resultsOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]resultsOptions.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.image.rawValue)let c = collection[i]let assetsFetchResult = PHAsset.fetchAssets(in: c , options: resultsOptions)// 没有图片的空相簿不显示if assetsFetchResult.count > 0 {let title = titleOfAlbumForChinese(title: c.localizedTitle)items.append(AlbumItem(title: title, fetchResult: assetsFetchResult))}}}// 由于系统返回的相册集名称为英文,我们需要转换为中文private func titleOfAlbumForChinese(title:String?) -> String? {if title == "Slo-mo" {return "慢动作"} else if title == "Recently Added" {return "最近添加"} else if title == "Favorites" {return "个人收藏"} else if title == "Recently Deleted" {return "最近删除"} else if title == "Videos" {return "视频"} else if title == "All Photos" {return "所有照片"} else if title == "Selfies" {return "自拍"} else if title == "Screenshots" {return "屏幕快照"} else if title == "Camera Roll" {return "相机胶卷"}return title}// 取消按钮点击监听方法func cancelBtnClicked() {// 退出当前vcself.dismiss(animated: true, completion: nil)}// 页面跳转override func prepare(for segue: UIStoryboardSegue, sender: Any?) {// 如果是跳转到展示相簿缩略图页面if segue.identifier == "showImages"{// 获取照片展示控制器guard let imageCollectionVC = segue.destinationas? AlbumImageCollectionViewController,let cell = sender as? AlbumImagePickerTableViewCell else{return}// 设置回调函数imageCollectionVC.completeHandler = completeHandler// 设置标题imageCollectionVC.title = cell.titleLabel.text// 设置最多可选图片数量imageCollectionVC.maxSelected = self.maxSelectedguard  let indexPath = self.tableView.indexPath(for: cell) else { return }// 获取选中的相簿信息let fetchResult = self.items[indexPath.row].fetchResult// 传递相簿内的图片资源imageCollectionVC.assetsFetchResults = fetchResult}}override func didReceiveMemoryWarning() {super.didReceiveMemoryWarning()}}// 相簿列表页控制器UITableViewDelegate,UITableViewDataSource协议方法的实现
extension AlbumImagePickerViewController: UITableViewDelegate, UITableViewDataSource {// 设置单元格内容func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)-> UITableViewCell {// 同一形式的单元格重复使用,在声明时已注册let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)as! AlbumImagePickerTableViewCelllet item = self.items[indexPath.row]cell.titleLabel.text = "\(item.title ?? "") "cell.countLabel.text = "(\(item.fetchResult.count))"return cell}// 表格单元格数量func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {return self.items.count}// 表格单元格选中func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {tableView.deselectRow(at: indexPath, animated: true)}
}extension UIViewController {// AlbumImagePicker提供给外部调用的接口,同于显示图片选择页面func presentAlbumImagePicker(maxSelected:Int = Int.max, completeHandler:((_ assets:[PHAsset])->())?) -> AlbumImagePickerViewController? {if let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "imagePickerVC") as? AlbumImagePickerViewController {//设置选择完毕后的回调vc.completeHandler = completeHandler//设置图片最多选择的数量vc.maxSelected = maxSelected//将图片选择视图控制器外添加个导航控制器,并显示let nav = UINavigationController(rootViewController: vc)self.present(nav, animated: true, completion: nil)return vc}return nil}
}

AlbumImageCollectionViewController:

import UIKit
import Photos// 图片缩略图集合页控制器
class AlbumImageCollectionViewController: UIViewController {// 用于显示所有图片缩略图的collectionView@IBOutlet weak var collectionView: UICollectionView!// 下方工具栏@IBOutlet weak var toolBar: UIToolbar!// 取得的资源结果,用了存放的PHAssetvar assetsFetchResults: PHFetchResult<PHAsset>!// 带缓存的图片管理对象var imageManager: PHCachingImageManager!// 缩略图大小var assetGridThumbnailSize: CGSize!// 每次最多可选择的照片数量var maxSelected: Int = Int.max// 照片选择完毕后的回调var completeHandler: ((_ assets: [PHAsset])->())?// 完成按钮var completeButton: AlbumImageCompleteButton!override func viewWillAppear(_ animated: Bool) {super.viewWillAppear(animated)// 根据单元格的尺寸计算我们需要的缩略图大小let scale = UIScreen.main.scalelet cellSize = (self.collectionView.collectionViewLayout as!UICollectionViewFlowLayout).itemSizeassetGridThumbnailSize = CGSize(width: cellSize.width * scale, height: cellSize.height * scale)}override func viewDidLoad() {super.viewDidLoad()self.collectionView.dataSource = selfself.collectionView.delegate = self// 背景色设置为白色(默认是黑色)self.collectionView.backgroundColor = UIColor.white// 初始化和重置缓存self.imageManager = PHCachingImageManager()self.resetCachedAssets()// 设置单元格尺寸let layout = (self.collectionView.collectionViewLayout as!UICollectionViewFlowLayout)layout.itemSize = CGSize(width: UIScreen.main.bounds.size.width/4-1,height: UIScreen.main.bounds.size.width/4-1)// 允许多选self.collectionView.allowsMultipleSelection = true// 添加导航栏右侧的取消按钮let rightBarItem = UIBarButtonItem(title: "取消", style: .plain,target: self, action: #selector(cancel))self.navigationItem.rightBarButtonItem = rightBarItem// 添加下方工具栏的完成按钮completeButton = AlbumImageCompleteButton()completeButton.addTarget(target: self, action: #selector(finishSelect))completeButton.center = CGPoint(x: UIScreen.main.bounds.width - 50, y: 22)completeButton.isEnabled = falsetoolBar.addSubview(completeButton)}// 重置缓存func resetCachedAssets() {self.imageManager.stopCachingImagesForAllAssets()}// 取消按钮点击func cancel() {// 退出当前视图控制器self.navigationController?.dismiss(animated: true, completion: nil)}// 获取已选择个数func selectedCount() -> Int {return self.collectionView.indexPathsForSelectedItems?.count ?? 0}// 完成按钮点击func finishSelect(){// 取出已选择的图片资源var assets:[PHAsset] = []if let indexPaths = self.collectionView.indexPathsForSelectedItems{for indexPath in indexPaths{assets.append(assetsFetchResults[indexPath.row] )}}// 调用回调函数self.navigationController?.dismiss(animated: true, completion: {self.completeHandler?(assets)})}override func didReceiveMemoryWarning() {super.didReceiveMemoryWarning()}}
// 图片缩略图集合页控制器UICollectionViewDataSource,UICollectionViewDelegate协议方法的实现
extension AlbumImageCollectionViewController: UICollectionViewDelegate, UICollectionViewDataSource {// CollectionView项目func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {return self.assetsFetchResults.count}// 获取单元格func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {// 获取storyboard里设计的单元格,不需要再动态添加界面元素let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! AlbumImageCollectionViewCelllet asset = self.assetsFetchResults[indexPath.row]// 获取缩略图self.imageManager.requestImage(for: asset, targetSize: assetGridThumbnailSize, contentMode: .aspectFill, options: nil) {(image, _) incell.imageView.image = image}return cell}// 单元格选中响应func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {if let cell = collectionView.cellForItem(at: indexPath) as? AlbumImageCollectionViewCell {// 获取选中的数量let count = self.selectedCount()// 如果选择的个数大于最大选择数if count > self.maxSelected {// 设置为不选中状态collectionView.deselectItem(at: indexPath, animated: false)// 弹出提示let title = "你最多只能选择\(self.maxSelected)张照片"let alertController = UIAlertController(title: title, message: nil, preferredStyle: .alert)let cancelAction = UIAlertAction(title:"我知道了", style: .cancel, handler:nil)alertController.addAction(cancelAction)self.present(alertController, animated: true, completion: nil)}// 如果不超过最大选择数else{// 改变完成按钮数字,并播放动画completeButton.num = countif count > 0 && !self.completeButton.isEnabled{completeButton.isEnabled = true}cell.playAnimate()}}}// 单元格取消选中响应func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {if let cell = collectionView.cellForItem(at: indexPath) as? AlbumImageCollectionViewCell {// 获取选中的数量let count = self.selectedCount()completeButton.num = count// 改变完成按钮数字,并播放动画if count == 0{completeButton.isEnabled = false}cell.playAnimate()}}
}

至此,可以在ViewController里使用这个选择图片的控件了:

import UIKit
import Photosclass ViewController: UIViewController, UIImagePickerControllerDelegate,UINavigationControllerDelegate {// 存放图片的数组var imageArray = [UIImage]()// 存放图片等collectionviewvar collectionView: UICollectionView!// 最大图片张数let maxImageCount = 9// 添加图片按钮var addButton: UIButton!// 选择上传图片方式弹窗var addImageAlertViewController: UIAlertController!// 缩略图大小var imageSize: CGSize!override func viewDidLoad() {super.viewDidLoad()// 设置背景色self.view.backgroundColor = UIColor.groupTableViewBackground// 设置添加图片按钮相关属性addButton = UIButton(type: UIButtonType.custom)addButton.setTitle("添加图片", for: UIControlState.normal)addButton.addTarget(self, action: #selector(addItem(_:)), for: UIControlEvents.touchUpInside)addButton.backgroundColor = UIColor.init(red: 164 / 255, green: 193 / 255, blue: 244 / 255, alpha: 1)addButton.frame = CGRect(x: 20, y: 35, width: 100, height: 25)addButton.layer.masksToBounds = trueaddButton.layer.cornerRadius = 8.0// 设置collection的layoutlet layout = UICollectionViewFlowLayout()layout.itemSize = CGSize(width: 50, height: 50)// 列间距layout.minimumInteritemSpacing = 10// 行间距layout.minimumLineSpacing = 10// 偏移量layout.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10)// 设置collectionview的大小、背景色、代理、数据源collectionView = UICollectionView(frame: CGRect(x: 10, y: 60, width: 200, height: self.view.frame.size.height), collectionViewLayout: layout)collectionView.backgroundColor = UIColor.groupTableViewBackgroundcollectionView.delegate = selfcollectionView.dataSource = self// 注册cellcollectionView.register(PhotoCollectionViewCell.self, forCellWithReuseIdentifier: "photoCell")// 选择上传照片方式的弹窗设置addImageAlertViewController = UIAlertController(title: "请选择上传方式", message: "相册或者相机", preferredStyle: UIAlertControllerStyle.actionSheet)let cancelAction = UIAlertAction(title: "取消", style: UIAlertActionStyle.cancel, handler: nil)let cameraAction = UIAlertAction(title: "拍照", style: UIAlertActionStyle.default, handler: {(action: UIAlertAction) in self.cameraAction()})let albumAction = UIAlertAction(title: "从相册选择", style: UIAlertActionStyle.default, handler: {(action: UIAlertAction) in self.albumAction()})self.addImageAlertViewController.addAction(cancelAction)self.addImageAlertViewController.addAction(cameraAction)self.addImageAlertViewController.addAction(albumAction)self.view.addSubview(addButton)self.view.addSubview(collectionView)}override func viewWillAppear(_ animated: Bool) {// 获取缩略图的大小let cellSize = (self.collectionView.collectionViewLayout as! UICollectionViewFlowLayout).itemSizeself.imageSize = cellSize}// MARK: - 删除图片按钮监听方法func removeItem(_ button: UIButton) {// 数据变更self.collectionView.performBatchUpdates({self.imageArray.remove(at: button.tag)let indexPath = IndexPath(item: button.tag, section: 0)let arr = [indexPath]self.collectionView.deleteItems(at: arr)}, completion: {(completion) inself.collectionView.reloadData()})// 判断是否使添加图片按钮生效if imageArray.count < 9 {self.addButton.isEnabled = trueself.addButton.backgroundColor = UIColor.init(red: 164 / 255, green: 193 / 255, blue: 244 / 255, alpha: 1)}}// MARK: - 添加图片按钮监听方法func addItem(_ button: UIButton) {self.present(addImageAlertViewController, animated: true, completion: nil)}// MARK: - 拍照监听方法func cameraAction() {if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.camera) {// 创建图片控制器let picker = UIImagePickerController()// 设置代理picker.delegate = self// 设置来源picker.sourceType = UIImagePickerControllerSourceType.camera// 允许编辑picker.allowsEditing = true// 打开相机self.present(picker, animated: true, completion: { () -> Void in})}else {// 弹出提示let title = "找不到相机"let alertController = UIAlertController(title: title, message: nil, preferredStyle: .alert)let cancelAction = UIAlertAction(title:"我知道了", style: .cancel, handler:nil)alertController.addAction(cancelAction)self.present(alertController, animated: true, completion: nil)}}// MARK: - 相册监听方法func albumAction() {// 可选照片数量let count = maxImageCount - imageArray.count// 开始选择照片,最多允许选择count张_ = self.presentAlbumImagePicker(maxSelected: count) { (assets) in// 结果处理for asset in assets {// 从asset获取imagelet image = self.PHAssetToUIImage(asset: asset)// 数据变更self.collectionView.performBatchUpdates({let indexPath = IndexPath(item: self.imageArray.count, section: 0)let arr = [indexPath]self.collectionView.insertItems(at: arr)self.imageArray.append(image)}, completion: {(completion) inself.collectionView.reloadData()})// 判断是否使添加图片按钮失效if self.imageArray.count > 8 {self.addButton.isEnabled = falseself.addButton.backgroundColor = UIColor.darkGray}}}}// MARK: - 将PHAsset对象转为UIImage对象func PHAssetToUIImage(asset: PHAsset) -> UIImage {var image = UIImage()// 新建一个默认类型的图像管理器imageManagerlet imageManager = PHImageManager.default()// 新建一个PHImageRequestOptions对象let imageRequestOption = PHImageRequestOptions()// PHImageRequestOptions是否有效imageRequestOption.isSynchronous = true// 缩略图的压缩模式设置为无imageRequestOption.resizeMode = .none// 缩略图的质量为高质量,不管加载时间花多少imageRequestOption.deliveryMode = .highQualityFormat// 按照PHImageRequestOptions指定的规则取出图片imageManager.requestImage(for: asset, targetSize: self.imageSize, contentMode: .aspectFill, options: imageRequestOption, resultHandler: {(result, _) -> Void inimage = result!})return image}// MARK: - 相机图片选择器func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {// 将相机刚拍好的照片拿出来let gotImage = info[UIImagePickerControllerOriginalImage] as! UIImage// 数据变更self.collectionView.performBatchUpdates({let indexPath = IndexPath(item: self.imageArray.count, section: 0)let arr = [indexPath]self.collectionView.insertItems(at: arr)self.imageArray.append(gotImage)print(self.imageArray.count)}, completion: {(completion) inself.collectionView.reloadData()})// 判断是否使添加图片按钮失效if imageArray.count > 8 {self.addButton.isEnabled = falseself.addButton.backgroundColor = UIColor.darkGray}// 关闭此页面self.dismiss(animated: true, completion: nil)}override func didReceiveMemoryWarning() {super.didReceiveMemoryWarning()// Dispose of any resources that can be recreated.}}// MARK: - collection代理方法实现
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {// 每个区的item个数func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {return imageArray.count}// 分区个数func numberOfSections(in collectionView: UICollectionView) -> Int {return 1}// 自定义cellfunc collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "photoCell", for: indexPath) as! PhotoCollectionViewCellcell.imageView.image = imageArray[indexPath.item]cell.button.addTarget(self, action: #selector(removeItem(_:)), for: UIControlEvents.touchUpInside)cell.button.tag = indexPath.rowreturn cell}// 是否可以移动func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool {return true}
}

参考:
相册图片多选功能的实现
Swift中将PHAsset对象转为UIImage对象
整个项目源码地址

用Swift实现iOS相机及相册图片上传相关推荐

  1. iOS 多选相册图片上传,添加、删除图片

    通知:在使用下边的方法的时候出现的问题总结,猿友们看到后请自行更正. 问题一:有人反映打开本演示的时候第一次打不开相册,必须重新启动一下才可以打开. 此问题是由于初始化ZYQAssetPickerCo ...

  2. iOS学习:调用相机,选择图片上传,带预览功能

    iOS学习:调用相机,选择图片上传,带预览功能 发表于2年前(2013-05-30 21:38)   阅读( 18194) | 评论( 16) 27人收藏此文章,我要收藏 赞3 8月22日珠海 OSC ...

  3. 选择相册图片上传后,客户端显示图片却是被旋转过角度

    我想某位大哥也遇到过这个问题,就是手机相册选择图片时,显示是正的(未被旋转角度的),但是上传该图片后,客户端却显示被旋转了xx度的.那这肯定是客户不想出现的问题,同样也是我们开发者不想出现bug.我想 ...

  4. 微信小程序开发之从相册获取图片 使用相机拍照 本地图片上传

    今天遇到微信小程序的用户头像设置功能,做笔记. 先上gif: 再上代码: 小demo,代码很简单. 1.index.wxml <!--index.wxml--> <button st ...

  5. iOS-打开照相机打开本地相册图片上传

    ResourceFrom:http://www.xuanyusong.com/archives/1493 Hello 大家好 IOS的文章好久都木有更新了,今天更新一篇哈. 这篇文章主要学习如何在IO ...

  6. Ajax安卓成功iOS失败,api.ajax图片上传,ios可以上传成功,安卓上传失败

    本帖最后由 孤鹜恋落霞 于 2019-4-10 18:02 编辑 function uploadImg(token, urlParams, imgSrc, callback) { var time = ...

  7. H5实现拍照及相册图片上传

    最近在做一个H5的小型电商项目,其中有一个是现金刷卡之后需要上传凭证图片的,因此也就需要在H5中实现可以上传图片. 我们都知道,input标签的type为file是可以上传图片的,本来想着这么简单,有 ...

  8. Android实现拍照相册图片上传功能

    更改头像功能不像修改信息一样直接提交参数就可以,需要上传图片文件 我就直接贴代码了首先给出布局文件 <ImageViewandroid:id="@+id/iv"android ...

  9. (复习)android 调用系统相机 图库 裁剪-图片上传-客服端-服务器

    引用转载http://www.cnblogs.com/eyu8874521/archive/2012/07/20/2600697.html 效果: 客服端代码: package com.cn.lxz. ...

最新文章

  1. 手机超广角拍摄软件_桂林好的拍摄短视频手机软件
  2. Android--判断发送短信后的状态/发送成功Or发送失败
  3. Elasticsearch-06 Spring Boot 2.0.9整合ElasticSearch5.6.16
  4. 安装好的nginx安装新的模块
  5. IT:银行类金融科技岗笔试习题集合—各大行(工商+建设+农业+浦发+招商+平安+人民+邮政银行)计算机信息科技岗笔试集合(包括计算机基础知识+网络+操作系统+数据库系统原理)
  6. 德云斗笑社何九华为什么没参加_江西省会为什么是南昌?
  7. Windows MobileCE 开发书籍大全
  8. [html] h5页面如何传递参数给小程序?
  9. Tablestore + Blink实战:交易数据的实时统计
  10. Blink 有何特别之处?菜鸟供应链场景最佳实践
  11. Android 替换字体的方式以及同时替换多种字体
  12. 系统学习机器学习之非参数方法
  13. MongoDB 3.2+ 安全授权登录访问控制
  14. 文章编辑数据结构课程设计
  15. oracle没有网卡驱动,联想台式机网卡驱动,手把手教你联想台式机网卡驱动
  16. 苹果发布 iOS 14 Beta 8 iphone 14 GM要来咯
  17. Android开发中,如何从系统固件里提取可用的APK
  18. 2022年9月最新【国际版阿里云的注册流程】分享
  19. [面经整理] 机器/深度学习补充篇
  20. 数据结构:排序 思维导图

热门文章

  1. Jmeter启动报错returned error code 5,解决方法
  2. 基于深度学习的目标检测论文综述Paper Reading:A Survey of Deep Learning-based Object Detection(中文)
  3. 【附源码】Python计算机毕业设计怦然心动网上服装商城
  4. X射线电子能谱原理及软件图谱处理
  5. vue+ElementUI实现文件的单个下载,以及批量压缩下载
  6. java8的stream中的toMap
  7. 如何快速的学好英语(Aprial)
  8. 微信与财付通接口签名调试总结
  9. 【笔记】数据库高级操作 给数据添加约束
  10. 心怀梦想奋力前行_心怀不满的风投公司起诉初创八卦网站