11.0相册读取权限 ios

2020-04-02 18:01:34 u013756604 阅读数 89

常用类介绍

  1. PHPhotoLibrary:该类用于表示设备和iCloud上所有的收藏和资源。可以使用一个共享实例以一种线程安全的方法对照片库的变化进行管理,比如添加新的资源和相簿,或者编辑和删除已有的资源或相簿,此外,共享实例还可以注册一个关于照片库发生变化的监听对象,以实现用户界面的同步。

  2. PHAssetCollection:该类一般用于表示一组照片或者视频》可以在设备上本地创建,可以从iPhone中同步照片,可以从相册或保存照片的相簿中获取图片,或者从根据一定的约束条件得到的智能相簿中(比如全景照片)同步图片。该类还提供以组的方式访问资源。资源集合可以以集合列表的方式进行组合。

  3. PHAsset:该类用于表示照片或者视频的元数据。它提供了一些类方法,返回使用了相同约束条件的资源结果,提供带资源信息的实例方法,比如日期,位置,类型,和方向之类的信息。

  4. PHFetchResult:这是一个轻量级的对象,用于表示一组资源或者资源集合。当按照约束条件请求资源或者资源集合时m,类方法将会返回一个抓取结构(fetch result)。抓取结果会按照需求载入资源 而不是一次将所有资源载入内存,这样即使面对大资源也能很好的加以处理,抓取结果同时还是线程安全的,这意味着如果底层数据发生变化,对象的个数不会变。可以为照片库的变化注册一个请求通知类,这样变化通过PHFetchResultChangeDetails 实例发送,可以用于更新抓取结果和所有的相应的用户界面

  5. PHImageManager:图片管理器以异步的方式处理图片数据的抓取和保存,尤其适用于获取指定尺寸的图片或者管理从iCloud获取的图片数据。此外,当需要在表现图或者集合视图中显示大量资源时m,照片库还提供PHCachingImageManager 类来提升视图滑动的流畅度。

PHAssetCollectionType 和PHAssetCollectionSubtype 介绍

public enum PHAssetCollectionType : Int {
 case album       /// 自己创建的相册
 
 case smartAlbum  /// 经由系统相机得来的相册
 
 case moment      /// 自动生成的时间分组的相册
 }
 
 
 public enum PHAssetCollectionSubtype : Int {
 
 
 // PHAssetCollectionTypeAlbum regular subtypes
 case albumRegular             // 在iPhone中自己创建的相册
 
 case albumSyncedEvent         // 从iPhoto(就是现在的图片app)中导入图片到设备
 
 case albumSyncedFaces         // 从图片app中导入的人物照片
 
 case albumSyncedAlbum         // 从图片app导入的相册
 
 case albumImported            // 从其他的相机或者存储设备导入的相册
 
 
 // PHAssetCollectionTypeAlbum shared subtypes
 case albumMyPhotoStream    // 照片流,照片流和iCloud有关,如果在设置里关闭了iCloud开关,就获取不到了
 
 case albumCloudShared      // iCloud的共享相册,点击照片上的共享tab创建后就能拿到了,但是前提是你要在设置中打开iCloud的共享开关(打开后才能看见共享tab)
 
 
 // PHAssetCollectionTypeSmartAlbum subtypes
 case smartAlbumGeneric      //一般的照片
 
 case smartAlbumPanoramas    // 全景图、全景照片
 
 case smartAlbumVideos       // 视频
 
 case smartAlbumFavorites    // 标记为喜欢、收藏
 
 case smartAlbumTimelapses   // 延时拍摄、定时拍摄
 
 case smartAlbumAllHidden    // 隐藏的
 
 case smartAlbumRecentlyAdded  // 最近添加的、近期添加
 
 case smartAlbumBursts         // 连拍
 
 case smartAlbumSlomoVideos    // 慢动作视频
 
 case smartAlbumUserLibrary    // 相机胶卷
 
 @available(iOS 9.0, *)
 case smartAlbumSelfPortraits    // 使用前置摄像头拍摄的作品
 
 @available(iOS 9.0, *)
 case smartAlbumScreenshots      // 屏幕截图
 
 @available(iOS 10.2, *)
 case smartAlbumDepthEffect      // 使用深度摄像模式拍的照片
 
 @available(iOS 10.3, *)
 case smartAlbumLivePhotos       //Live Photo资源
 
 @available(iOS 11.0, *)
 case smartAlbumAnimated
 
 @available(iOS 11.0, *)
 case smartAlbumLongExposures
 
 // Used for fetching, if you don't care about the exact subtype
 case any
 }

请求相册和相机权限


 /*使用说明

 需要在Info.plist 中添加

 Privacy - Photo Library Additions Usage Description 添加图片

 Privacy - Photo Library Usage Description  读取图片*/
import Foundation
import Photos
import AVFoundation

public enum PhotoAuthorizationStatus:String{
    case photoNotDetermined = "用户尚未对相册权限做出选择"
    case photoRestricted    = "用户无权访问相册"
    case photoDenied        = "用户拒绝访问相册"
    case photoAuthorized    = "用户允许访问相册"
}

public enum CameraAuthorizationStatus:String{
    case cameraNotDetermined = "用户尚未对相机权限做出选择"
    case cameraRestricted    = "用户无权访问相机"
    case cameraDenied        = "用户拒绝访问相机"
    case cameraAuthorized    = "用户允许访问相机"
}


struct AuthorizationManager{
    public static var photoStatus:PhotoAuthorizationStatus{
        let status = PHPhotoLibrary.authorizationStatus()
        var photoStatus: PhotoAuthorizationStatus = .photoNotDetermined
        switch status {
        case .notDetermined:
            photoStatus = .photoNotDetermined
        case .restricted:
            photoStatus = .photoRestricted
        case .denied:
            photoStatus = .photoDenied
        case .authorized:
            photoStatus = .photoAuthorized
        @unknown default:
            photoStatus = .photoNotDetermined
        }
        return photoStatus
    }
    
    
    public static var cameraStatus:CameraAuthorizationStatus{
        let status =  AVCaptureDevice.authorizationStatus(for: .video)
        var cameraStatus: CameraAuthorizationStatus = .cameraNotDetermined
        switch status {
        case .notDetermined:
            cameraStatus = .cameraNotDetermined
        case .restricted:
            cameraStatus = .cameraRestricted
        case .denied:
            cameraStatus = .cameraDenied
        case .authorized:
            cameraStatus = .cameraAuthorized
        @unknown default:
            cameraStatus = .cameraNotDetermined
        }
        return cameraStatus
    }
    
