相册图片多选功能的实现

自定义多选imagePicker 说一下简易版自定义多选imagePicker,只用UIImagePickerController,没用到AV Foundation framework和Photos framework。 阅读详情
使用  UIImagePickerController,我们可以很方便的从系统相册中选择照片。但  UIImagePickerController 每次只能选择一张图片,不支持多选。这样如果我们需要一次上传多张图片到服务器,使用  UIImagePickerController 效率就会很低。
本文演示如何实现一个多选组件,类似微信发朋友圈那样,可以一次打勾选择多张照片。

一、组件介绍

1,实现原理

(1)我们通过  Photos 框架( PhotoKit)读取出所有图片数据,并使用自定义的  collection view 将缩略图显示出来,同时实现相关的多选功能。
(2)相簿列表同样是通过  PhotoKit 读取出所有相簿信息,并使用  tableview 展示出来。

2,效果图

(1)点击“ 开始选择照片”按钮,会弹出我们自定义的图片多选组件。默认显示出“ 相机胶卷”里的所有照片。
    原文:Swift - 相册图片多选功能的实现       原文:Swift - 相册图片多选功能的实现

(2)点击导航栏左上角的“ 相簿”按钮,即可列出设备中所有相簿,点击便显示出该相簿中的所有照片。
    原文:Swift - 相册图片多选功能的实现       原文:Swift - 相册图片多选功能的实现

(3)点击图片缩略图即可在选中和取消选中状态间切换,同时图片右上角的打勾图标在状态改变时也会有弹性变化效果。
原文:Swift - 相册图片多选功能的实现

(4)选择图片的同时下方工具栏也会实时显示出选中的图片数量,这个改变时同样有动画效果。如果超过限制(可用设置),则会弹出消息提示。
    原文:Swift - 相册图片多选功能的实现       原文:Swift - 相册图片多选功能的实现

(5)点击“ 完成”按钮,退出图片选择组件。这里我们在在回调方法中把选中的结果给打印出来。
原文:Swift - 相册图片多选功能的实现

二、组件介绍

整个组件的代码结构如下,下面分别进行介绍。
原文:Swift - 相册图片多选功能的实现

1,相簿列表相关

(1) HGImagePickerController.swift(相簿列表页控制器)
  • 注意最下面,为方便使用,我扩展了 UIViewController,添加了个 presentHGImagePicker() 方法,调用后便会出现照片选择组件。
  • 虽然上面方法调用时加载的是相簿列表视图。但一开始时会先加载相机胶卷图片,并自动跳转到图片选择视图。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
import  UIKit
import  Photos
 
//相簿列表项
struct  HGImageAlbumItem  {
     //相簿名称
     var  title: String ?
     //相簿内的资源
     var  fetchResult: PHFetchResult < PHAsset >
}
 
//相簿列表页控制器
class  HGImagePickerController :  UIViewController  {
     //显示相簿列表项的表格
     @IBOutlet  weak  var  tableView: UITableView !
     
     //相簿列表项集合
     var  items:[ HGImageAlbumItem ] = []
     
     //每次最多可选择的照片数量
     var  maxSelected: Int  =  Int . max
     
     //照片选择完毕后的回调
     var  completeHandler:((_ assets:[ PHAsset ])->())?
     
