demo:https://download.csdn.net/download/u012881779/10739412
之前做电商平台图片搜索时写的一个拍照购的demo。
示意图:
自定义相机:
#import <UIKit/UIKit.h>
#import "ResultViewController.h"
typedef void(^customSelectImageViewBlock)(UIImage *selectImage);
@interface GACustomCameraViewController : UIViewController
@property (nonatomic, strong) customSelectImageViewBlock selectImage;
@end
#import "GACustomCameraViewController.h"
#import <AVFoundation/AVFoundation.h>
#import <AssetsLibrary/AssetsLibrary.h>
@interface GACustomCameraViewController () <UINavigationControllerDelegate, UIImagePickerControllerDelegate>
@property (weak, nonatomic) IBOutlet UIButton *theFlashBut;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *statusHighConstraint;
@property (strong, nonatomic) AVCaptureSession *session;
@property (strong, nonatomic) AVCaptureVideoPreviewLayer *previewLayer;
@property (strong, nonatomic) AVCaptureDevice *device;
@property (strong, nonatomic) AVCaptureDeviceInput *deviceInput;
@property (strong, nonatomic) AVCaptureStillImageOutput *imageOutput;
@property (strong, nonatomic) AVCaptureConnection *connection;
@end
@implementation GACustomCameraViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 判断相机 是否可以使用
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
NSLog(@"sorry, no camera or camera is unavailable.");
return;
}
// 设置相机
[self initCameraSettings];
// 当前闪光灯状态
if (self.device.flashMode == AVCaptureFlashModeOff) {
_theFlashBut.selected = NO;
} else {
_theFlashBut.selected = YES;
}
// 状态栏高度
_statusHighConstraint.constant = [[UIApplication sharedApplication] statusBarFrame].size.height;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:YES];
if (self.session) {
[self.session startRunning];
}
[self shatusViewHidden:NO];
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:YES];
if (self.session) {
[self.session stopRunning];
}
// [self shatusViewHidden:NO];
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault;
}
// 状态栏显示/隐藏
- (void)shatusViewHidden:(BOOL)result {
UIView *statusView = [[[UIApplication sharedApplication] valueForKey:@"statusBarWindow"] valueForKey:@"statusBar"];
statusView.hidden = result;
}
// 初始化相机
- (void)initCameraSettings {
NSError *error;
// 创建会话层
self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
// 初始化session
self.session = [[AVCaptureSession alloc] init];
if ([self.session canSetSessionPreset:AVCaptureSessionPresetPhoto]) {
self.session.sessionPreset = AVCaptureSessionPresetPhoto;
}
// 初始化输入设备
self.deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:&error];
// 初始化照片输出对象
self.imageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:AVVideoCodecJPEG,AVVideoCodecKey, nil];
[self.imageOutput setOutputSettings:outputSettings];
// 判断输入输出设备是否可用
if ([self.session canAddInput:self.deviceInput]) {
[self.session addInput:self.deviceInput];
}
if ([self.session canAddOutput:self.imageOutput]) {
[self.session addOutput:self.imageOutput];
}
// 初始化预览图层
self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session];
/** 设置图层的填充样式
AVLayerVideoGravityResize, // 非均匀模式。两个维度完全填充至整个视图区域
AVLayerVideoGravityResizeAspect, // 等比例填充,直到一个维度到达区域边界
AVLayerVideoGravityResizeAspectFill, // 等比例填充,直到填充满整个视图区域,其中一个维度的部分区域会被裁剪
*/
self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
self.previewLayer.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
[self.view.layer insertSublayer:self.previewLayer atIndex:0];
}
// 返回
- (IBAction)returnAction:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)returnWithImage:(UIImage *)image {
/*
// 在首页跳转
[self returnAction:nil];
if (self.selectImage) {
self.selectImage(image);
}*/
// 在自定义相机跳转
[self entryRsultVCWithImage:image];
}
- (void)entryRsultVCWithImage:(UIImage *)image {
ResultViewController *result = [[ResultViewController alloc] initWithNibName:@"ResultViewController" bundle:nil];
result.originalImage = image;
[self presentViewController:result animated:YES completion:nil];
}
// 闪光灯
- (IBAction)theFlashAction:(id)sender {
AVCaptureFlashMode mode;
UIButton *tempBut = (UIButton *)sender;
if (tempBut.isSelected) {
tempBut.selected = NO;
mode = AVCaptureFlashModeOff;
} else {
tempBut.selected = YES;
mode = AVCaptureFlashModeOn;
}
if ([self.device isFlashModeSupported:mode]) {
[self.session beginConfiguration];
[self.device lockForConfiguration:nil];
[self.device setFlashMode:mode]; // 这行代码就要放在这个顺序的位置否则会崩溃
[self.device unlockForConfiguration];
[self.session commitConfiguration];
[self.session startRunning];
}
}
// 拍照
- (IBAction)takePhotoAction:(id)sender {
self.connection = [self.imageOutput connectionWithMediaType:AVMediaTypeVideo];
[self.connection setVideoOrientation:AVCaptureVideoOrientationPortrait];
[self.imageOutput captureStillImageAsynchronouslyFromConnection:self.connection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
NSData *jpegData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *image = [UIImage imageWithData:jpegData];
[self returnWithImage:image];
}];
}
// 前置/后置摄像头
- (IBAction)frontOrRearCameraAction:(id)sender {
[UIView beginAnimations:@"animation" context:nil];
[UIView setAnimationDuration:.5f];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.view cache:YES];
[UIView commitAnimations];
NSArray *inputs = self.session.inputs;
for ( AVCaptureDeviceInput *input in inputs ) {
AVCaptureDevice *device = input.device;
if ( [device hasMediaType:AVMediaTypeVideo] ) {
AVCaptureDevicePosition position = device.position;
AVCaptureDevice *newCamera = nil;
AVCaptureDeviceInput *newInput = nil;
if (position == AVCaptureDevicePositionFront) {
newCamera = [self cameraWithPosition:AVCaptureDevicePositionBack];
} else {
newCamera = [self cameraWithPosition:AVCaptureDevicePositionFront];
}
newInput = [AVCaptureDeviceInput deviceInputWithDevice:newCamera error:nil];
[self.session beginConfiguration];
[self.session removeInput:input];
[self.session addInput:newInput];
[self.session commitConfiguration];
break;
}
}
}
// 相机状态
- (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position {
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for ( AVCaptureDevice *device in devices ) {
if ( device.position == position ) {
return device;
}
}
return nil;
}
// 打开相册
- (IBAction)openAlbumAction:(id)sender {
UIImagePickerController *pickerController = [[UIImagePickerController alloc] init];
pickerController.delegate = self;
pickerController.navigationBar.translucent = NO;
pickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
pickerController.edgesForExtendedLayout = UIRectEdgeNone;
[self presentViewController:pickerController animated:YES completion:nil];
}
#pragma mark - 从相册选择图片后操作
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
// 获取拍照的图像
// UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
// 获取图片裁剪的图
// UIImage *edit = [info objectForKey:UIImagePickerControllerEditedImage];
// 获取照片的原图
UIImage *original = [info objectForKey:UIImagePickerControllerOriginalImage];
[picker dismissViewControllerAnimated:NO completion:^{
[self returnWithImage:original];
}];
}
#pragma mark - 取消操作时调用
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[picker dismissViewControllerAnimated:YES completion:nil];
}
@end
结果展示页:
#import <UIKit/UIKit.h>
@interface ResultViewController : UIViewController
@property (strong, nonatomic) UIImage *originalImage;
@end
#import "ResultViewController.h"
#import "YBPictureCutterView.h"
static float const LIMIT_SV_BOTTOM = 120.0; // scrollView底部最大移动范围
static float const LIMIT_DRAG_DISTANCE = 44.0; // 拖动超过响应范围后自动滚动到约束位置
static float const LIMIT_CO_TOP = 100.0; // 约束内容视图顶部
static float const LIMIT_CO_BOTTOM = 200.0; // 约束图片视图底部
@interface ResultViewController () <YBPictureCutterViewDataSource, YBPictureCutterViewDelegate, UIGestureRecognizerDelegate>
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
@property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
@property (weak, nonatomic) IBOutlet UIView *contentTitleBGView;
@property (weak, nonatomic) IBOutlet UIImageView *tempShowIV;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *ivBottomConstraint; // 图片区域的bottom约束
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *contentTopConstraint; // 内容区域的top约束
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *scrollViewBottomConstraint; // 图片区域sv的bottom约束
@property (strong, nonatomic) YBPictureCutterView *cutterView;
@property (strong, nonatomic) UIImageView *showImageView;
@property (assign, nonatomic) CGPoint startPoint; // 开始拖动时的起始点
@property (assign, nonatomic) CGFloat startTopY; // 开始拖动时的内容区域top约束
@property (assign, nonatomic) CGFloat startSCBottomY; // 开始拖动时图片区域Bottom约束
@property (assign, nonatomic) CGFloat startSCOffSetY; // 开始拖动时图片区域offset.y
@end
@implementation ResultViewController
- (void)viewDidLoad {
[super viewDidLoad];
_contentTopConstraint.constant = LIMIT_CO_TOP;
_ivBottomConstraint.constant = LIMIT_CO_BOTTOM;
_scrollViewBottomConstraint.constant = LIMIT_SV_BOTTOM;
// UIView部分倒圆角
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:_contentTitleBGView.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii:CGSizeMake(10, 10)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = _contentTitleBGView.bounds;
maskLayer.path = maskPath.CGPath;
_contentTitleBGView.layer.mask = maskLayer;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self updateScrollViewWithDev:0];
[_scrollView addSubview:self.showImageView];
self.showImageView.image = _originalImage;
[self.showImageView addSubview:self.cutterView];
self.showImageView.userInteractionEnabled = YES;
[self adaptationSafeAreaWith:_scrollView useArea:1];
}
- (void)updateScrollViewWithDev:(float)dev {
float hbw = _originalImage.size.height/(float)_originalImage.size.width;
float newWidth = [UIScreen mainScreen].bounds.size.width;
float newHeight = newWidth*hbw;
float devY = 0;
BOOL result = NO;
if (_scrollView.frame.size.height > newHeight) {
devY = (_scrollView.frame.size.height - newHeight)/2.0;
} else {
result = YES;
}
[_scrollView setContentSize:CGSizeMake(newWidth, newHeight)];
self.showImageView.frame = CGRectMake(0, devY, newWidth, newHeight);
if (result) {
CGPoint oldOffset = _scrollView.contentOffset;
oldOffset.y = _startSCOffSetY - dev;
CGFloat jgHeight = _scrollView.contentSize.height-_scrollView.frame.size.height;
if (oldOffset.y < 0) {
oldOffset.y = 0;
} else if (oldOffset.y > jgHeight) {
oldOffset.y = jgHeight;
}
[_scrollView setContentOffset:oldOffset];
}
}
- (UIImageView *)showImageView {
if (!_showImageView) {
_showImageView = [UIImageView new];
[_showImageView setContentMode:UIViewContentModeScaleToFill];
}
return _showImageView;
}
- (YBPictureCutterView *)cutterView {
if (!_cutterView) {
_cutterView = [[YBPictureCutterView alloc] init];
_cutterView.dataSource = self;
_cutterView.delegate = self;
}
_cutterView.frame = _showImageView.bounds;
return _cutterView;
}
#pragma mark - pictureCutterView dataSource & delegate
- (UIImage *)imageForPictureCutterView:(YBPictureCutterView *)cutterView {
return _showImageView.image;
}
- (void)pictureCutterView:(YBPictureCutterView *)cutterView didClippedImage:(UIImage *)image {
NSLog(@"%@", NSStringFromCGSize(image.size));
_tempShowIV.image = image;
}
- (IBAction)returnAction:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
/**
* 适配iPhone X的安全区域
* isUse = 1 表示使用安全区域
* isUse = 0 表示不使用安全区域
*/
- (void)adaptationSafeAreaWith:(UIScrollView *)sv useArea:(NSInteger)isUse {
#ifdef __IPHONE_11_0
if ([sv respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) {
if (isUse) {
if (@available(iOS 11.0, *)) {
sv.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
if ([[sv class] isSubclassOfClass:[UITableView class]]) {
UITableView *tv = (UITableView *)sv;
[tv setInsetsContentViewsToSafeArea:NO];
}
} else {
// Fallback on earlier versions
}
} else {
if (@available(iOS 11.0, *)) {
sv.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentAlways;
} else {
// Fallback on earlier versions
}
}
}
#endif
}
#pragma mark - PanGestureRecognizer手势
- (IBAction)contentViewPanGesture:(UIPanGestureRecognizer *)gestureRecognizer {
UIView *tempView = gestureRecognizer.view;
if (tempView.tag == 567) {
CGPoint tempPoint = [gestureRecognizer locationInView:self.view];
// 图片ScrollView底部约束移动范围
CGFloat scMaxMoveRange = LIMIT_SV_BOTTOM;
// 图片ScrollView移动范围/内容View移动范围
CGFloat bl = scMaxMoveRange/(float)([UIScreen mainScreen].bounds.size.height - LIMIT_CO_TOP - LIMIT_CO_BOTTOM);
CGFloat scrHeight = [UIScreen mainScreen].bounds.size.height;
CGFloat devY = tempPoint.y-_startPoint.y;
CGFloat newY = _startTopY + devY;
if (newY >= LIMIT_CO_TOP) {
if (newY <= (scrHeight - LIMIT_CO_BOTTOM)) {
_contentTopConstraint.constant = newY;
_scrollViewBottomConstraint.constant = _startSCBottomY - bl*devY;
[self updateScrollViewWithDev:bl*devY];
} else {
[self toBottomLocationWithConstant:0 Dev:0];
return;
}
} else {
[self toTopLocationWithConstant:scMaxMoveRange Dev:0];
return;
}
// 判断是否拖动结束
if (gestureRecognizer.state == UIGestureRecognizerStateEnded || gestureRecognizer.state == UIGestureRecognizerStateCancelled) {
CGFloat responseRange = LIMIT_DRAG_DISTANCE;
BOOL result = NO;
if (devY > 0) {
if (devY >= responseRange) {
[self toBottomLocationWithConstant:0 Dev:bl*devY];
} else {
result = YES;
}
} else if (devY < 0) {
if (devY <= (0 - responseRange)) {
[self toTopLocationWithConstant:scMaxMoveRange Dev:bl*devY];
} else {
result = YES;
}
}
if (result) {
[self toOldLocationWithConstant:self.startSCBottomY Dev:bl*devY];
}
}
}
}
// 滚动到底部位置
- (void)toBottomLocationWithConstant:(float)constant Dev:(float)dev {
self.scrollViewBottomConstraint.constant = constant;
[self.view layoutIfNeeded];
[UIView animateWithDuration:0.3 animations:^{
self.contentTopConstraint.constant = [UIScreen mainScreen].bounds.size.height - LIMIT_CO_BOTTOM;
[self updateScrollViewWithDev:dev];
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
self.collectionView.scrollEnabled = NO;
}];
}
// 滚动到顶部位置
- (void)toTopLocationWithConstant:(float)constant Dev:(float)dev {
self.scrollViewBottomConstraint.constant = constant;
[self.view layoutIfNeeded];
[UIView animateWithDuration:0.3 animations:^{
self.contentTopConstraint.constant = LIMIT_CO_TOP;
[self updateScrollViewWithDev:dev];
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
self.collectionView.scrollEnabled = YES;
}];
}
// 滚动回到原位置
- (void)toOldLocationWithConstant:(float)constant Dev:(float)dev {
self.scrollViewBottomConstraint.constant = constant;
[self.view layoutIfNeeded];
[UIView animateWithDuration:0.3 animations:^{
self.contentTopConstraint.constant = self.startTopY;
[self updateScrollViewWithDev:dev];
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
if (self.startTopY == LIMIT_CO_TOP) {
self.collectionView.scrollEnabled = YES;
}
}];
}
#pragma mark - UIGestureRecognizerDelegate
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
UIView *tempView = gestureRecognizer.view;
if (tempView.tag == 567) {
_startPoint = [gestureRecognizer locationInView:self.view];
_startSCBottomY = _scrollViewBottomConstraint.constant;
_startSCOffSetY = _scrollView.contentOffset.y;
_startTopY = _contentTopConstraint.constant;
CGFloat tempLimitBottom = [UIScreen mainScreen].bounds.size.height - LIMIT_CO_BOTTOM;
if (_startTopY != LIMIT_CO_TOP && _startTopY != tempLimitBottom) {
if (_startTopY-LIMIT_CO_TOP < tempLimitBottom-_startTopY) {
_startTopY = LIMIT_CO_TOP;
} else {
_startTopY = tempLimitBottom;
}
}
}
return YES;
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView.tag == 2000) {
CGPoint offset = scrollView.contentOffset;
if (offset.y <= 0) {
offset.y = 0;
scrollView.contentOffset = offset;
self.collectionView.scrollEnabled = NO;
} else {
}
}
}
@end