    public static func checkPhotoAuthorization(complition:@escaping (PhotoAuthorizationStatus)->Void){
        if photoStatus == .photoAuthorized{
            complition(.photoAuthorized)
        }
        requestPhotoAuthorization(complition: complition)
    }
    
    
    public static func requestPhotoAuthorization(complition:@escaping (PhotoAuthorizationStatus)->Void){
        var photoStatus:PhotoAuthorizationStatus = .photoNotDetermined
        PHPhotoLibrary.requestAuthorization { (status) in
            switch status{
            case .notDetermined:
                photoStatus = .photoNotDetermined
            case .restricted:
                photoStatus = .photoRestricted
            case .denied:
                photoStatus = .photoDenied
            case .authorized:
                photoStatus = .photoAuthorized
            @unknown default:
                photoStatus = .photoNotDetermined
            }
            DispatchQueue.main.async {
                complition(photoStatus)
            }
        }
    }
    
    public  static func checkCameraAuthorization(complition:@escaping (CameraAuthorizationStatus)->Void){
        if cameraStatus == .cameraAuthorized{
            complition(.cameraAuthorized)
        }
        requestCameraAuthorization(complition: complition)
    }
    
    public static func requestCameraAuthorization(complition:@escaping (CameraAuthorizationStatus)->Void){
        var cameraStatus: CameraAuthorizationStatus = .cameraNotDetermined
        AVCaptureDevice.requestAccess(for: .video) { (isSuccess) in
            if isSuccess{
                cameraStatus = .cameraAuthorized
            }else{
                cameraStatus = .cameraDenied
            }
            DispatchQueue.main.async {
               complition(cameraStatus)
            }
        }
    }
    
}


获取相册的图片

 
import UIKit
import Photos
open class LQPhotoManager:NSObject {
    public weak var delegate:LQPhotoManagerDelegate?
    public init(photoDelegate:LQPhotoManagerDelegate?){
        delegate = photoDelegate
        super.init()
        PHPhotoLibrary.shared().register(self)
    }
    
    public func unregisterChangeObserver(){
        PHPhotoLibrary.shared().unregisterChangeObserver(self)
    }

    public func requestPhotosData(){
        AuthorizationManager.checkPhotoAuthorization { (status) in
            self.notifyObserver(status: status)
        }
    }
}



/*
 可以使用PHChnage下面的方法去监听发生的变化
 public func changeDetails<T>(for fetchResult: PHFetchResult<T>) -> PHFetchResultChangeDetails<T>? where T : PHObject
 
 通过PHFetchResultChangeDetails的下面的发生的变化
 //
 open var fetchResultBeforeChanges: PHFetchResult<ObjectType> { get }
 
 open var fetchResultAfterChanges: PHFetchResult<ObjectType> { get }
 */
public protocol LQPhotoManagerDelegate:AnyObject{
    func photosDataDidChange(changeInstance: PHChange)
    func photosAuthorizationChange(status:PhotoAuthorizationStatus)
}

extension LQPhotoManager{
    /// 获取相册
    ///
    /// - Parameters:
    ///   - type: 相册类型
    ///   - subtype: 哪种类型拍摄的
    ///   - options: 筛选条件
    public class func getAlbum(with type: PHAssetCollectionType, subtype: PHAssetCollectionSubtype, options: PHFetchOptions? = nil)-> PHFetchResult<PHAssetCollection>{
        return PHAssetCollection.fetchAssetCollections(with: PHAssetCollectionType.album, subtype: PHAssetCollectionSubtype.any, options: options)
    }
    
    
    
    /// 创建相册
    ///
    /// - Parameter albumName: 相册的名字
    public class  func createAlbum(albumName:String?) {
        guard let name = albumName else{
            return
        }
        PHPhotoLibrary.shared().performChanges({
            PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: name)
        }) { (success, error) in
            if !success{
                print("创建相册失败 错误信息:\(error?.localizedDescription ?? "没有错误信息")")
            }
        }
    }
    
    /// 删除多个相册
    ///
    /// - Parameter albumsToBeDeleted: [PHAssetCollection]?
    public class func deleteAlbums(albumsToBeDeleted: [PHAssetCollection]?) {
        guard let albums = albumsToBeDeleted else {
            return
        }
        PHPhotoLibrary.shared().performChanges({
            PHAssetCollectionChangeRequest.deleteAssetCollections(albums as NSFastEnumeration)
        }) { (success, error) in
            if !success{
                print("删除多个相册失败 错误信息:\(error?.localizedDescription ?? "没有错误信息")")
            }
        }
    }
    
    /// 获取相册中的照片
    ///
    /// - Parameter album: PHAssetCollection
    /// - Returns: PHFetchResult<PHAsset>
    public class func getPhotosFromAlbum(album:PHAssetCollection?) -> PHFetchResult<PHAsset>? {
        guard let al = album else {
            return nil
        }
        return PHAsset.fetchAssets(in: al, options: nil)
    }
    
    
    
    /// 将图片加入相册
    ///
    /// - Parameters:
    ///   - image: UIImage
    ///   - toAlbum: PHAssetCollection
    public class func addImage(image:UIImage?,toAlbum:PHAssetCollection?){
        guard let img = image,let album = toAlbum  else {
            return
        }
        
        PHPhotoLibrary.shared().performChanges({
            let addImageRequest = PHAssetChangeRequest.creationRequestForAsset(from: img)
            let addedImagePlaceholder = addImageRequest.placeholderForCreatedAsset
            let addImageToAlbum = PHAssetCollectionChangeRequest.init(for: album)
            addImageToAlbum?.addAssets([addedImagePlaceholder] as NSFastEnumeration)
        }) { (success, error) in
            if !success{
                print("将图片加入相册失败 错误信息:\(error?.localizedDescription ?? "没有错误信息")")
            }
        }
    }
    
    
    /// 将照片从相册中删除
    ///
    /// - Parameter photoAssets: [PHAsset]
    public class func deleteImagesFromAlbum(photoAssets:[PHAsset]?){
        guard let photos = photoAssets else {
            return
        }
        
        PHPhotoLibrary.shared().performChanges({
            PHAssetChangeRequest.deleteAssets(photos as NSFastEnumeration)
        }) { (success, error) in
            if !success{
                print("将图片加入相册失败 错误信息:\(error?.localizedDescription ?? "没有错误信息")")
            }
        }
    }
    
    
    /// 将PHAsset 转成Image
    ///
    /// - Parameters:
    ///   - photoAsset: PHAsset
    ///   - imageSize:  图片尺寸
    ///   - contentMode: 拉伸模式
    ///   - resultHandler: 转换成功后的回调
    public class func changePHAssetToImage(photoAsset:PHAsset?,contentMode:PHImageContentMode = PHImageContentMode.aspectFill,resultHandler: @escaping (UIImage?, [AnyHashable : Any]?) -> Void) {
        guard let photoA = photoAsset else {
            return
        }
        
        let imageManager =  PHImageManager.default()
        imageManager.requestImage(for: photoA, targetSize: CGSize.init(width: photoA.pixelWidth, height: photoA.pixelHeight), contentMode: contentMode, options: nil) { (image, imageInfo) in
            resultHandler(image,imageInfo)
        }
    }
    
    internal func notifyObserver(status:PhotoAuthorizationStatus){
        DispatchQueue.main.async {
            self.delegate?.photosAuthorizationChange(status: status)
        }
    }
}