     //从xib或者storyboard加载完毕就会调用
     override  func  awakeFromNib() {
         super .awakeFromNib()
         
         //申请权限
         PHPhotoLibrary .requestAuthorization({ (status)  in
             if  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  in
                 return  item1.fetchResult.count > item2.fetchResult.count
             }
             
             //异步加载表格数据,需要在主线程中调用reloadData() 方法
             DispatchQueue .main.async{
                 self .tableView?.reloadData()
                 
                 //首次进来后直接进入第一个相册图片展示页面(相机胶卷)
                 if  let  imageCollectionVC =  self .storyboard?
                     .instantiateViewController(withIdentifier:  "hgImageCollectionVC" )
                     as ?  HGImageCollectionViewController {
                     imageCollectionVC.title =  self .items.first?.title
                     imageCollectionVC.assetsFetchResults =  self .items.first?.fetchResult
                     imageCollectionVC.completeHandler =  self .completeHandler
                     imageCollectionVC.maxSelected =  self .maxSelected
                     self .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(cancel) )
         self .navigationItem.rightBarButtonItem = rightBarItem
     }
     
     //转化处理获取到的相簿
     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 = titleOfAlbumForChinse(title: c.localizedTitle)
                 items.append( HGImageAlbumItem (title: title,
                                               fetchResult: assetsFetchResult))
             }
         }
     }
     
     //由于系统返回的相册集名称为英文,我们需要转换为中文
     private  func  titleOfAlbumForChinse(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  cancel() {
         //退出当前视图控制器
         self .dismiss(animated:  true , completion:  nil )
     }
     
     //页面跳转
     override  func  prepare( for  segue:  UIStoryboardSegue , sender:  Any ?) {
         //如果是跳转到展示相簿缩略图页面
         if  segue.identifier ==  "showImages" {
             //获取照片展示控制器
             guard  let  imageCollectionVC = segue.destination
                 as ?  HGImageCollectionViewController ,
                 let  cell = sender  as ?  HGImagePickerCell  else {
                 return
             }
             //设置回调函数
             imageCollectionVC.completeHandler = completeHandler
             //设置标题
             imageCollectionVC.title = cell.titleLabel.text
             //设置最多可选图片数量
             imageCollectionVC.maxSelected =  self .maxSelected
             guard   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  HGImagePickerController : UITableViewDelegate , UITableViewDataSource {
     //设置单元格内容
     func  tableView(_ tableView:  UITableView , cellForRowAt indexPath:  IndexPath )
         ->  UITableViewCell  {
         //同一形式的单元格重复使用,在声明时已注册
         let  cell = tableView.dequeueReusableCell(withIdentifier:  "cell" ,  for : indexPath)
             as !  HGImagePickerCell
         let  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  {
     //HGImagePicker提供给外部调用的接口,同于显示图片选择页面
     func  presentHGImagePicker(maxSelected: Int  =  Int . max ,
                               completeHandler:((_ assets:[ PHAsset ])->())?)
         ->  HGImagePickerController ?{
         //获取图片选择视图控制器
         if  let  vc =  UIStoryboard (name:  "HGImage" , bundle:  Bundle .main)
             .instantiateViewController(withIdentifier:  "imagePickerVC" )
             as ?  HGImagePickerController {
             //设置选择完毕后的回调
             vc.completeHandler = completeHandler
             //设置图片最多选择的数量
             vc.maxSelected = maxSelected
             //将图片选择视图控制器外添加个导航控制器,并显示
             let  nav =  UINavigationController (rootViewController: vc)
             self .present(nav, animated:  true , completion:  nil )
             return  vc
         }
         return  nil
     }
}

(2) HGImagePickerCell.swift(相簿列表单元格)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import  UIKit
 
//相簿列表单元格
class  HGImagePickerCell :  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)
     }
}


2,图片选择页面相关

(1) HGImageCollectionViewController.swift(图片缩略图集合页控制器)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
import  UIKit
import  Photos
 
//图片缩略图集合页控制器
class  HGImageCollectionViewController :  UIViewController  {
     //用于显示所有图片缩略图的collectionView
     @IBOutlet  weak  var  collectionView: UICollectionView !
     
     //下方工具栏
     @IBOutlet  weak  var  toolBar: UIToolbar !
 
     //取得的资源结果,用了存放的PHAsset
     var  assetsFetchResults: PHFetchResult < PHAsset >?
     
     //带缓存的图片管理对象
     var  imageManager: PHCachingImageManager !
     
     //缩略图大小
     var  assetGridThumbnailSize: CGSize !
     
     //每次最多可选择的照片数量
     var  maxSelected: Int  =  Int . max
     
     //照片选择完毕后的回调
     var  completeHandler:((_ assets:[ PHAsset ])->())?
     
     //完成按钮
     var  completeButton: HGImageCompleteButton !
     
     override  func  viewWillAppear(_ animated:  Bool ) {
         super .viewWillAppear(animated)
         
         //根据单元格的尺寸计算我们需要的缩略图大小
         let  scale =  UIScreen .main.scale
         let  cellSize = ( self .collectionView.collectionViewLayout  as !
             UICollectionViewFlowLayout ).itemSize
         assetGridThumbnailSize =  CGSize (width: cellSize.width*scale ,
                                         height: cellSize.height*scale)
     }
 
     override  func  viewDidLoad() {
         super .viewDidLoad()
         
         //背景色设置为白色(默认是黑色)
         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 =  HGImageCompleteButton ()
         completeButton.addTarget(target:  self , action: #selector(finishSelect))
         completeButton.center =  CGPoint (x:  UIScreen .main.bounds.width - 50, y: 22)
         completeButton.isEnabled =  false
         toolBar.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)
         })
     }
}
 