extension LQPhotoManager:PHPhotoLibraryChangeObserver{
    public func photoLibraryDidChange(_ changeInstance: PHChange) {
        DispatchQueue.main.async {
            self.delegate?.photosDataDidChange(changeInstance: changeInstance)
        }
    }
}

2017-10-20 11:34:00 weixin_30905133 阅读数 94

http://www.jianshu.com/p/c03a87e4ca87

 

大家都知道访问相册需要申请用户权限。 相册权限需要在info.plist—Property List文件中添加NSPhotoLibraryUsageDescription键值对,描述文字不能为空。 iOS11之前:访问相册和存储照片到相册(读写权限),需要用户授权,需要添加NSPhotoLibraryUsageDescription。
iOS11之后:默认开启访问相册权限(读权限),无需用户授权,无需添加NSPhotoLibraryUsageDescription,适配iOS11之前的还是需要加的。 添加图片到相册(写权限),需要用户授权,需要添加NSPhotoLibraryAddUsageDescription

总结:iOS11之后如果需要写入权限需要添加
NSPhotoLibraryAddUsageDescription字段。  
   NSPhotoLibraryUsageDescription:
Property List:
   Privacy - Photo Library Usage Description
Source Code:
  NSPhotoLibraryUsageDescription
是否允许此APP使用相册?
   NSPhotoLibraryAddUsageDescription :
Property List:
   Privacy - Photo Library Additions Usage Description
Source Code:
   NSPhotoLibraryAddUsageDescription
是否允许此APP保存图片到相册?

苹果官方文档说明:

https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW73

NSPhotoLibraryAddUsageDescription

NSPhotoLibraryAddUsageDescription (String - iOS) This key lets you describe the reason your app seeks write-only access to the user’s photo library. When the system prompts the user to allow access, this string is displayed as part of the alert.

Important: To protect user privacy, an iOS app linked on or after iOS 10.0, and that accesses the user’s photo library, must statically declare the intent to do so. Include the NSPhotoLibraryAddUsageDescription key (in apps that link on or after iOS 11) or NSPhotoLibraryUsageDescription key in your app’s Info.plist file and provide a purpose string for the key. If your app attempts to access the user’s photo library without a corresponding purpose string, your app exits.

 

This key is supported in iOS 11.0 and later.

NSPhotoLibraryUsageDescription

NSPhotoLibraryUsageDescription (String - iOS) This key lets you describe the reason your app accesses the user’s photo library. When the system prompts the user to allow access, this string is displayed as part of the alert.

Although this keys governs read and write access to the user’s photo library, it’s best to use NSPhotoLibraryAddUsageDescription if your app needs only to add assets to the library and does not need to read any assets.

Important: To protect user privacy, an iOS app linked on or after iOS 10.0, and that accesses the user’s photo library, must statically declare the intent to do so. Include the NSPhotoLibraryUsageDescription key in your app’s Info.plist file and provide a purpose string for this key. If your app attempts to access the user’s photo library without a corresponding purpose string, your app exits.

 

This key is supported in iOS 6.0 and later.

 



作者:小孩仔
链接:http://www.jianshu.com/p/c03a87e4ca87
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

转载于:https://www.cnblogs.com/huangzizhu/p/7698648.html

2019-05-14 18:42:02 Morris_ 阅读数 2442

iOS文件上传功能

									Morris_
									2019.05.14

demo下载

搭建本地php环境

在本地搭建一个php服务,在iOS端进行图片、文件等上传测试。

一、搭建服务

常用命令

查看本地php版本
php - v

在本机上开一个服务,并且指定工作目录。局域网内用户都可访问。
php -S 0.0.0.0:9999 -t /Users/Document/php/

监听该服务
Listening on http://0.0.0.0:9999

开启服务
php -S 0.0.0.0:9999 -t /Users/Document/php/

//关闭服务
Ctrl + c

搭建一个本地的php服务

1、搭建php环境

php - v 查看php环境版本,我是下载了个软件,在电脑上默认装了php环境,具体怎么装还得看专业的。

2、本地新建一个php工作文件夹

我在/Users/xxx/Documents/php/下创建了文件夹upload,则工作空间是在/Users/xxx/Documents/php/upload/下。

3、在upload文件夹下创建一个文件,改名字为upload_file.php保存

即创建一个php文件,在文件中写如下php代码

<?php

ini_set('memory_limit',0);
ini_set('post_max_size','1024G');
ini_set('upload_max_filesize','1000G');
set_time_limit(0);

move_uploaded_file($_FILES["file"]["tmp_name"],"/Users/xxx/Documents/php/upload/".$_FILES["file"]["name"]);
exit(json_encode([
    "status"=>200
]));

设置上传文件大小限制。

move_uploaded_file()这个方法是下载文件,并将文件move操作。

上传上来的文件是uploaded,会到电脑的/tmp目录下,move操作将其转移到upload目录下。上传的文件首先是会短暂的存储到电脑的/tmp文件中,如果不做处理,生命周期结束后,/tmp中文件将被删除。

上传成功后有个返回码200。

4、开启服务

终端中进入工作目录upload文件下:cd /Users/xxx/Documents/php/upload

开启服务并指定当前服务的工作区间:php -S 0.0.0.0:9999 -t /Users/xxx/Documents/php/upload/

完成

然后就OK了,局域网内的用户都可访问,域名是本地ip+/upload.php: http://172.16.11.223:9999/upload.php

二、上传文件

选取手机相册里的图片、拍照、选取iCloud里面的文件,进行上传。

基础知识

先看一些基础知识。

  • 打开相册

先在plist文件中添加相册使用权限申请的key和value。

- (IBAction)photoABtnClick:(UIButton *)sender {
    UIImagePickerController *picker = [[UIImagePickerController alloc] init];
    //如果需要编辑图片,建议sourceType选择UIImagePickerControllerSourceTypeSavedPhotosAlbum,如果不需要,可选择UIImagePickerControllerSourceTypePhotoLibrary。
    picker.allowsEditing = YES;
    picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
    picker.delegate = self;
    [self presentViewController:picker animated:YES completion:nil];
}
  • 打开相机

先在plist文件中添加相机使用权限申请的key和value。

- (IBAction)cameraBtnClick:(UIButton *)sender {
    UIImagePickerController *picker = [[UIImagePickerController alloc] init];
    picker.allowsEditing = YES;
    picker.sourceType = UIImagePickerControllerSourceTypeCamera;
    picker.delegate = self;
    [self presentViewController:picker animated:YES completion:nil];
}
  • 打开iCloud
- (IBAction)iCloudBtnClick:(UIButton *)sender {
    if (![DocUploadViewController ICloudEnable]) {
        NSLog(@"iCloud没有开启");
        return;
    }
    NSArray *documentTypes = @[@"public.content",
                               @"public.text",
                               @"public.source-code",
                               @"public.image",
                               @"public.jpeg",
                               @"public.png",
                               @"com.adobe.pdf",
                               @"com.apple.keynote.key",
                               @"com.microsoft.word.doc",
                               @"com.microsoft.excel.xls",
                               @"com.microsoft.powerpoint.ppt"];
    UIDocumentPickerViewController *picker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:documentTypes inMode:UIDocumentPickerModeOpen];
    picker.delegate = self;
    [self presentViewController:picker animated:YES completion:nil];
}

使用iCloud前先判断是否有权限:

+ (BOOL)ICloudEnable {
    NSURL *url = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
    return url != nil;
}

UIImagePickerController和UIDocumentPickerViewController是系统的UIKit框架下的两个类。在使用iCloud前需要先打开App的iCloud使用权限,请阅读 Enabling CloudKit in Your App

  • 压缩图片的方法
UIImageJPEGRepresentation(image, 0.8)
  • 获取相册回调返回的图片URL
 NSURL *url = nil;
 if (@available(iOS 11.0, *)) {
     url = info[UIImagePickerControllerImageURL];
 } else {
     url = info[UIImagePickerControllerReferenceURL];
 }
  • 获取选择相册/拍照的照片
 //获取经过编辑后的图片
 UIImage *image = info[UIImagePickerControllerEditedImage];
 if (!image) {
 //如果未编辑,取原图
 image = info[UIImagePickerControllerOriginalImage];
 }
  • 将图片保存到相册
 //保存到相册
 [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
    [PHAssetChangeRequest creationRequestForAssetFromImage:iamge];
 } completionHandler:^(BOOL success, NSError * _Nullable error) {
 
 }];

PHPhotoLibrary是系统的Photos框架下的一个类,使用需引头文件#import <Photos/Photos.h>。

performChanges相当于打开相册,PHAssetChangeRequest *request = [PHAssetChangeRequest creationRequestForAssetFromImage:image];是将图片写入相册,通过request.placeholderForCreatedAsset.localIdentifier可读取写入后的文件id。

  • 获取相册图片信息
 //获取图片信息
 PHFetchResult *result = [PHAsset fetchAssetsWithLocalIdentifiers:@[locolId] options:nil];
 PHAsset *asset = [result firstObject];
 [[PHImageManager defaultManager] requestImageDataForAsset:asset options:nil resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
 
    NSLog(@"==>%@",info);
 
 }];

这里的locolId就是保存在相册里的文件identifier。

  • 读取iCoud文件信息
	 //创建doc
    WeCDocument *document = [[WeCDocument alloc] initWithFileURL:documentsURL];
    
    //打开doc
    [document openWithCompletionHandler:^(BOOL success) {
        if (success)
        {
        
        }
        else
        {
        
        }
        //关闭doc
        [document closeWithCompletionHandler:^(BOOL success) {
            
        }];
    }];

由于CompletionHandler这个block里对document持有一份引用,多以document对象不会被直接释放掉,等回调过来后需要关闭doc,document对象才会被释放。

这里ICDocument是继承UIDocument的类,实现如下:

@interface WeCDocument : UIDocument

@property (nonatomic, strong, nullable) NSData *data;
//文件名
@property (nonatomic, copy, nullable) NSString *fileName;
//文件类型
@property (nonatomic, copy, nullable) NSString *MIMEType;

@end

@implementation WeCDocument

- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError * _Nullable __autoreleasing *)outError
{
    //.page等文件会闪退
    if ([contents isKindOfClass:[NSData class]])
    {
        self.data = [contents copy];
        self.fileName = self.fileURL.lastPathComponent;
        if (self.fileName && self.fileName.length) {
            NSRange startRange = [self.fileName rangeOfString:@"."];
            self.MIMEType = [self.fileName substringFromIndex:startRange.location];
        }
    }
    else {
        NSLog(@"读取文件出错!");
        return NO;
    }
    return YES;
}

@end

这里通过documentURL去获取的文件名和类型。

信息回调

  • UIImagePickerControllerDelegate

选择相册文件或者拍照后,回调方法:

//selected
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    //此处需要先dismiss掉picker,然后再present出alert,佛否则alert显示不出,bug
    [picker dismissViewControllerAnimated:YES completion:nil];
    
    //获取经过编辑后的图片
    UIImage *image = info[UIImagePickerControllerEditedImage];
    if (!image) {
        //如果未编辑,取原图
        image = info[UIImagePickerControllerOriginalImage];
    }
    
    if (picker.sourceType == UIImagePickerControllerSourceTypeSavedPhotosAlbum)
    {
        NSURL *url = nil;
        if (@available(iOS 11.0, *)) {
            url = info[UIImagePickerControllerImageURL];
        } else {
            url = info[UIImagePickerControllerReferenceURL];
        }
        [self selectedImage:image url:url reName:nil];
    }
    else if (picker.sourceType == UIImagePickerControllerSourceTypeCamera)
    {
        __block NSString *locolId = nil;
        
        [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
            //保存到相册
            PHAssetChangeRequest *request = [PHAssetChangeRequest creationRequestForAssetFromImage:image];
            locolId = request.placeholderForCreatedAsset.localIdentifier;
        } completionHandler:^(BOOL success, NSError * _Nullable error) {
            if (error == nil) {
                //获取图片信息
                PHFetchResult *result = [PHAsset fetchAssetsWithLocalIdentifiers:@[locolId] options:nil];
                PHAsset *asset = [result firstObject];
                [[PHImageManager defaultManager] requestImageDataForAsset:asset options:nil resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
                    
                    NSURL *url = info[@"PHImageFileURLKey"];
                    [self selectedImage:image url:url reName:nil];
                }];
            }
            else {
                NSLog(@"图片保存失败!");
            }
        }];
    }
}
//cancel
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
    [picker dismissViewControllerAnimated:YES completion:nil];
}
  • UIDocumentPickerDelegate
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray <NSURL *>*)urls
{
    [self selectedDocumentAtURLs:urls reName:nil];
}
- (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller
{
    [controller dismissViewControllerAnimated:YES completion:nil];
}
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url
{
    [self selectedDocumentAtURLs:@[url] reName:nil];
}

iCloud选取文件后,会返回一个documentURL,即文件路径的URL。

上传

起初我想都通过传入url,读取文件,然后上传,后来发现了问题:NSURL有个属性isFileURL,iCloud获取的文件返回的documentURL的isFileURL是YES,这说明从iCloud获取的documentURL是文件URL。但是从相册或者拍照得到的文件的url并非是文件路径,它的isFileURL是NO。从documentURL里获取文件信息可以通过系统提供的类UIDocument来获取,但是相册和拍照的url不是文件url,使用UIDocument直接闪退,闪退信息:*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'must pass a valid file URL to -[UIDocument initWithFileURL:]'

看来获取文件信息,相册/拍照和iCloud是不同的。

后来分别提供了两个方法,一个上传图片,一个上传iCloud文件。

/**
 *  iCloud文件上传
 *  documentsURL 文件地址;rename 文件重命名,文件名不需带文件类型后缀
 */
+ (void)uploadICloudFileWithUrl:(NSURL *)documentsURL
                         reName:(nullable NSString *)rename
                       progress:(nullable void (^)(NSProgress * progress))uploadProgress
                         sucess:(void(^)(NSDictionary *responseObject))uploadSuccess
                        failure:(void(^)(NSError *error))uploadFailure;

/**
 图片上传,相册、拍照文件上传。
 url为referenceURL,相册UIImagePickerControllerReferenceURL,拍照PHImageFileURLKey,可为空,为空时不会上传源文件名称。
 rename,通过此值修改上传文件名称,文件名不需带文件类型后缀。
 */
+ (void)uploadImage:(UIImage *)image
       referenceURL:(nullable NSURL *)url
             reName:(nullable NSString *)rename
           progress:(nullable void (^)(NSProgress * progress))uploadProgress
             sucess:(void(^)(NSDictionary *responseObject))uploadSuccess
            failure:(void(^)(NSError *error))uploadFailure;

这块代码比较多,直接看demo吧,调试demo前先自己搭建好本地的php服务,修改请求地址,然后可以上传图片到本地服务。

疑问

  • 网上有关获取文件的MIMEType方法
-(NSString *)getMIMETypeURLRequestAtPath:(NSURL *)path
{
    NSURL *url = path;//[NSURL fileURLWithPath:path];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    NSHTTPURLResponse *response = nil;
    [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
    NSString *mimeType = response.MIMEType;
    return mimeType;
}

我很纳闷,这个是个异步的怎么去获取MIMEType,难道是等获取到MIMEType在做其他操作吗?

人云亦云?或者说我不知道怎么使用?

2018-04-03 11:22:34 u013896628 阅读数 9739

iOS不同版本升级的新特性:

iOS3.0

开发特性

一、点对点蓝牙连接

二、远程浏览内容

三、连接周边产品(硬件)

四、其它应用可以使用maps

五、定位软件

六、推送功能

七、网络电话(voip)

八、录音

九、播放音乐

十、增强了 app store

iOS4.0

开发特性

一、点对点蓝牙连接

二、远程浏览内容

三、连接周边产品(硬件)

四、其它应用可以使用maps

五、定位软件

六、推送功能

七、网络电话(voip)

八、录音

九、播放音乐

十、增强了 app store

iOS5.0

一、iCloud

iCloud存储API使程序可以将用户文档和关键数据存储到iCloud中,并同时将改动推送到用户所有的计算机和设备上这一切都是自动的

二、通知中心

三、新闻综述

使用News stand kit

四、自动引用计数(ARC)

五、深度集成twitter

六、Storyboard

七、AirPlay

   作为在 iOS 4.2 引入的新功能,AirPlay 能够将视频,音频和照片作为媒体流输入到 Apple TV 上。在 iOS 5 中,AirPlay 可以将 iPad 2 上的

   任何内容通过 Apple TV 无线镜像到 HDTV 上。您的应用程序会自动支持这一功能。您也可以调用额外的 API 从而在每个 HDTV 和 iPad 2    屏幕上显示不同的内容。在 iOS 5 中,使用了 AV Foundation 框架的应用程序可以通过 AirPlay 将视频和音频内容转成媒体流,并且            AirPlay现在支持通过 HTTP Live 媒体流发布加密的媒体流内容
  • 1
  • 2
  • 3
  • 4

八、Core Image

   您可以使用 Core Image 在镜头和编辑图片的应用程序中创建令人惊叹的效果。Core Image 是一个硬件加速的框架,使您可以轻松地增强    照片和视频。Core Image 提供了一些内置的滤镜,例如颜色效果,变形和变换等。同时它还包括一些高级功能如自动增强,消除红眼,       脸部识别等
  • 1
  • 2

九、GameCenter

   游戏中心在 iOS 5 中支持多人游戏中的回合制游戏。在回合制游戏中,玩家可以在方便的时候才做出游戏动作,而游戏中心会管理每一个   回合。游戏中心将自动通过通知中心发送一个推送通知到下一个游戏玩家。其他为开发者提供的功能还包括,添加玩家到现有的多人游     戏中,显示成就通知,每个排行榜支持不同的图标。
  • 1
  • 2

十、OpenGL ES

加了GLKit

十一 、iMessage

十二、新的工具

十三、无需pc

十四、模拟位置信息

iOS6.0

一、地图 抛弃了googlemap 使用了自家地图

二、深度社交网络集成 ios5时深度集成了twitter, ios6集成了facebook, sina weibo

三、passbook 和passkit

四、gamecenter 主要联机对战

五、提醒开放了向reminder里添加东西和从读取的api (event kit) 和标准用户界面

六、新的iap

能直接从itunes store 购买音乐

七、collection views 瀑布流布局

八、自动布局

iOS7.0

1、UIKIT 全新偏化风络

2、UIKIT 力学模型,增加UIDynamicItem委托

3、游戏支持加了自己的精灵系统 SpriteKit 2d游戏引擎 xcode 还提供创建粒子 系统和贴图工具

4、支持专门游戏硬件

5、点对点连接:用来发现和连接附近的设备并传输数,而这一切不需要有网络 连接(direct wifi (P2P Wifi)技术, 所以iOS7 的airdrop 需要 iPhone5 或最新iPad/iPad mini才支持,因为还依赖硬件)

6、Store Kit:IAP方面采用了新的订单系统,减少内购实现流程和内购破解。实现

对订单的本机验证

7、Inter-APPAudio 应用间的音频

同一设置不同应用之间发送midi指令和传送音频能力,比如在一个应用中使用

AudioUnit录音,然后在另一个应用中打以处理.

8、地图MapKit

MKMapCamera:可以将一个MKMapCamera对象加到地图上,在指明位置,角度和方向后将呈现3d效果

MKDirections

MKGeodesicPolyline

MKMapSnapshotter

改变了overlay对象的渲染方式

9、多任务

iOS7之前

后台完成某些费时间的特定任务

后台播放音乐

位置服务

ip电话

newsstand

iOS7

改变了后台任务的运行方式除网络外的后台任务,被分布到不同的应用唤醒系

统时执行.

网络传输的应用建议使用NSURLSessioin 创建后台的session并进行网格传输.

增加了后台获取 Background Fetch

应用打开前有机会执行代码来获取数取,刷新ui,省去了网络加载过程

增加了推送唤醒( 静默推送,Slient Remove Notifications)

增加了后台传输(Background Transfer service)

ios8.0 
1、iOS开发界面的统一

Size Classes 的引入和新的 Presentation 系统了

2、IB的重大改进,可提高开发效率,节省开发时间

3、 Cloud Kit 的框架的推出

4、Health Kit 和 Home Kit的引入

5、Scene Kit 的引入

6、本地通知也需要用户许可

7、CoreLocation 室内定位

8、Touch ID API简化了Paypal 或者支付宝这样的第三方支付和确认的流程的

9、新增加了 Photos.framework 框架

iOS9新特性

· iOS9系统发送的网络请求将统一使用HTTPs,将不再默认使用HTTP等不安全的网络协议,而默认采用TLS 1.2。服务器因此需要更新,以解析相关数据。如不更新,可通过在 info.plist 中声明,倒退回不安全的网络请求。

· 将允许出现这种场景:同一app中多个location manager:一些只能在前台定位,另一些可在后台定位

· bitcode的理解应该是把程序编译成的一种过渡代码,然后苹果再把这个过渡代码编译成可执行的程序。bitcode也允许苹果在后期重新优化我们程序的二进制文件,有类似于App瘦身的思想。

· stackView

· Multasking:多任务特性,三种形式

· 临时调出的滑动覆盖:Slide Over

视频播放的画中画模式(Picture in Picture)(AVPlayerViewController默认支持。MPMoviePlayerViewController被deprecated掉了,不支持) 

iPad真正同时使用两个App

· UI Test:iOS9.0之前加入异步代码测设和性能测试,可以说Xcode自带的测试框架已经能满足绝大部分单元测试的需求了,但是这并不够,因为开发一个iOS app从来都是很注重UI和用户体验的,之前UI测试使用KIF,Automating,iOS9.0的Xcode给出了自带的XCUITest的一系列工具,和大多数UI测试工具类似,XCUI使用Accessbility标记来确定view,但因为是Apple自家的东西,可以自动记录操作流程,所以只要书写最后的验证部分就好了,比其他UI测试工具方便多了

· Swift2

· APP Thinning:app为了后向兼容,都同时包含了32bit和64bit,在图片资源2X和3X的一应俱全,下载的时候只需要当前机型对应的一套资源,但是却要全部打包下载,现在只需要升级iOS9,就可以省很多流量

· 3D touch

· 地图显示实时的交通状况

· 人工智能siri更加智能,几个大城市的地铁及火车站入口都有详细的标识

· 手机电池的低功耗设置

· Spootlight,你的设备会向推荐最近通话过的联系人,使用过的APP以及你可能感兴趣的去处、信息呈现更精彩

iOS10新特性

· SiriKit 在 iOS 10 里面开发者可以使用 Siri SDK,这可能是 iOS 10 最重要的新 SDK之一。从此开发者可以使用原生API提供语音搜索、语音转文字消息甚至更多常见语音功能。

· Proactive Suggestions 貌似是一个和 CoreSpotlight 有整合的使用建议的东西。

· Message App Extension 在 iOS 10 里面开发者可以给 Message.app 提供两种 App Extension,分别是可以提供一个表情包,和一个自定义的界面,用于表情搜索等。

· User Notifications 这个 API 让你可以处理本地或远程的用户通知,并且可以基于某个条件,例如时间或者地理位置。这个异常强大,好像可以在通知里包含图片和视频了,貌似可以拦截并替换自己 app 发下来的 payload。

· Speech Recognition 见闻知意,语音识别 API,可以把音频流实时的转换为文本。虽说早期版本已经有了TTS语音转文字,但毕竟Siri语义识别的加入让机器对自然语义的把握更精准,详见Speech.framework

· App Search Enhancements 对 CoreSpotlight 的增强,其中我比较感兴趣的是 Visualization of validation results。

· Widget Enhancements 为了配合 iOS 10 锁屏下面 Widget 的体验,苹果提供了 widgetPrimaryVibrancyEffect 和 widgetSecondaryVibrancyEffect 用于定制化 Widget 的界面。

· CallKit callkit框架 VoIP应用程序集成与iPhone的通话界面,给用户一个很棒的体验,锁屏后VoIP网络电话可以直接用iPhone系统UI接听了。

· App Extensions 其实上面也有提到,iOS 10最重要的开发特点就是允许第三方应用对自带基础app的拓展关联, 全新 7 种 App Extension: 

Call Directory(VoIP回调)

Intents(接Siri、Apple map等服务)

Intents UI(接Siri、Apple map等服务的自定义界面)

Messages(iMessage拓展)

Notification Content(内容通知)

Notification Service (服务通知)

StickerPack(iMessage表情包)

· Custom Keyboard 对第三方键盘的改进 通过 handleInputModeListFromView:withEvent: 可以弹出系统键盘列表。同时使用 documentInputMode 可以检测输入上下文中的语言,你可以对输入方式进行一些类似于对齐方式的调整。

另外需要注意的是,和以往历代iOS版本推出一样,新陈代谢,有新SDK、新API的开放,也会有旧的API被遗弃,所以好好检查你的项目,使用了被遗弃的API要尽快修改,以免不兼容!还有个要注意的问题 iOS10 对隐私权限的管理更为严格 ,比如访问的摄像头、麦克风等硬件,都需要提前请求应用权限、允许后才可以使用,或者现在要提前声明,虽然以往要求不严格。


ios11新特性

1.UIKit Bars的改版

1.在浏览功能上的大标题视图(向上滑动后标题会回到原来的UI效果)、横屏状态下tab上的文字和icon会变为左右排列。

横屏.png

当然,如果图标和文字觉得太小了,可以长按item进行放大操作。具体只需要添加代码

放大.png

2. iOS11导航栏

设置导航栏大标题:navigationBar.prefersLargeTitles,显示的时机可以用navigationItem.largeTitleDisplayMode设置枚举值。

设置导航栏搜索控制器

searchController  :self.navigationItem.searchController = searchController

3.上面设置titleView为customView时出现宽度缩小的问题 (这个网上提到了这个问题,但是我自定义代码并没有出现这个情况)

3.调整相册权限的key

NSPhotoLibraryUsageDescription改为NSPhotoLibraryAddUsageDescription

。(beta版本好像又改过来了,我现在没有调整key也是可以访问相册的)

4.近场通讯

首先也要在info.plist配置NFCReaderUsageDescription

5.无线配备

6. 弃用API,scrollview和tableview的改变

iOS11弃用了automaticallyAdjustsScrollViewInsets

属性,新增contentInsetAdjustmentBehavior

来替代它。这可能使得一些刷新出现头部错乱。contentInsetAdjustmentBehavior其实是一个枚举值。用来管理adjustedContentInset

-automatic和scrollableAxes一样,scrollView会自动计算和适应顶部和底部的内边距并且在scrollView不可滚动时,也会设置内边距.

-scrollableAxes自动计算内边距.

-never不计算内边距

-always根据safeAreaInsets计算内边距

我们这里要设置为never,所以我们的OC代码可以这样写:

声明tableView的位置 添加下面代码

if(@available(iOS11.0, *)) {self.tableView.contentInsetAdjustmentBehavior =UIScrollViewContentInsetAdjustmentNever;self.tableView.contentInset =UIEdgeInsetsMake(0,0,0,0);self.tableView.scrollIndicatorInsets = _tableView.contentInset;        }

在iOS8引入Self-Sizing之后,我们可以通过实现estimatedRowHeight相关的属性来展示动态的内容。Self-Sizing在iOS11下是默认开启的,Headers,Footers, and cells都默认开启Self-Sizing,所有estimated高度默认值从iOS11之前的 0 改变为UITableViewAutomaticDimension.

如果目前项目中没有使用estimateRowHeight属性,在iOS11的环境下就要注意了,因为开启Self-Sizing之后,tableView是使用estimateRowHeight属性的,就会可能会使代理方法设置高度失效.

通过以下方法进行关闭:

Table Views:separatorInset 扩展

iOS 7 引入separatorInset属性,用以设置 cell 的分割线边距,在 iOS 11 中对其进行了扩展。可以通过新增的UITableViewSeparatorInsetReference枚举类型的separatorInsetReference属性来设置separatorInset属性的参照值。

typedefNS_ENUM(NSInteger,UITableViewSeparatorInsetReference) {UITableViewSeparatorInsetFromCellEdges,//默认值,表示separatorInset是从cell的边缘的偏移量UITableViewSeparatorInsetFromAutomaticInsets//表示separatorInset属性值是从一个insets的偏移量}

7.拖放

8.文件管理

9.tableView右滑操作

增加了两个代理方法来取代原来的代理方法(tableView: editActionsForRowAtIndexPath:)

// Swipe actions// These methods supersede -editActionsForRowAtIndexPath: if implemented- (nullableUISwipeActionsConfiguration*)tableView:(UITableView*)tableView leadingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath*)indexPath- (nullableUISwipeActionsConfiguration*)tableView:(UITableView*)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath*)indexPath

创建UIContextualAction对象时,UIContextualActionStyle有两种类型,如果是置顶、已读等按钮就使用UIContextualActionStyleNormal类型,delete操作按钮可使用UIContextualActionStyleDestructive类型,当使用该类型时,如果是右滑操作,一直向右滑动某个cell,会直接执行删除操作,不用再点击删除按钮。

10.Safe Area

以前做悬浮(支付,头部标题等),系统为我们提供了viewController的topLayoutGuide和bottomLayoutGuide方法进行约束,但是在iOS11中被废弃了,引用了一个新的概念。safeArea,它提供两种方式:safeAreaInsets或safeAreaLayoutGuide来提供给你safeArea的参照值,即insets或者layout guide。同时如果改变Insets时会获得两个回调

UIView.safeAreaInsetsDidChange()UIViewController.viewSafeAreaInsetsDidChange()

这个iOS的safe Area涉及到的第三方库有mansory和IQkeyboard所以可能这两个库也会进行一些适配

11.UIToolbar and UINavigationBar— Layout

在 iOS 11 中,当苹果进行所有这些新特性时,也进行了其他的优化,针对 UIToolbar 和 UINavigaBar 做了新的自动布局扩展支持,自定义的bar button items、自定义的title都可以通过layout来表示尺寸。 需要注意的是,你的constraints需要在view内部设置,所以如果你有一个自定义的标题视图,你需要确保任何约束只依赖于标题视图及其任何子视图。当你使用自动布局,系统假设你知道你在做什么。

12. 管理margins 和 insets

基于约束的Auto Layout,使我们搭建能够动态响应内部和外部变化的用户界面。Auto Layout为每一个view都定义了margin。margin指的是控件显示内容部分的边缘和控件边缘的距离。 可以用layoutMargins或者layoutMarginsGuide属性获得view的margin,margin是视图内部的一部分。layoutMargins允许获取或者设置UIEdgeInsets结构的margin。layoutMarginsGuide则获取到只读的UILayoutGuide对象。

在iOS11新增了一个属性:directional layout margins,该属性是NSDirectionalEdgeInsets结构体类型的属性:

typedefstructNSDirectionalEdgeInsets{CGFloattop, leading, bottom, trailing;}NSDirectionalEdgeInsetsAPI_AVAILABLE(ios(11.0),tvos(11.0),watchos(4.0));

layoutMargins是UIEdgeInsets结构体类型的属性:

typedefstructUIEdgeInsets{CGFloattop, left, bottom, right;}UIEdgeInsets;

从上面两种结构体的对比可以看出,NSDirectionalEdgeInsets属性用leading 和 trailing 取代了之前的 left 和 right。

directional layout margins属性的说明如下:

directionalLayoutMargins.leading is used on the left when the user interface direction is LTR and on the right for RTL.

Vice versa for directionalLayoutMargins.trailing.

例子:当你设置了trailing = 30;当在一个right to left 语言下trailing的值会被设置在view的左边,可以通过layout margins的left属性读出该值。

如下图所示:

还有其他一些更新。自从引入layout margins,当将一个view添加到viewController时,viewController会修复view的layoutMargins为UIKit定义的一个值,这些调整对外是封闭的。从iOS11开始,这些不再是一个固定的值,它们实际是最小值,你可以改变你的view的layoutMargins为任意一个更大的值。而且,viewController新增了一个属性:viewRespectsSystemMinimumLayoutMargins,如果你设置该属性为”false”,你就可以改变你的layout margins为任意你想设置的值,包括0。

2016-11-15 15:25:51 u013756604 阅读数 3884

#import "AllPhotoViewController.h"

#import <Photos/Photos.h>

#import "ImageViewController.h"

@interface AllPhotoViewController ()<UICollectionViewDelegate,UICollectionViewDataSource,UINavigationControllerDelegate,UIImagePickerControllerDelegate>

@property(nonatomic,strong)UICollectionView *collectionView;

@property(nonatomic,strong)NSMutableArray *imageArr;

@property(nonatomic,strong)NSMutableArray <PHAsset*> *assets;

@property(nonatomic,strong)UILabel *titleLabel;

@property(nonatomic,assign)CGSize size;

@property(nonatomic,strong)UIImagePickerController *imagePicker;

@end



@implementation AllPhotoViewController


- (void)viewDidLoad {

    [super viewDidLoad];

    self.view.backgroundColor = [UIColor whiteColor];

    [self createNav];

    [self createView];

    [self loadImage];

    

}

-(void)createNav{

    UIFont *font;

    CGRect frame;

    if ([[UIDevice currentDevice ]userInterfaceIdiom]==UIUserInterfaceIdiomPad) {

        font = [UIFont systemFontOfSize:11.0/375*ScreenWidth];

        frame = CGRectMake(0, 0, 8.0/375*ScreenWidth, 15.0/375*ScreenWidth);

    }else{

        font = [UIFont systemFontOfSize:16.0/375*ScreenWidth];

        frame = CGRectMake(0, 0, 15.0/375*ScreenWidth, 25.0/375*ScreenWidth);

    }

    self.titleLabel= [MyTools createLabelWithText:@"全部照片(0)" andFont:font andTextAlignment:1 andTextColor:[UIColor whiteColor]];

    self.titleLabel.frame = CGRectMake(0, 0, 100, 20);

    

    self.navigationItem.titleView = self.titleLabel ;

    

    UIButton *back = [MyTools createButtonWithFrame:frame andBgImageName:@"scanback"];

    [back addTarget:self action:@selector(goBack) forControlEvents:UIControlEventTouchUpInside];

    self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithCustomView:back];

    CGRect tframe;

    if (IsPad) {

        tframe =  CGRectMake(0, 0, 18.0/375*ScreenWidth, 13.0/375*ScreenWidth);

    }

    else{

        tframe =  CGRectMake(0, 0, 25.0/375*ScreenWidth, 18.0/375*ScreenWidth);

    }

    

    UIButton *takePhoto = [MyTools createButtonWithFrame:tframe andBgImageName:@"zhaoxiangji"];

    [takePhoto addTarget:self action:@selector(goTake) forControlEvents:UIControlEventTouchUpInside];

    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithCustomView:takePhoto];

    

}

-(void)goTake{

    [self presentViewController:self.imagePicker animated:YES completion:nil];

    

}



-(void)goBack{

    [self.navigationController popViewControllerAnimated:YES];

    

}

-(void)createView{

    self.imageArr = [NSMutableArray array];

    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc]init];

    layout.minimumLineSpacing = 10;

    layout.minimumInteritemSpacing = 10;

    

    if ([[UIDevice currentDevice]userInterfaceIdiom] == UIUserInterfaceIdiomPad) {

        self.size = CGSizeMake(50.0/375*ScreenWidth, 35.0/375*ScreenWidth);

    }else{

        self.size = CGSizeMake(100.0/375*ScreenWidth, 70.0/375*ScreenWidth);

    }

    layout.itemSize = self.size;

    self.collectionView  = [[UICollectionView alloc]initWithFrame:CGRectMake(10.0/375*ScreenWidth, 10.0/375*ScreenWidth, ScreenWidth -  20.0/375*ScreenWidth, ScreenHeight) collectionViewLayout:layout];

    self.collectionView.delegate = self;

    self.collectionView.dataSource = self;

    self.collectionView.backgroundColor = [UIColor whiteColor];

    self.collectionView.showsVerticalScrollIndicator = NO;

    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cell"];

    [self.view addSubview:_collectionView];

}