//图片缩略图集合页控制器UICollectionViewDataSource,UICollectionViewDelegate协议方法的实现
extension  HGImageCollectionViewController : UICollectionViewDataSource
, UICollectionViewDelegate {
     //CollectionView项目
     func  collectionView(_ collectionView:  UICollectionView ,
                         numberOfItemsInSection section:  Int ) ->  Int  {
         return  self .assetsFetchResults?.count ?? 0
     }
     
     // 获取单元格
     func  collectionView(_ collectionView:  UICollectionView ,
                         cellForItemAt indexPath:  IndexPath ) ->  UICollectionViewCell  {
         //获取storyboard里设计的单元格,不需要再动态添加界面元素
         let  cell = collectionView.dequeueReusableCell(withReuseIdentifier:  "cell" ,
                                     for : indexPath)  as !  HGImageCollectionViewCell
         let  asset =  self .assetsFetchResults![indexPath.row]
         //获取缩略图
         self .imageManager.requestImage( for : asset, targetSize: assetGridThumbnailSize,
                                        contentMode: .aspectFill, options:  nil ) {
                                         (image, nfo)  in
             cell.imageView.image = image
         }
         
         return  cell
     }
     
     //单元格选中响应
     func  collectionView(_ collectionView:  UICollectionView ,
                         didSelectItemAt indexPath:  IndexPath ) {
         if  let  cell = collectionView.cellForItem(at: indexPath)
             as ?  HGImageCollectionViewCell {
             //获取选中的数量
             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 = count
                 if  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 ?  HGImageCollectionViewCell {
              //获取选中的数量
             let  count =  self .selectedCount()
             completeButton.num = count
              //改变完成按钮数字,并播放动画
             if  count == 0{
                 completeButton.isEnabled =  false
             }
             cell.playAnimate()
         }
     }
}

(2) HGImageCollectionViewCell.swift(图片缩略图集合页单元格)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import  UIKit
 
//图片缩略图集合页单元格
open  class  HGImageCollectionViewCell :  UICollectionViewCell  {
     //显示缩略图
     @IBOutlet  weak  var  imageView: UIImageView !
     
     //显示选中状态的图标
     @IBOutlet  weak  var  selectedIcon: UIImageView !
     
     //设置是否选中
     open  override  var  isSelected:  Bool  {
         didSet {
             if  isSelected {
                 selectedIcon.image =  UIImage (named:  "hg_image_selected" )
             } else {
                 selectedIcon.image =  UIImage (named:  "hg_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 )
     }
     
     open  override  func  awakeFromNib() {
         super .awakeFromNib()
         imageView.contentMode = .scaleAspectFill
         imageView.clipsToBounds =  true
     }
}

(3) HGImageCompleteButton.swift(照片选择页下方工具栏的“完成”按钮)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import  UIKit
 
//照片选择页下方工具栏的“完成”按钮
class  HGImageCompleteButton :  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 =  false
                 numLabel.text =  "\(num)"
                 playAnimate()
             }
         }
     }
     
     //是否可用
     var  isEnabled: Bool  =  true  {
         didSet {
             if  isEnabled {
                 titleLabel.textColor = titleColor
                 tapSingle?.isEnabled =  true
             } else {
                 titleLabel.textColor =  UIColor .gray
                 tapSingle?.isEnabled =  false
             }
         }
     }
     
     init (){
         super . init (frame:defaultFrame)
     
         //已选照片数量标签初始化
         numLabel =  UILabel (frame: CGRect (x: 0 , y: 0 , width: 20, height: 20))
         numLabel.backgroundColor = titleColor
         numLabel.layer.cornerRadius = 10
         numLabel.layer.masksToBounds =  true
         numLabel.textAlignment = .center
         numLabel.font =  UIFont .systemFont(ofSize: 15)
         numLabel.textColor =  UIColor .white
         numLabel.isHidden =  true
         self .addSubview(numLabel)
         
         //按钮标题标签初始化
         titleLabel =  UILabel (frame: CGRect (x: 20 , y: 0 ,
                                           width: defaultFrame.width - 20,
                                           height: 20))
         titleLabel.text =  "完成"
         titleLabel.textAlignment = .center
         titleLabel.font =  UIFont .systemFont(ofSize: 15)
         titleLabel.textColor = titleColor
         self .addSubview(titleLabel)
     }
     
     required  init ?(coder aDecoder:  NSCoder ) {
         super . init (coder: aDecoder)
     }
     
     //用户数字改变时播放的动画
     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 )
     }
     
     //添加事件响应
     func  addTarget(target:  Any ?, action:  Selector ?) {
         //单击监听
         tapSingle =  UITapGestureRecognizer (target:target,action:action)
         tapSingle!.numberOfTapsRequired = 1
         tapSingle!.numberOfTouchesRequired = 1
         self .addGestureRecognizer(tapSingle!)
     }
}

3,相关资源

(1) HGImage.xcassets
里面就两个图标图片,分别表示选中和未选中状态。
原文:Swift - 相册图片多选功能的实现

(2) HGImage.storyboard
  • 在 storyboard 中添加两个 View Controller Scene,分别绑定上面的 HGImagePickerController 和 HGImageCollectionViewController。
  • 同时两个 Scene 中的 cell 分别绑定 HGImagePickerCell 和 HGImageCollectionViewCell。
  • Image Picker Controller Scene 的 Storyboard ID 设置为 imagePickerVC。单元格 identifier 设置为 cell。
  • Image Collection View Controller Scene 的 Storyboard ID 设置为 hgImageCollectionVC。单元格 identifier 设置为 cell。
  • 从 Image Picker Controller Scene 的单元格拖动一个 Segue 到 Image Collection View Controller Scene。类型为 Show,identifier 为 showImages。
原文:Swift - 相册图片多选功能的实现

三、使用样例

1,Info.plist配置

由于苹果安全策略更新,在使用  Xcode8 开发时,需要在  Info.plist 配置请求照片相的关描述字段( Privacy - Photo Library Usage Description)
原文:Swift - 相册图片多选功能的实现

2,样例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import  UIKit
 
class  ViewController :  UIViewController  {
 
     override  func  viewDidLoad() {
         super .viewDidLoad()
     }
 
     //“开始选择照片”按钮点击
     @IBAction  func  buttonTapped(_ sender:  AnyObject ) {
         //开始选择照片,最多允许选择4张
         _ =  self .presentHGImagePicker(maxSelected:4) { (assets)  in
             //结果处理
             print ( "共选择了\(assets.count)张图片,分别如下:" )
             for  asset  in  assets {
                 print (asset)
             }
         }
     }
     
     override  func  didReceiveMemoryWarning() {
         super .didReceiveMemoryWarning()
     }
}
原文链接: http://www.hangge.com/blog/cache/detail_1512.html
TZImagePickerController:支持iOS6-9的多选图片和视频选择器组件 本文还有配套的精品资源,点击获取 简介: TZImagePickerController是一款为iOS平台开发的图片和视频选择器,支持多选、原图选择及预览功能,并全面适配iOS6到iOS9系统。它通过Objective-C编写,并遵循UIKit框架,通过自定义代理协议简化开发者在应用中集成该组件的复杂度。TZImagePickerController为开发者提供了丰富的自定... 阅读详情

相关推荐

Android多选图片自定义相册功能实战Demo

在移动应用中,相册功能是用户非常喜爱和常用的功能之一。通过自定义相册,我们可以实现更丰富的用户交互和更个性化的展示方式。自定义相册的基本需求包括:浏览和预览图片、多选图片、图片加载和性能优化、动态权限申请等。自定义相册的基本框架包括:UI界面设计、图片列表的布局和样式设计、图片加载和预览的实现、多选图片功能的实现、图片选择结果的保存和返回等。在搭建框架时,我们需要考虑框架的可扩展性和兼容性。

weixin_42612405的博客 589

iOS学习笔记-024.UIImagePickerController图片选择

UIImagePickerController图片选择 一简介 二代码示例 三代理返回的info信息 四图示UIImagePickerController图片选择.一、简介使用UIImagePickerController可以选择照片 照片的来源UIImagePickerControllerSourceType包括: - 照片库 - 照相机 - 保存的照片使用步骤 1. 判断照片来源是否可

qiwenmingshiwo的专栏 607

解决linux 2.5G网卡驱动问题

解决linux 2.5G网卡驱动问题 关键词:2.5G网卡驱动,ubuntu,安装,2.5G Ethernet LINUX driver,Realtek网卡驱动 由于2.5G网卡较新,各linux发行版的通用驱动已不适用,新装的系统会出现无法上网问题,由于国内网络环境去Realtek官网很难下载到驱动文件,故本文提供github上的2.5G Ethernet LINUX driver r8125 for kernel up to 5.6的版本驱动文件r8125-9.003.05.tar.bz2各位可以自