//从相册获取图片

-(void)loadImage{

    PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];

    if (status == PHAuthorizationStatusRestricted ||

        status == PHAuthorizationStatusDenied) {

        [AlertView alertWithTarget:self andMessage:@"设置访问权限打开相册" andConfirm:^(UIAlertAction *action) {

            NSLog(@"点击了确定");

        }];

    }

    

    self.assets =  [self getAllAssetInPhotoAblumWithAscending:YES];

    _titleLabel.text = [NSString stringWithFormat:@"全部图片(%ld)",_assets.count];

    for (PHAsset *set in _assets) {

        PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];

        options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;

        

        [[PHImageManager defaultManager] requestImageForAsset:set targetSize:[UIScreen mainScreen].bounds.size contentMode:PHImageContentModeAspectFit options:options resultHandler:^(UIImage *result, NSDictionary *info) {

            //设置图片

            [self.imageArr addObject:result];

            [self.collectionView reloadData];

        }];

        //NSLog(@"%ld",_imageArr.count);

        

    }

}


-(UIImagePickerController *)imagePicker{

    if (_imagePicker == nil) {

        _imagePicker = [[UIImagePickerController alloc]init];

        if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {

            _imagePicker .sourceType = UIImagePickerControllerSourceTypeCamera;

            //设置拍摄照片

            _imagePicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;

            _imagePicker.allowsEditing = YES;

            _imagePicker.delegate = self;

        } else {

            [AlertView alertWithTarget:self andMessage:@"摄像头无法使用" andConfirm:nil];

        }

    }

    return _imagePicker;

}