poorguy_aos的博客 1万+

UIImagePickerController 的部分用法详解

概述 不能定制界面,不可派生子类。 使用步骤: 检查制定源是否可用. isSourceTypeAvailable:方法. 检查可用媒体(视频还是只能是图片) availableMediaTypesForSourceType:方法. 设置界面媒体属性 mediaTypes property. 显示界面使用 presentViewController:animated:co

drmi的专栏 1889

单细胞转录组分析(scRNA-seq)

单细胞转录组测序(Single-cell RNA sequencing,scRNA-seq)是一种高通量测序技术,可以在单细胞水平解析基因表达情况,揭示细胞异质性,探索稀有细胞类型,并研究细胞状态转换及动态调控机制。与传统的。

大甘的博客 6148

中微8S6990实战:5分钟搞定GPIO配置与按键中断(附避坑指南)

本文以中微8S6990单片机为例,详细讲解了GPIO按键中断的快速配置方法与实战避坑指南。内容涵盖GPIO初始化、中断配置、软件消抖优化及低功耗唤醒等关键步骤,并提供了利用逻辑分析仪进行调试验证的有效方法,帮助开发者快速构建稳定可靠的嵌入式按键交互系统。

weixin_29194817的博客 280

iOS开发利用ELCImagePicker第三方实现图片的多选

iOS开发利用ELCImagePicker第三方实现图片的多选

秦晓康 4350

Innovus 安装全过程

【代码】Innovus 安装全过程。

lmyynlbcy的博客 5534

实现功能:从相册多选图片,再对图片进行拖动排序、预览、删除,类似微信朋友圈发布图片功能

该文章实现一个简单功能,先从相册中选择图片到图片展示页面,展示页面可对图片进行拖动排序、直接删除、预览删除。(多图选择使用三方框架TZImagePickerController,大图预览使用第三方框架YBImageBrowser) 一、单张图片Cell:XPYImagePickerCollectionViewCell (1)XPYImagePickerCollectionViewCel...

xianglinping的博客 1241

A*(star)算法原理讲解以及程序实现(基于Python)

本文将以迷宫探索最优路径为例,讲解A *搜索算法原理及其程序实现。在原理讲解部分,首先从为什么要使用A *搜索算法和A *搜索算法的全局最优逻辑出发,讲解A *算法的原理。其次对A *的行动函数g(n)和启发函数h(n)的细节进行了讲解说明,并补充了启发函数的选择对于A *算法的影响。在程序实现部分,首先从程序流程框图出发,解释A *算法的流程。其次按函数的类划分并讲解主要代码,接着展示程序的运行结果,最后对A *算法进行总结分析。

m0_73366745的博客 8281

android ios 相册图片多选,iOS实现相册多选图片上传功能

本文实例为大家分享了iOS实现相册多选图片上传的具体代码,供大家参考,具体内容如下原理:获取手机里的全部照片,显示在自定义的视图里//获取到相册的所有图片- (void)addAllPhotos{@WeakObj(self);_assetsLibrary=[[ALAssetsLibrary alloc]init];[_assetsLibrary enumerateGroupsWithTypes:A...

weixin_34669783的博客 280

20、Citrix 云桌面常见VDA注册问题汇总

本文总结了与 VDA 中出现的 VDA 会话断开连接、注册、会话卡住以及挂起/崩溃相关的主要支持文章。

我在Citrix、VMware、Veeam、微软和NVIDIA等领域拥有丰富经验的专业人士,专注于设计和维护虚拟化基础设施、实施备份与灾难恢复策略,致力于优化企业的IT环境。 943

模仿仿微信发表朋友圈图片多选实现

模仿微信的图片选择界面,具有图片浏览,选择相册,图片预览,图片缩放等,选中多个图片。

Assur的博客 2977

强化学习(Q-learning、DQN) —— 理论、案例与交互式 GUI 实现

本文详细介绍了强化学习中基于值函数的 Q-learning 算法及深度强化学习的 DQN 方法,从基本理论、数学模型到算法流程均进行了全面阐述。通过迷宫导航、股票交易策略和机器人控制等典型案例,展示了强化学习在处理高维、非线性和时变问题中的强大适应性。为了帮助工程师和研究者更直观地理解强化学习的训练过程,本文还提供了一份基于 Python 与 PyQt6 实现的交互式 GUI 演示系统代码示例,用户可以在线调整参数,实时观察累计奖励和 Q 值的变化,从而更深入地理解算法收敛和策略演化的过程。