#pragma mark-- imagePicker的代理

-(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker{

    [picker dismissViewControllerAnimated:YES completion:nil];

}

-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info{

    //获取到图片

    UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage];

    //保存照片到相册

    UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);

    

    [picker dismissViewControllerAnimated:YES completion:nil];

}

-(void)image:(UIImage*)image didFinishSavingWithError:(NSError*)error contextInfo:(void*)contextInfo

{

      if (error) {

        //errornil保存失败

        NSLog(@"%@ 保存失败",[error localizedDescription]);

    }else

    {

        //保存成功

        NSLog(@"保存成功");

        [_imageArr addObject:image];

        [_collectionView reloadData];

    }

}



#pragma mark -- collection代理

-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{

    NSLog(@"%ld",_imageArr.count);

    return _imageArr.count;

}

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{

    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];

    

    UIImage *image = _imageArr[indexPath.row];

    //CGFloat height = image.size.height/image.size.width*100.0/375*ScreenWidth;

    UIImageView *imageView = [[UIImageView alloc]initWithFrame:cell.bounds];

    imageView.image = image;

    [cell.contentView addSubview:imageView];

    return cell;

}


-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{

    ImageViewController *imageV = [[ImageViewController alloc]init];

    [self.navigationController pushViewController:imageV animated:YES];

    

}


#pragma mark - 获取相册内所有照片资源  

- (NSMutableArray<PHAsset *> *)getAllAssetInPhotoAblumWithAscending:(BOOL)ascending

{

    NSMutableArray<PHAsset *> *assets = [NSMutableArray array];

    

    PHFetchOptions *option = [[PHFetchOptions alloc] init];

    //ascending YES时,按照照片的创建时间升序排列;NO时,则降序排列

    option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:ascending]];

    

    PHFetchResult *result = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:option];

    

    [result enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

        PHAsset *asset = (PHAsset *)obj;

        NSLog(@"照片名%@", [asset valueForKey:@"filename"]);

        [assets addObject:asset];

    }];

   // NSLog(@"%ld",assets.count);

    return assets;

}





- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}


/*

#pragma mark - Navigation


// In a storyboard-based application, you will often want to do a little preparation before navigation

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

    // Get the new view controller using [segue destinationViewController].

    // Pass the selected object to the new view controller.

}

*/


@end