闲人编程的博客 1475

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

android+获取相册列表,android 获取相册列表的实现(二)

该项目实现的功能如下: 获取手机相册,点击每个相册之后进入该相册的图片列表界面,在图片列表界面可以实现图片多选,然后进入所选择的图片界面,在该界面内可该项目实现的功能如下:获取手机相册,点击每个相册之后进入该相册的图片列表界面,在图片列表界面可以实现图片多选,然后进入所选择的图片界面,在该界面内可以实现所选图片的上传等功能。该项目最大特色:1、获取相册列表,目前网络上面介绍获取相册的项目很少,本文...

weixin_36485371的博客 1312

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

android 获取相册列表的实现及源码下载(三)

该项目实现的功能如下: 获取手机相册,点击每个相册之后进入该相册的图片列表界面,在图片列表界面可以实现图片多选,然后进入所选择的图片界面,在该界面内可以实现所选图片的上传等功能。 该项目最大特色: 1、获取相册列表,目前网络上面介绍获取相册的项目很少,本文专门讲述相册的获取。 2、使用Android-Universal-Image-Loader集成框架-第三方jar包加载本地图片,熟

nyyin@qq.com 4051

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

iOS 相册多选 相机选择图片

前言经过几天的断断续续的编写终于把这一个小项目完成了,现在刚刚完成,代码看着不整洁,请多包涵。 前几天要弄个相册多选和照相选图的功能,以前做过单选上传头像之类的。但是多选确实不像那么简单,github找了好多的例子,都是在用几个框架。不是说人家封的不好,封的很好,但是卤煮比较笨,看了好久还是马马虎虎。然后上网查了下资料,还是决定自己写一个。demo传送门在此,哈哈哈正文多选主要是需要一个frame

猿人在北京 6587

iOS 使用photokit框架获取系统相册,实现多选,全选。

获取系统相册一共两种方式,系统框架为ALAssetsLibrary和photokit,其中photokit需系统在iOS8.0以后,ALAssetsLibrary这个推荐使用ELCImagePickerController,photokit推荐使用VOKMultiImagePicker-iOS,不过两个demo都存在一个共同的问题,就是选择多张图片的时候,在返回的代理方法中,使用block回调去取

qq_28699375的博客 3595

android高仿微信拍照、多选、预览、删除(去除相片)相冊功能

先声明授人与鱼不如授人与渔,仅仅能提供一个思路,当然须要源代码的同学能够私下有偿问我要源代码:QQ:508181017 工作了将近三年时间了,一直没正儿八经的研究系统自带的相冊和拍照,这回来个高仿微信的拍照、多选、预览、删除(去除相片)相冊功能, 之前开发的全部应用都带有这需求,可是一直都不有用!废话就不多说了,先来捋一下思路: 1、拍照能实时保存到本地并实时查询(不...

weixin_30733003的博客 134

UITableView 相册-判断多选相册的位置

相册页面实现如下多选功能 我实现的时候使用了 UITableView @interface PTASysAlbumVC : BaseViewController<UITableViewDelegate,UITableViewDataSource,PTSysAssetPickerControllerDelegate,PhotoAlbumClickedDelegate>{...

weixin_30826095的博客 130

iOS打开相册多选、图片浏览器、图片裁剪

参考:https://www.cnblogs.com/cqb-learner/p/5792292.html https://www.jianshu.com/p/140f8996279e 资源类 PHPhotoLibrary 是一个资源库。能够获取相册权限以及对相册的操作,与AL不同,它不能获取资源对象哦. PHFetchResult 是一个结果集,一个泛型类。通过方法获取到的相册或者资源组...

代码解释生活 1959

IOS多选单选相册图片

之前做项目让实现多选相册的图片,自己写了一个demo一直保存在电脑上,今天下午发现电脑128G的容量已经快没有了,准备清理电脑,所以把之前做的一些demo放在博客上,以后方便用。 1.首先准备3个图片 2.定义单元格PhoCollectionViewCell #import <UIKit/UIKit.h> typedef void(...

weixin_34071713的博客 149

ios 使用ELCImagePicker实现相册照片多选和真机拍照调试

iOS ELCImagePicker三方库 真机拍照

张树的博客 1017
上一篇: 多section分区的collectionView
下一篇: 隐藏状态栏(statusBar)的两种方法
FlyingFireFish
博客等级 码龄9年 23粉丝 57原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值