├── MKCustomCamera ├── Assets.xcassets │ ├── Contents.json │ ├── 085625KMV.imageset │ │ ├── 085625KMV.jpg │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── LittleVideoController.h ├── ShowUIImagePickerViewController.h ├── AppDelegate.h ├── PostVideoPlayerController.h ├── main.m ├── Info.plist ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── AppDelegate.m ├── UIView+RMAdditions.h ├── PostVideoPlayerController.m ├── UIView+RMAdditions.m ├── ShowUIImagePickerViewController.m └── LittleVideoController.m ├── README.md ├── .gitignore └── MKCustomCamera.xcodeproj └── project.pbxproj /MKCustomCamera/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /MKCustomCamera/Assets.xcassets/085625KMV.imageset/085625KMV.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinMkY/MKCustomCamera/HEAD/MKCustomCamera/Assets.xcassets/085625KMV.imageset/085625KMV.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 自定义相机, UIImagePickerController && AVCaptureSession 2 | 3 | 微信小视频模仿 Demo 效果展示(如果看不清可点击观看大图): 4 | 5 | (PS:模拟器因为没有摄像头等设备,所以并不支持,请在真机上运行) 6 | 7 | ![](http://7xn5aw.com1.z0.glb.clouddn.com/%E5%B0%8F%E8%A7%86%E9%A2%911111.gif) 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | project.xcworkspace 2 | xcuserdata 3 | iWeidao.xccheckout 4 | eonfig.plist 5 | *.swp 6 | .DS_Store 7 | Build 8 | iWeidao.xcodeproj/project.pbxproj 9 | xcshareddata 10 | *.xcscheme 11 | iWeidao.xcworkspace/xcshareddata/xcschemes/iWeidao.xcscheme 12 | -------------------------------------------------------------------------------- /MKCustomCamera/LittleVideoController.h: -------------------------------------------------------------------------------- 1 | // 2 | // LittleVideoController.h 3 | // MKCustomCamera 4 | // 5 | // Created by ykh on 16/1/5. 6 | // Copyright © 2016年 MK. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LittleVideoController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /MKCustomCamera/ShowUIImagePickerViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // MKCustomCamera 4 | // 5 | // Created by ykh on 16/1/4. 6 | // Copyright © 2016年 MK. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ShowUIImagePickerViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /MKCustomCamera/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // MKCustomCamera 4 | // 5 | // Created by ykh on 16/1/4. 6 | // Copyright © 2016年 MK. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /MKCustomCamera/PostVideoPlayerController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PostVideoPlayerController.h 3 | // MKCustomCamera 4 | // 5 | // Created by ykh on 16/1/6. 6 | // Copyright © 2016年 MK. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PostVideoPlayerController : UIViewController 12 | 13 | @property (nonatomic,strong) NSURL *videoUrl; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /MKCustomCamera/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MKCustomCamera 4 | // 5 | // Created by ykh on 16/1/4. 6 | // Copyright © 2016年 MK. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /MKCustomCamera/Assets.xcassets/085625KMV.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "085625KMV.jpg", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /MKCustomCamera/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /MKCustomCamera/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /MKCustomCamera/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /MKCustomCamera/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // MKCustomCamera 4 | // 5 | // Created by ykh on 16/1/4. 6 | // Copyright © 2016年 MK. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /MKCustomCamera/UIView+RMAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+RMAdditions.h 3 | // RMCategories 4 | // 5 | // Created by Richard McClellan on 5/27/13. 6 | // Copyright (c) 2013 Richard McClellan. All rights reserved. 7 | // 8 | 9 | #define SCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width) 10 | 11 | #define SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height) 12 | 13 | #import 14 | 15 | @interface UIView (RMAdditions) 16 | /** 17 | * Shortcut for frame.origin.x. 18 | * 19 | * Sets frame.origin.x = x 20 | */ 21 | @property (nonatomic, assign) CGFloat x; 22 | /** 23 | * Shortcut for frame.origin.x. 24 | * 25 | * Sets frame.origin.x = left 26 | */ 27 | @property (nonatomic, assign) CGFloat left; 28 | 29 | /** 30 | * Shortcut for frame.origin.y 31 | * 32 | * Sets frame.origin.y = top 33 | */ 34 | @property (nonatomic, assign) CGFloat top; 35 | 36 | /** 37 | * Shortcut for frame.origin.y 38 | * 39 | * Sets frame.origin.y = y 40 | */ 41 | @property (nonatomic, assign) CGFloat y; 42 | 43 | /** 44 | * Shortcut for frame.origin.x + frame.size.width 45 | * 46 | * Sets frame.origin.x = right - frame.size.width 47 | */ 48 | @property (nonatomic, assign) CGFloat right; 49 | 50 | /** 51 | * Shortcut for frame.origin.y + frame.size.height 52 | * 53 | * Sets frame.origin.y = bottom - frame.size.height 54 | */ 55 | @property (nonatomic, assign) CGFloat bottom; 56 | 57 | /** 58 | * Shortcut for frame.size.width 59 | * 60 | * Sets frame.size.width = width 61 | */ 62 | @property (nonatomic, assign) CGFloat width; 63 | 64 | /** 65 | * Shortcut for frame.size.height 66 | * 67 | * Sets frame.size.height = height 68 | */ 69 | @property (nonatomic, assign) CGFloat height; 70 | 71 | /** 72 | * Shortcut for center.x 73 | * 74 | * Sets center.x = centerX 75 | */ 76 | @property (nonatomic, assign) CGFloat centerX; 77 | 78 | /** 79 | * Shortcut for center.y 80 | * 81 | * Sets center.y = centerY 82 | */ 83 | @property (nonatomic, assign) CGFloat centerY; 84 | 85 | /** 86 | * Shortcut for origin 87 | * 88 | * Sets frame.origin = origin 89 | */ 90 | @property (nonatomic, assign) CGPoint origin; 91 | 92 | /** 93 | * Shortcut for size 94 | * 95 | * Sets frame.size = size 96 | */ 97 | @property (nonatomic, assign) CGSize size; 98 | 99 | /** 100 | * Utility to convert UIViewAnimationCurve to UIViewAnimationOptions 101 | * 102 | * Used in UIViewController+RMAdditions for animating view for keyboard changes 103 | */ 104 | + (UIViewAnimationOptions)animationOptionsWithCurve:(UIViewAnimationCurve)curve; 105 | 106 | - (void) addLoadingView; 107 | - (void) addLoadingViewWithText:(NSString *)text; 108 | - (void) removeLoadingView; 109 | 110 | @end 111 | -------------------------------------------------------------------------------- /MKCustomCamera/PostVideoPlayerController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PostVideoPlayerController.m 3 | // MKCustomCamera 4 | // 5 | // Created by ykh on 16/1/6. 6 | // Copyright © 2016年 MK. All rights reserved. 7 | // 8 | 9 | #import "PostVideoPlayerController.h" 10 | #import 11 | #import 12 | #import 13 | 14 | @interface PostVideoPlayerController(){ 15 | 16 | AVPlayer *_player; 17 | AVPlayerItem *_playItem; 18 | AVPlayerLayer *_playerLayer; 19 | AVPlayerLayer *_fullPlayer; 20 | BOOL _isPlaying; 21 | } 22 | @property (weak, nonatomic) IBOutlet UIButton *saveBtn; 23 | 24 | @end 25 | 26 | @implementation PostVideoPlayerController 27 | 28 | - (void)viewDidLoad 29 | { 30 | [super viewDidLoad]; 31 | self.view.backgroundColor = [UIColor whiteColor]; 32 | [self create]; 33 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:)name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; 34 | 35 | //时间差 36 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 37 | self.saveBtn.enabled = YES; 38 | }); 39 | } 40 | 41 | - (void)viewWillAppear:(BOOL)animated 42 | { 43 | [super viewWillAppear:animated]; 44 | } 45 | 46 | - (void)viewWillDisappear:(BOOL)animated 47 | { 48 | [super viewWillDisappear:animated]; 49 | [_player pause]; 50 | _player = nil; 51 | } 52 | 53 | - (void)create 54 | { 55 | _playItem = [AVPlayerItem playerItemWithURL:self.videoUrl]; 56 | _player = [AVPlayer playerWithPlayerItem:_playItem]; 57 | _playerLayer =[AVPlayerLayer playerLayerWithPlayer:_player]; 58 | _playerLayer.frame = CGRectMake(200, 200, 100, 100); 59 | _playerLayer.videoGravity=AVLayerVideoGravityResizeAspectFill;//视频填充模式 60 | [self.view.layer addSublayer:_playerLayer]; 61 | [_player play]; 62 | } 63 | 64 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 65 | { 66 | if (!_isPlaying) { 67 | _playerLayer.frame = [UIScreen mainScreen].bounds; 68 | }else{ 69 | _playerLayer.frame = CGRectMake(200, 200, 100, 100); 70 | } 71 | _isPlaying = !_isPlaying; 72 | } 73 | 74 | -(void)playbackFinished:(NSNotification *)notification 75 | { 76 | [_player seekToTime:CMTimeMake(0, 1)]; 77 | [_player play]; 78 | } 79 | 80 | #pragma mark 保存压缩 81 | - (NSURL *)compressedURL 82 | { 83 | return [NSURL fileURLWithPath:[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true) lastObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"compressed.mp4"]]]; 84 | } 85 | 86 | - (CGFloat)fileSize:(NSURL *)path 87 | { 88 | return [[NSData dataWithContentsOfURL:path] length]/1024.00 /1024.00; 89 | } 90 | 91 | // 压缩视频 92 | - (IBAction)compressVideo:(id)sender 93 | { 94 | NSLog(@"开始压缩,压缩前大小 %f MB",[self fileSize:self.videoUrl]); 95 | 96 | self.saveBtn.enabled = NO; 97 | 98 | AVURLAsset *avAsset = [[AVURLAsset alloc] initWithURL:self.videoUrl options:nil]; 99 | NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset]; 100 | if ([compatiblePresets containsObject:AVAssetExportPresetLowQuality]) { 101 | 102 | AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:avAsset presetName:AVAssetExportPreset640x480]; 103 | exportSession.outputURL = [self compressedURL]; 104 | //优化网络 105 | exportSession.shouldOptimizeForNetworkUse = true; 106 | //转换后的格式 107 | exportSession.outputFileType = AVFileTypeMPEG4; 108 | //异步导出 109 | [exportSession exportAsynchronouslyWithCompletionHandler:^{ 110 | // 如果导出的状态为完成 111 | if ([exportSession status] == AVAssetExportSessionStatusCompleted) { 112 | NSLog(@"压缩完毕,压缩后大小 %f MB",[self fileSize:[self compressedURL]]); 113 | [self saveVideo:[self compressedURL]]; 114 | }else{ 115 | NSLog(@"当前压缩进度:%f",exportSession.progress); 116 | } 117 | 118 | self.saveBtn.enabled = YES; 119 | }]; 120 | } 121 | } 122 | 123 | 124 | - (void)saveVideo:(NSURL *)outputFileURL 125 | { 126 | ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; 127 | [library writeVideoAtPathToSavedPhotosAlbum:outputFileURL 128 | completionBlock:^(NSURL *assetURL, NSError *error) { 129 | if (error) { 130 | NSLog(@"保存视频失败:%@",error); 131 | } else { 132 | NSLog(@"保存视频到相册成功"); 133 | } 134 | }]; 135 | } 136 | 137 | -(void)dealloc 138 | { 139 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 140 | } 141 | 142 | @end 143 | -------------------------------------------------------------------------------- /MKCustomCamera/UIView+RMAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+RMAdditions.m 3 | // RMCategories 4 | // 5 | // Created by Richard McClellan on 5/27/13. 6 | // Copyright (c) 2013 Richard McClellan. All rights reserved. 7 | // 8 | 9 | 10 | #import "UIView+RMAdditions.h" 11 | #import 12 | 13 | @implementation UIView (RMAdditions) 14 | 15 | - (CGFloat)left { 16 | return self.frame.origin.x; 17 | } 18 | 19 | - (void)setLeft:(CGFloat)x { 20 | CGRect frame = self.frame; 21 | frame.origin.x = x; 22 | self.frame = frame; 23 | } 24 | 25 | - (CGFloat)x { 26 | return self.frame.origin.x; 27 | } 28 | 29 | - (void)setX:(CGFloat)x { 30 | CGRect frame = self.frame; 31 | frame.origin.x = x; 32 | self.frame = frame; 33 | } 34 | 35 | 36 | - (CGFloat)top { 37 | return self.frame.origin.y; 38 | } 39 | 40 | - (void)setTop:(CGFloat)y { 41 | CGRect frame = self.frame; 42 | frame.origin.y = y; 43 | self.frame = frame; 44 | } 45 | 46 | - (CGFloat)y { 47 | return self.frame.origin.y; 48 | } 49 | 50 | - (void)setY:(CGFloat)y { 51 | CGRect frame = self.frame; 52 | frame.origin.y = y; 53 | self.frame = frame; 54 | } 55 | 56 | - (CGFloat)right { 57 | return self.frame.origin.x + self.frame.size.width; 58 | } 59 | 60 | - (void)setRight:(CGFloat)right { 61 | CGRect frame = self.frame; 62 | frame.origin.x = right - frame.size.width; 63 | self.frame = frame; 64 | } 65 | 66 | - (CGFloat)bottom { 67 | return self.frame.origin.y + self.frame.size.height; 68 | } 69 | 70 | - (void)setBottom:(CGFloat)bottom { 71 | CGRect frame = self.frame; 72 | frame.origin.y = bottom - frame.size.height; 73 | self.frame = frame; 74 | } 75 | 76 | - (CGFloat)centerX { 77 | return self.center.x; 78 | } 79 | 80 | - (void)setCenterX:(CGFloat)centerX { 81 | self.center = CGPointMake(centerX, self.center.y); 82 | } 83 | 84 | - (CGFloat)centerY { 85 | return self.center.y; 86 | } 87 | 88 | - (void)setCenterY:(CGFloat)centerY { 89 | self.center = CGPointMake(self.center.x, centerY); 90 | } 91 | 92 | - (CGFloat)width { 93 | return self.frame.size.width; 94 | } 95 | 96 | - (void)setWidth:(CGFloat)width { 97 | CGRect frame = self.frame; 98 | frame.size.width = width; 99 | self.frame = frame; 100 | } 101 | 102 | - (CGFloat)height { 103 | return self.frame.size.height; 104 | } 105 | 106 | - (void)setHeight:(CGFloat)height { 107 | CGRect frame = self.frame; 108 | frame.size.height = height; 109 | self.frame = frame; 110 | } 111 | 112 | - (void)setOrigin:(CGPoint)origin { 113 | CGRect frame = self.frame; 114 | frame.origin = origin; 115 | self.frame = frame; 116 | } 117 | 118 | - (CGPoint) origin { 119 | return self.frame.origin; 120 | } 121 | 122 | - (void) setSize:(CGSize)size { 123 | CGRect frame = self.frame; 124 | frame.size = size; 125 | self.frame = frame; 126 | } 127 | 128 | - (CGSize) size { 129 | return self.frame.size; 130 | } 131 | 132 | + (UIViewAnimationOptions)animationOptionsWithCurve:(UIViewAnimationCurve)curve { 133 | switch(curve) { 134 | case UIViewAnimationCurveEaseIn: 135 | return UIViewAnimationOptionCurveEaseIn; 136 | case UIViewAnimationCurveEaseInOut: 137 | return UIViewAnimationOptionCurveEaseInOut; 138 | case UIViewAnimationCurveEaseOut: 139 | return UIViewAnimationOptionCurveEaseOut; 140 | case UIViewAnimationCurveLinear: 141 | return UIViewAnimationOptionCurveLinear; 142 | } 143 | } 144 | 145 | static const void *kLoadingViewKey = @"LoadingViewKey"; 146 | 147 | - (void) addLoadingView { 148 | [self addLoadingViewWithText:@"Loading..."]; 149 | } 150 | 151 | - (void) addLoadingViewWithText:(NSString *)text { 152 | [self removeLoadingView]; 153 | 154 | UIView *loadingView = [[UIView alloc] initWithFrame:self.bounds]; 155 | [loadingView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; 156 | [loadingView setBackgroundColor:[UIColor colorWithWhite:0.9 alpha:1.0]]; 157 | objc_setAssociatedObject(self, kLoadingViewKey, loadingView, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 158 | 159 | UILabel *loadingLabel = [[UILabel alloc] initWithFrame:CGRectZero]; 160 | loadingLabel.backgroundColor = [UIColor clearColor]; 161 | loadingLabel.font = [UIFont systemFontOfSize:15.0]; 162 | loadingLabel.textColor = [UIColor blackColor]; 163 | [loadingLabel setText:text]; 164 | [loadingLabel sizeToFit]; 165 | 166 | UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; 167 | activityIndicator.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; 168 | [activityIndicator startAnimating]; 169 | activityIndicator.left = (self.width - activityIndicator.width - loadingLabel.width - 5.0) / 2; 170 | activityIndicator.centerY = self.centerY; 171 | [loadingView addSubview:activityIndicator]; 172 | 173 | loadingLabel.left = (self.width - activityIndicator.width - loadingLabel.width - 5.0) / 2 + activityIndicator.width + 5.0; 174 | loadingLabel.centerY = self.centerY; 175 | loadingLabel.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; 176 | [loadingView addSubview:loadingLabel]; 177 | 178 | [self addSubview:loadingView]; 179 | } 180 | 181 | - (void) removeLoadingView { 182 | UIView *loadingView = objc_getAssociatedObject(self, kLoadingViewKey); 183 | [loadingView removeFromSuperview]; 184 | } 185 | 186 | 187 | @end 188 | -------------------------------------------------------------------------------- /MKCustomCamera/ShowUIImagePickerViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // MKCustomCamera 4 | // 5 | // Created by ykh on 16/1/4. 6 | // Copyright © 2016年 MK. All rights reserved. 7 | // 8 | 9 | #import "ShowUIImagePickerViewController.h" 10 | #import "UIView+RMAdditions.h" 11 | #import 12 | 13 | @interface ShowUIImagePickerViewController () 14 | //@property (nonatomic,strong) UIImagePickerController *imagePickerController; 15 | @end 16 | 17 | @implementation ShowUIImagePickerViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | // Do any additional setup after loading the view, typically from a nib. 22 | } 23 | 24 | //拍照 25 | - (IBAction)showImagePickerForCamera:(id)sender{ 26 | 27 | [self showImagePickerForSourceType:UIImagePickerControllerSourceTypeCamera andCameraCaptureMode:UIImagePickerControllerCameraCaptureModePhoto]; 28 | } 29 | 30 | //摄像 31 | - (IBAction)showImagePickerForCameraShooting:(id)sender { 32 | 33 | [self showImagePickerForSourceType:UIImagePickerControllerSourceTypeCamera andCameraCaptureMode:UIImagePickerControllerCameraCaptureModeVideo]; 34 | } 35 | 36 | //相册 37 | - (IBAction)showImagePickerForPhotoPicker:(id)sender{ 38 | 39 | [self showImagePickerForSourceType:UIImagePickerControllerSourceTypePhotoLibrary andCameraCaptureMode:0]; 40 | } 41 | 42 | - (void)showImagePickerForSourceType:(UIImagePickerControllerSourceType)sourceType andCameraCaptureMode:(UIImagePickerControllerCameraCaptureMode)mode{ 43 | 44 | UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init]; 45 | //这是 VC 的各种 modal 形式 46 | imagePickerController.modalPresentationStyle = UIModalPresentationFullScreen; 47 | imagePickerController.sourceType = sourceType; 48 | //支持的摄制类型,拍照或摄影,此处将本设备支持的所有类型全部获取,并且同时赋值给imagePickerController的话,则可左右切换摄制模式 49 | imagePickerController.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera]; 50 | imagePickerController.delegate = self; 51 | //允许拍照后编辑 52 | imagePickerController.allowsEditing = YES; 53 | //显示默认相机 UI, 默认为yes--> 显示 54 | // imagePickerController.showsCameraControls = NO; 55 | 56 | if (sourceType == UIImagePickerControllerSourceTypeCamera) { 57 | //设置模式-->拍照/摄像 58 | imagePickerController.cameraCaptureMode = mode; 59 | //开启默认摄像头-->前置/后置 60 | imagePickerController.cameraDevice = UIImagePickerControllerCameraDeviceFront; 61 | //设置默认的闪光灯模式-->开/关/自动 62 | imagePickerController.cameraFlashMode = UIImagePickerControllerCameraFlashModeAuto; 63 | 64 | //拍摄时预览view的transform属性,可以实现旋转,缩放功能 65 | // imagePickerController.cameraViewTransform = CGAffineTransformMakeRotation(M_PI); 66 | // imagePickerController.cameraViewTransform = CGAffineTransformMakeScale(2.0,2.0); 67 | 68 | //自定义覆盖图层-->overlayview 69 | UIImage *img = [UIImage imageNamed:@"085625KMV.jpg"]; 70 | UIImageView *iv = [[UIImageView alloc] initWithImage:img]; 71 | iv.width = 300; 72 | iv.height = 200; 73 | imagePickerController.cameraOverlayView = iv; 74 | } 75 | [self presentViewController:imagePickerController animated:YES completion:NULL]; 76 | } 77 | 78 | 79 | #pragma mark delegate 80 | 81 | - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{ 82 | 83 | NSString *mediaType = info[UIImagePickerControllerMediaType]; 84 | 85 | if ([mediaType isEqualToString:@"public.image"]) { 86 | NSLog(@"image..."); 87 | 88 | /* 89 | //获取照片的原图 90 | UIImage* original = [info objectForKey:UIImagePickerControllerOriginalImage]; 91 | //获取图片裁剪后,剩下的图 92 | UIImage* crop = [info objectForKey:UIImagePickerControllerCropRect]; 93 | //获取图片的url 94 | NSURL* url = [info objectForKey:UIImagePickerControllerMediaURL]; 95 | //获取图片的metadata数据信息 96 | NSDictionary* metadata = [info objectForKey:UIImagePickerControllerMediaMetadata]; 97 | */ 98 | 99 | //获取图片裁剪的图 100 | UIImage* edit = [info objectForKey:UIImagePickerControllerEditedImage]; 101 | 102 | [self saveImage:edit]; 103 | 104 | }else{ // public.movie 105 | NSLog(@"video..."); 106 | NSURL *url=[info objectForKey:UIImagePickerControllerMediaURL];//视频路径 107 | NSString *urlStr=[url path]; 108 | 109 | [self saveVideo:urlStr]; 110 | } 111 | 112 | [picker dismissViewControllerAnimated:YES completion:nil]; 113 | 114 | } 115 | 116 | 117 | - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker{ 118 | 119 | NSLog(@"取消"); 120 | 121 | [picker dismissViewControllerAnimated:YES completion:nil]; 122 | 123 | } 124 | 125 | //取消屏幕旋转 126 | - (BOOL)shouldAutorotate { 127 | return YES; 128 | } 129 | 130 | #pragma mark save 131 | 132 | - (void)saveImage:(UIImage *)img 133 | { 134 | // //如果是拍照的照片,则需要手动保存到本地,系统不会自动保存拍照成功后的照片 135 | // UIImageWriteToSavedPhotosAlbum(img, self, @selector(image:didFinishSavingWithError:contextInfo:), nil); 136 | 137 | [[[ALAssetsLibrary alloc]init] writeImageToSavedPhotosAlbum:[img CGImage] orientation:(ALAssetOrientation)img.imageOrientation completionBlock:^(NSURL *assetURL, NSError *error) { 138 | if (error) { 139 | NSLog(@"Save image fail:%@",error); 140 | }else{ 141 | NSLog(@"Save image succeed."); 142 | } 143 | }]; 144 | 145 | } 146 | 147 | - (void)saveVideo:(NSString *)videoPath 148 | { 149 | [[[ALAssetsLibrary alloc]init] writeVideoAtPathToSavedPhotosAlbum:[NSURL URLWithString:videoPath] completionBlock:^(NSURL *assetURL, NSError *error) { 150 | 151 | if (error) { 152 | NSLog(@"Save video fail:%@",error); 153 | }else{ 154 | NSLog(@"Save video succeed."); 155 | } 156 | 157 | }]; 158 | 159 | 160 | // if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(videoPath)) { 161 | // //保存视频到相簿,注意也可以使用ALAssetsLibrary来保存 162 | // UISaveVideoAtPathToSavedPhotosAlbum(videoPath, self, @selector(video:didFinishSavingWithError:contextInfo:), nil);//保存视频到相簿 163 | // }else{ 164 | // 165 | // NSLog(@"您的设备不支持保存视频到相册"); 166 | // 167 | // } 168 | 169 | } 170 | 171 | - (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo{ 172 | 173 | if (error) { 174 | NSLog(@"保存照片过程中发生错误,错误信息:%@",error.localizedDescription); 175 | }else{ 176 | NSLog(@"照片保存成功."); 177 | } 178 | } 179 | 180 | - (void)video:(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo 181 | { 182 | if (error) { 183 | NSLog(@"保存视频过程中发生错误,错误信息:%@",error.localizedDescription); 184 | }else{ 185 | NSLog(@"视频保存成功."); 186 | } 187 | } 188 | 189 | @end 190 | -------------------------------------------------------------------------------- /MKCustomCamera.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 45AA64D51C3D2FF400159E3B /* AssetsLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 45AA64D41C3D2FF400159E3B /* AssetsLibrary.framework */; }; 11 | 45AA64D71C3D320500159E3B /* AVKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 45AA64D61C3D320500159E3B /* AVKit.framework */; }; 12 | 45AA64DA1C3D35FC00159E3B /* PostVideoPlayerController.m in Sources */ = {isa = PBXBuildFile; fileRef = 45AA64D91C3D35FC00159E3B /* PostVideoPlayerController.m */; }; 13 | 45D879711C3BB5EC001AF659 /* LittleVideoController.m in Sources */ = {isa = PBXBuildFile; fileRef = 45D879701C3BB5EC001AF659 /* LittleVideoController.m */; }; 14 | 45EF726D1C3A45DA00FA5876 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 45EF726C1C3A45DA00FA5876 /* main.m */; }; 15 | 45EF72701C3A45DA00FA5876 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 45EF726F1C3A45DA00FA5876 /* AppDelegate.m */; }; 16 | 45EF72731C3A45DA00FA5876 /* ShowUIImagePickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 45EF72721C3A45DA00FA5876 /* ShowUIImagePickerViewController.m */; }; 17 | 45EF72761C3A45DA00FA5876 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 45EF72741C3A45DA00FA5876 /* Main.storyboard */; }; 18 | 45EF72781C3A45DA00FA5876 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 45EF72771C3A45DA00FA5876 /* Assets.xcassets */; }; 19 | 45EF727B1C3A45DA00FA5876 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 45EF72791C3A45DA00FA5876 /* LaunchScreen.storyboard */; }; 20 | 45EF72871C3A64A000FA5876 /* UIView+RMAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 45EF72861C3A64A000FA5876 /* UIView+RMAdditions.m */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 45AA64D41C3D2FF400159E3B /* AssetsLibrary.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AssetsLibrary.framework; path = System/Library/Frameworks/AssetsLibrary.framework; sourceTree = SDKROOT; }; 25 | 45AA64D61C3D320500159E3B /* AVKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVKit.framework; path = System/Library/Frameworks/AVKit.framework; sourceTree = SDKROOT; }; 26 | 45AA64D81C3D35FC00159E3B /* PostVideoPlayerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PostVideoPlayerController.h; sourceTree = ""; }; 27 | 45AA64D91C3D35FC00159E3B /* PostVideoPlayerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PostVideoPlayerController.m; sourceTree = ""; }; 28 | 45D8796F1C3BB5EC001AF659 /* LittleVideoController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LittleVideoController.h; sourceTree = ""; }; 29 | 45D879701C3BB5EC001AF659 /* LittleVideoController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LittleVideoController.m; sourceTree = ""; }; 30 | 45EF72681C3A45DA00FA5876 /* MKCustomCamera.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MKCustomCamera.app; sourceTree = BUILT_PRODUCTS_DIR; }; 31 | 45EF726C1C3A45DA00FA5876 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 32 | 45EF726E1C3A45DA00FA5876 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 33 | 45EF726F1C3A45DA00FA5876 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 34 | 45EF72711C3A45DA00FA5876 /* ShowUIImagePickerViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShowUIImagePickerViewController.h; sourceTree = ""; }; 35 | 45EF72721C3A45DA00FA5876 /* ShowUIImagePickerViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ShowUIImagePickerViewController.m; sourceTree = ""; }; 36 | 45EF72751C3A45DA00FA5876 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 37 | 45EF72771C3A45DA00FA5876 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 38 | 45EF727A1C3A45DA00FA5876 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 39 | 45EF727C1C3A45DA00FA5876 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 40 | 45EF72851C3A64A000FA5876 /* UIView+RMAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+RMAdditions.h"; sourceTree = ""; }; 41 | 45EF72861C3A64A000FA5876 /* UIView+RMAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+RMAdditions.m"; sourceTree = ""; }; 42 | /* End PBXFileReference section */ 43 | 44 | /* Begin PBXFrameworksBuildPhase section */ 45 | 45EF72651C3A45DA00FA5876 /* Frameworks */ = { 46 | isa = PBXFrameworksBuildPhase; 47 | buildActionMask = 2147483647; 48 | files = ( 49 | 45AA64D71C3D320500159E3B /* AVKit.framework in Frameworks */, 50 | 45AA64D51C3D2FF400159E3B /* AssetsLibrary.framework in Frameworks */, 51 | ); 52 | runOnlyForDeploymentPostprocessing = 0; 53 | }; 54 | /* End PBXFrameworksBuildPhase section */ 55 | 56 | /* Begin PBXGroup section */ 57 | 45EF725F1C3A45DA00FA5876 = { 58 | isa = PBXGroup; 59 | children = ( 60 | 45AA64D61C3D320500159E3B /* AVKit.framework */, 61 | 45AA64D41C3D2FF400159E3B /* AssetsLibrary.framework */, 62 | 45EF726A1C3A45DA00FA5876 /* MKCustomCamera */, 63 | 45EF72691C3A45DA00FA5876 /* Products */, 64 | ); 65 | sourceTree = ""; 66 | }; 67 | 45EF72691C3A45DA00FA5876 /* Products */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | 45EF72681C3A45DA00FA5876 /* MKCustomCamera.app */, 71 | ); 72 | name = Products; 73 | sourceTree = ""; 74 | }; 75 | 45EF726A1C3A45DA00FA5876 /* MKCustomCamera */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | 45EF726E1C3A45DA00FA5876 /* AppDelegate.h */, 79 | 45EF726F1C3A45DA00FA5876 /* AppDelegate.m */, 80 | 45EF72711C3A45DA00FA5876 /* ShowUIImagePickerViewController.h */, 81 | 45EF72721C3A45DA00FA5876 /* ShowUIImagePickerViewController.m */, 82 | 45D8796F1C3BB5EC001AF659 /* LittleVideoController.h */, 83 | 45D879701C3BB5EC001AF659 /* LittleVideoController.m */, 84 | 45AA64D81C3D35FC00159E3B /* PostVideoPlayerController.h */, 85 | 45AA64D91C3D35FC00159E3B /* PostVideoPlayerController.m */, 86 | 45EF72851C3A64A000FA5876 /* UIView+RMAdditions.h */, 87 | 45EF72861C3A64A000FA5876 /* UIView+RMAdditions.m */, 88 | 45EF72741C3A45DA00FA5876 /* Main.storyboard */, 89 | 45EF72771C3A45DA00FA5876 /* Assets.xcassets */, 90 | 45EF72791C3A45DA00FA5876 /* LaunchScreen.storyboard */, 91 | 45EF727C1C3A45DA00FA5876 /* Info.plist */, 92 | 45EF726B1C3A45DA00FA5876 /* Supporting Files */, 93 | ); 94 | path = MKCustomCamera; 95 | sourceTree = ""; 96 | }; 97 | 45EF726B1C3A45DA00FA5876 /* Supporting Files */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 45EF726C1C3A45DA00FA5876 /* main.m */, 101 | ); 102 | name = "Supporting Files"; 103 | sourceTree = ""; 104 | }; 105 | /* End PBXGroup section */ 106 | 107 | /* Begin PBXNativeTarget section */ 108 | 45EF72671C3A45DA00FA5876 /* MKCustomCamera */ = { 109 | isa = PBXNativeTarget; 110 | buildConfigurationList = 45EF727F1C3A45DA00FA5876 /* Build configuration list for PBXNativeTarget "MKCustomCamera" */; 111 | buildPhases = ( 112 | 45EF72641C3A45DA00FA5876 /* Sources */, 113 | 45EF72651C3A45DA00FA5876 /* Frameworks */, 114 | 45EF72661C3A45DA00FA5876 /* Resources */, 115 | ); 116 | buildRules = ( 117 | ); 118 | dependencies = ( 119 | ); 120 | name = MKCustomCamera; 121 | productName = MKCustomCamera; 122 | productReference = 45EF72681C3A45DA00FA5876 /* MKCustomCamera.app */; 123 | productType = "com.apple.product-type.application"; 124 | }; 125 | /* End PBXNativeTarget section */ 126 | 127 | /* Begin PBXProject section */ 128 | 45EF72601C3A45DA00FA5876 /* Project object */ = { 129 | isa = PBXProject; 130 | attributes = { 131 | LastUpgradeCheck = 0700; 132 | ORGANIZATIONNAME = MK; 133 | TargetAttributes = { 134 | 45EF72671C3A45DA00FA5876 = { 135 | CreatedOnToolsVersion = 7.0; 136 | DevelopmentTeam = 839GAQ3VG3; 137 | }; 138 | }; 139 | }; 140 | buildConfigurationList = 45EF72631C3A45DA00FA5876 /* Build configuration list for PBXProject "MKCustomCamera" */; 141 | compatibilityVersion = "Xcode 3.2"; 142 | developmentRegion = English; 143 | hasScannedForEncodings = 0; 144 | knownRegions = ( 145 | en, 146 | Base, 147 | ); 148 | mainGroup = 45EF725F1C3A45DA00FA5876; 149 | productRefGroup = 45EF72691C3A45DA00FA5876 /* Products */; 150 | projectDirPath = ""; 151 | projectRoot = ""; 152 | targets = ( 153 | 45EF72671C3A45DA00FA5876 /* MKCustomCamera */, 154 | ); 155 | }; 156 | /* End PBXProject section */ 157 | 158 | /* Begin PBXResourcesBuildPhase section */ 159 | 45EF72661C3A45DA00FA5876 /* Resources */ = { 160 | isa = PBXResourcesBuildPhase; 161 | buildActionMask = 2147483647; 162 | files = ( 163 | 45EF727B1C3A45DA00FA5876 /* LaunchScreen.storyboard in Resources */, 164 | 45EF72781C3A45DA00FA5876 /* Assets.xcassets in Resources */, 165 | 45EF72761C3A45DA00FA5876 /* Main.storyboard in Resources */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXResourcesBuildPhase section */ 170 | 171 | /* Begin PBXSourcesBuildPhase section */ 172 | 45EF72641C3A45DA00FA5876 /* Sources */ = { 173 | isa = PBXSourcesBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | 45EF72731C3A45DA00FA5876 /* ShowUIImagePickerViewController.m in Sources */, 177 | 45D879711C3BB5EC001AF659 /* LittleVideoController.m in Sources */, 178 | 45EF72871C3A64A000FA5876 /* UIView+RMAdditions.m in Sources */, 179 | 45EF72701C3A45DA00FA5876 /* AppDelegate.m in Sources */, 180 | 45AA64DA1C3D35FC00159E3B /* PostVideoPlayerController.m in Sources */, 181 | 45EF726D1C3A45DA00FA5876 /* main.m in Sources */, 182 | ); 183 | runOnlyForDeploymentPostprocessing = 0; 184 | }; 185 | /* End PBXSourcesBuildPhase section */ 186 | 187 | /* Begin PBXVariantGroup section */ 188 | 45EF72741C3A45DA00FA5876 /* Main.storyboard */ = { 189 | isa = PBXVariantGroup; 190 | children = ( 191 | 45EF72751C3A45DA00FA5876 /* Base */, 192 | ); 193 | name = Main.storyboard; 194 | sourceTree = ""; 195 | }; 196 | 45EF72791C3A45DA00FA5876 /* LaunchScreen.storyboard */ = { 197 | isa = PBXVariantGroup; 198 | children = ( 199 | 45EF727A1C3A45DA00FA5876 /* Base */, 200 | ); 201 | name = LaunchScreen.storyboard; 202 | sourceTree = ""; 203 | }; 204 | /* End PBXVariantGroup section */ 205 | 206 | /* Begin XCBuildConfiguration section */ 207 | 45EF727D1C3A45DA00FA5876 /* Debug */ = { 208 | isa = XCBuildConfiguration; 209 | buildSettings = { 210 | ALWAYS_SEARCH_USER_PATHS = NO; 211 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 212 | CLANG_CXX_LIBRARY = "libc++"; 213 | CLANG_ENABLE_MODULES = YES; 214 | CLANG_ENABLE_OBJC_ARC = YES; 215 | CLANG_WARN_BOOL_CONVERSION = YES; 216 | CLANG_WARN_CONSTANT_CONVERSION = YES; 217 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 218 | CLANG_WARN_EMPTY_BODY = YES; 219 | CLANG_WARN_ENUM_CONVERSION = YES; 220 | CLANG_WARN_INT_CONVERSION = YES; 221 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 222 | CLANG_WARN_UNREACHABLE_CODE = YES; 223 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 224 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 225 | COPY_PHASE_STRIP = NO; 226 | DEBUG_INFORMATION_FORMAT = dwarf; 227 | ENABLE_STRICT_OBJC_MSGSEND = YES; 228 | ENABLE_TESTABILITY = YES; 229 | GCC_C_LANGUAGE_STANDARD = gnu99; 230 | GCC_DYNAMIC_NO_PIC = NO; 231 | GCC_NO_COMMON_BLOCKS = YES; 232 | GCC_OPTIMIZATION_LEVEL = 0; 233 | GCC_PREPROCESSOR_DEFINITIONS = ( 234 | "DEBUG=1", 235 | "$(inherited)", 236 | ); 237 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 238 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 239 | GCC_WARN_UNDECLARED_SELECTOR = YES; 240 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 241 | GCC_WARN_UNUSED_FUNCTION = YES; 242 | GCC_WARN_UNUSED_VARIABLE = YES; 243 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 244 | MTL_ENABLE_DEBUG_INFO = YES; 245 | ONLY_ACTIVE_ARCH = YES; 246 | SDKROOT = iphoneos; 247 | TARGETED_DEVICE_FAMILY = "1,2"; 248 | }; 249 | name = Debug; 250 | }; 251 | 45EF727E1C3A45DA00FA5876 /* Release */ = { 252 | isa = XCBuildConfiguration; 253 | buildSettings = { 254 | ALWAYS_SEARCH_USER_PATHS = NO; 255 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 256 | CLANG_CXX_LIBRARY = "libc++"; 257 | CLANG_ENABLE_MODULES = YES; 258 | CLANG_ENABLE_OBJC_ARC = YES; 259 | CLANG_WARN_BOOL_CONVERSION = YES; 260 | CLANG_WARN_CONSTANT_CONVERSION = YES; 261 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 262 | CLANG_WARN_EMPTY_BODY = YES; 263 | CLANG_WARN_ENUM_CONVERSION = YES; 264 | CLANG_WARN_INT_CONVERSION = YES; 265 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 266 | CLANG_WARN_UNREACHABLE_CODE = YES; 267 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 268 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 269 | COPY_PHASE_STRIP = NO; 270 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 271 | ENABLE_NS_ASSERTIONS = NO; 272 | ENABLE_STRICT_OBJC_MSGSEND = YES; 273 | GCC_C_LANGUAGE_STANDARD = gnu99; 274 | GCC_NO_COMMON_BLOCKS = YES; 275 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 276 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 277 | GCC_WARN_UNDECLARED_SELECTOR = YES; 278 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 279 | GCC_WARN_UNUSED_FUNCTION = YES; 280 | GCC_WARN_UNUSED_VARIABLE = YES; 281 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 282 | MTL_ENABLE_DEBUG_INFO = NO; 283 | SDKROOT = iphoneos; 284 | TARGETED_DEVICE_FAMILY = "1,2"; 285 | VALIDATE_PRODUCT = YES; 286 | }; 287 | name = Release; 288 | }; 289 | 45EF72801C3A45DA00FA5876 /* Debug */ = { 290 | isa = XCBuildConfiguration; 291 | buildSettings = { 292 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 293 | CODE_SIGN_IDENTITY = "iPhone Developer"; 294 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 295 | INFOPLIST_FILE = MKCustomCamera/Info.plist; 296 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 297 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 298 | PRODUCT_BUNDLE_IDENTIFIER = com.mk.MKCustomCamera; 299 | PRODUCT_NAME = "$(TARGET_NAME)"; 300 | PROVISIONING_PROFILE = ""; 301 | }; 302 | name = Debug; 303 | }; 304 | 45EF72811C3A45DA00FA5876 /* Release */ = { 305 | isa = XCBuildConfiguration; 306 | buildSettings = { 307 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 308 | CODE_SIGN_IDENTITY = "iPhone Developer"; 309 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 310 | INFOPLIST_FILE = MKCustomCamera/Info.plist; 311 | IPHONEOS_DEPLOYMENT_TARGET = 7.1; 312 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 313 | PRODUCT_BUNDLE_IDENTIFIER = com.mk.MKCustomCamera; 314 | PRODUCT_NAME = "$(TARGET_NAME)"; 315 | PROVISIONING_PROFILE = ""; 316 | }; 317 | name = Release; 318 | }; 319 | /* End XCBuildConfiguration section */ 320 | 321 | /* Begin XCConfigurationList section */ 322 | 45EF72631C3A45DA00FA5876 /* Build configuration list for PBXProject "MKCustomCamera" */ = { 323 | isa = XCConfigurationList; 324 | buildConfigurations = ( 325 | 45EF727D1C3A45DA00FA5876 /* Debug */, 326 | 45EF727E1C3A45DA00FA5876 /* Release */, 327 | ); 328 | defaultConfigurationIsVisible = 0; 329 | defaultConfigurationName = Release; 330 | }; 331 | 45EF727F1C3A45DA00FA5876 /* Build configuration list for PBXNativeTarget "MKCustomCamera" */ = { 332 | isa = XCConfigurationList; 333 | buildConfigurations = ( 334 | 45EF72801C3A45DA00FA5876 /* Debug */, 335 | 45EF72811C3A45DA00FA5876 /* Release */, 336 | ); 337 | defaultConfigurationIsVisible = 0; 338 | defaultConfigurationName = Release; 339 | }; 340 | /* End XCConfigurationList section */ 341 | }; 342 | rootObject = 45EF72601C3A45DA00FA5876 /* Project object */; 343 | } 344 | -------------------------------------------------------------------------------- /MKCustomCamera/LittleVideoController.m: -------------------------------------------------------------------------------- 1 | // 2 | // LittleVideoController.m 3 | // MKCustomCamera 4 | // 5 | // Created by ykh on 16/1/5. 6 | // Copyright © 2016年 MK. All rights reserved. 7 | // 8 | 9 | #import "LittleVideoController.h" 10 | #import 11 | #import "UIView+RMAdditions.h" 12 | #import "PostVideoPlayerController.h" 13 | 14 | #define kDuration 6.0 15 | #define kTrans SCREEN_WIDTH/kDuration/60.0 16 | 17 | typedef NS_ENUM(NSInteger,VideoStatus){ 18 | VideoStatusEnded = 0, 19 | VideoStatusStarted 20 | }; 21 | 22 | @interface LittleVideoController(){ 23 | AVCaptureSession *_captureSession; 24 | AVCaptureDevice *_videoDevice; 25 | AVCaptureDevice *_audioDevice; 26 | AVCaptureDeviceInput *_videoInput; 27 | AVCaptureDeviceInput *_audioInput; 28 | AVCaptureMovieFileOutput *_movieOutput; 29 | AVCaptureVideoPreviewLayer *_captureVideoPreviewLayer; 30 | } 31 | 32 | @property (weak, nonatomic) IBOutlet NSLayoutConstraint *progressWidth; 33 | @property (weak, nonatomic) IBOutlet UIView *progressView; 34 | @property (weak, nonatomic) IBOutlet UILabel *cancelTip; 35 | @property (weak, nonatomic) IBOutlet UILabel *tapBtn; 36 | @property (weak, nonatomic) IBOutlet UIView *videoView; 37 | @property (weak, nonatomic) IBOutlet UIButton *changeBtn; 38 | @property (weak, nonatomic) IBOutlet UIButton *flashModelBtn; 39 | @property (nonatomic,weak) UIView *focusCircle; 40 | @property (nonatomic,assign) VideoStatus status; 41 | @property (nonatomic,assign) BOOL canSave; 42 | @property (nonatomic,strong) CADisplayLink *link; 43 | 44 | @end 45 | 46 | 47 | @implementation LittleVideoController 48 | // Do any additional setup after loading the view, typically from a nib. 49 | 50 | 51 | - (void)viewDidLoad 52 | { 53 | [super viewDidLoad]; 54 | [self initUI]; 55 | [self getAuthorization]; 56 | } 57 | 58 | - (void)initUI 59 | { 60 | [self.view bringSubviewToFront:self.cancelTip]; 61 | [self.view bringSubviewToFront:self.progressView]; 62 | [self.view bringSubviewToFront:self.changeBtn]; 63 | [self.view bringSubviewToFront:self.flashModelBtn]; 64 | self.videoView.layer.masksToBounds = YES; 65 | _tapBtn.layer.borderColor = [UIColor greenColor].CGColor; 66 | [self addGenstureRecognizer]; 67 | } 68 | 69 | 70 | //获取授权 71 | - (void)getAuthorization 72 | { 73 | /* 74 | AVAuthorizationStatusNotDetermined = 0,// 未进行授权选择 75 | 76 | AVAuthorizationStatusRestricted,    // 未授权,且用户无法更新,如家长控制情况下 77 | 78 | AVAuthorizationStatusDenied,       // 用户拒绝App使用 79 | 80 | AVAuthorizationStatusAuthorized,    // 已授权,可使用 81 | */ 82 | //此处获取摄像头授权 83 | switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) 84 | { 85 | case AVAuthorizationStatusAuthorized: //已授权,可使用 The client is authorized to access the hardware supporting a media type. 86 | { 87 | NSLog(@"授权摄像头使用成功"); 88 | [self setupAVCaptureInfo]; 89 | break; 90 | } 91 | case AVAuthorizationStatusNotDetermined: //未进行授权选择 Indicates that the user has not yet made a choice regarding whether the client can access the hardware. 92 | { 93 | //则再次请求授权 94 | [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) { 95 | if(granted){ //用户授权成功 96 | [self setupAVCaptureInfo]; 97 | return; 98 | } else { //用户拒绝授权 99 | [self pop]; 100 | [self showMsgWithTitle:@"出错了" andContent:@"用户拒绝授权摄像头的使用权,返回上一页.请打开\n设置-->隐私/通用等权限设置"]; 101 | return; 102 | } 103 | }]; 104 | break; 105 | } 106 | default: //用户拒绝授权/未授权 107 | { 108 | [self pop]; 109 | [self showMsgWithTitle:@"出错了" andContent:@"拒绝授权,返回上一页.请检查下\n设置-->隐私/通用等权限设置"]; 110 | break; 111 | } 112 | } 113 | 114 | } 115 | 116 | - (void)setupAVCaptureInfo 117 | { 118 | [self addSession]; 119 | 120 | [_captureSession beginConfiguration]; 121 | 122 | [self addVideo]; 123 | [self addAudio]; 124 | [self addPreviewLayer]; 125 | 126 | [_captureSession commitConfiguration]; 127 | 128 | //开启会话-->注意,不等于开始录制 129 | [_captureSession startRunning]; 130 | 131 | } 132 | 133 | - (void)addSession 134 | { 135 | _captureSession = [[AVCaptureSession alloc] init]; 136 | //设置视频分辨率 137 | /* 通常支持如下格式 138 | ( 139 | AVAssetExportPresetLowQuality, 140 | AVAssetExportPreset960x540, 141 | AVAssetExportPreset640x480, 142 | AVAssetExportPresetMediumQuality, 143 | AVAssetExportPreset1920x1080, 144 | AVAssetExportPreset1280x720, 145 | AVAssetExportPresetHighestQuality, 146 | AVAssetExportPresetAppleM4A 147 | ) 148 | */ 149 | //注意,这个地方设置的模式/分辨率大小将影响你后面拍摄照片/视频的大小, 150 | if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset640x480]) { 151 | [_captureSession setSessionPreset:AVCaptureSessionPreset640x480]; 152 | } 153 | } 154 | 155 | - (void)addVideo 156 | { 157 | // 获取摄像头输入设备, 创建 AVCaptureDeviceInput 对象 158 | /* MediaType 159 | AVF_EXPORT NSString *const AVMediaTypeVideo NS_AVAILABLE(10_7, 4_0); //视频 160 | AVF_EXPORT NSString *const AVMediaTypeAudio NS_AVAILABLE(10_7, 4_0); //音频 161 | AVF_EXPORT NSString *const AVMediaTypeText NS_AVAILABLE(10_7, 4_0); 162 | AVF_EXPORT NSString *const AVMediaTypeClosedCaption NS_AVAILABLE(10_7, 4_0); 163 | AVF_EXPORT NSString *const AVMediaTypeSubtitle NS_AVAILABLE(10_7, 4_0); 164 | AVF_EXPORT NSString *const AVMediaTypeTimecode NS_AVAILABLE(10_7, 4_0); 165 | AVF_EXPORT NSString *const AVMediaTypeMetadata NS_AVAILABLE(10_8, 6_0); 166 | AVF_EXPORT NSString *const AVMediaTypeMuxed NS_AVAILABLE(10_7, 4_0); 167 | */ 168 | 169 | /* AVCaptureDevicePosition 170 | typedef NS_ENUM(NSInteger, AVCaptureDevicePosition) { 171 | AVCaptureDevicePositionUnspecified = 0, 172 | AVCaptureDevicePositionBack = 1, //后置摄像头 173 | AVCaptureDevicePositionFront = 2 //前置摄像头 174 | } NS_AVAILABLE(10_7, 4_0) __TVOS_PROHIBITED; 175 | */ 176 | _videoDevice = [self deviceWithMediaType:AVMediaTypeVideo preferringPosition:AVCaptureDevicePositionBack]; 177 | 178 | [self addVideoInput]; 179 | [self addMovieOutput]; 180 | } 181 | 182 | - (void)addVideoInput 183 | { 184 | NSError *videoError; 185 | 186 | // 视频输入对象 187 | // 根据输入设备初始化输入对象,用户获取输入数据 188 | _videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:_videoDevice error:&videoError]; 189 | if (videoError) { 190 | NSLog(@"---- 取得摄像头设备时出错 ------ %@",videoError); 191 | return; 192 | } 193 | 194 | // 将视频输入对象添加到会话 (AVCaptureSession) 中 195 | if ([_captureSession canAddInput:_videoInput]) { 196 | [_captureSession addInput:_videoInput]; 197 | } 198 | 199 | } 200 | 201 | - (void)addMovieOutput 202 | { 203 | // 拍摄视频输出对象 204 | // 初始化输出设备对象,用户获取输出数据 205 | _movieOutput = [[AVCaptureMovieFileOutput alloc] init]; 206 | 207 | if ([_captureSession canAddOutput:_movieOutput]) { 208 | [_captureSession addOutput:_movieOutput]; 209 | AVCaptureConnection *captureConnection = [_movieOutput connectionWithMediaType:AVMediaTypeVideo]; 210 | 211 | //设置视频旋转方向 212 | /* 213 | typedef NS_ENUM(NSInteger, AVCaptureVideoOrientation) { 214 | AVCaptureVideoOrientationPortrait = 1, 215 | AVCaptureVideoOrientationPortraitUpsideDown = 2, 216 | AVCaptureVideoOrientationLandscapeRight = 3, 217 | AVCaptureVideoOrientationLandscapeLeft = 4, 218 | } NS_AVAILABLE(10_7, 4_0) __TVOS_PROHIBITED; 219 | */ 220 | // if ([captureConnection isVideoOrientationSupported]) { 221 | // [captureConnection setVideoOrientation:AVCaptureVideoOrientationPortrait]; 222 | // } 223 | 224 | // 视频稳定设置 225 | if ([captureConnection isVideoStabilizationSupported]) { 226 | captureConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto; 227 | } 228 | 229 | captureConnection.videoScaleAndCropFactor = captureConnection.videoMaxScaleAndCropFactor; 230 | } 231 | 232 | } 233 | 234 | - (void)addAudio 235 | { 236 | NSError *audioError; 237 | // 添加一个音频输入设备 238 | _audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio]; 239 | // 音频输入对象 240 | _audioInput = [[AVCaptureDeviceInput alloc] initWithDevice:_audioDevice error:&audioError]; 241 | if (audioError) { 242 | NSLog(@"取得录音设备时出错 ------ %@",audioError); 243 | return; 244 | } 245 | // 将音频输入对象添加到会话 (AVCaptureSession) 中 246 | if ([_captureSession canAddInput:_audioInput]) { 247 | [_captureSession addInput:_audioInput]; 248 | } 249 | } 250 | 251 | - (void)addPreviewLayer 252 | { 253 | 254 | [self.view layoutIfNeeded]; 255 | 256 | // 通过会话 (AVCaptureSession) 创建预览层 257 | _captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession]; 258 | _captureVideoPreviewLayer.frame = self.view.layer.bounds; 259 | /* 填充模式 260 | Options are AVLayerVideoGravityResize, AVLayerVideoGravityResizeAspect and AVLayerVideoGravityResizeAspectFill. AVLayerVideoGravityResizeAspect is default. 261 | */ 262 | //有时候需要拍摄完整屏幕大小的时候可以修改这个 263 | // _captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; 264 | // 如果预览图层和视频方向不一致,可以修改这个 265 | _captureVideoPreviewLayer.connection.videoOrientation = [_movieOutput connectionWithMediaType:AVMediaTypeVideo].videoOrientation; 266 | _captureVideoPreviewLayer.position = CGPointMake(self.view.width*0.5,self.videoView.height*0.5); 267 | 268 | // 显示在视图表面的图层 269 | CALayer *layer = self.videoView.layer; 270 | layer.masksToBounds = true; 271 | [self.view layoutIfNeeded]; 272 | [layer addSublayer:_captureVideoPreviewLayer]; 273 | 274 | } 275 | 276 | #pragma mark pop 277 | 278 | -(void)pop 279 | { 280 | if (self.navigationController) { 281 | [self.navigationController popViewControllerAnimated:YES]; 282 | } 283 | } 284 | 285 | - (void)showMsgWithTitle:(NSString *)title andContent:(NSString *)content 286 | { 287 | [[[UIAlertView alloc] initWithTitle:title message:content delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil] show]; 288 | } 289 | 290 | #pragma mark 获取摄像头-->前/后 291 | 292 | - (AVCaptureDevice *)deviceWithMediaType:(NSString *)mediaType preferringPosition:(AVCaptureDevicePosition)position 293 | { 294 | NSArray *devices = [AVCaptureDevice devicesWithMediaType:mediaType]; 295 | AVCaptureDevice *captureDevice = devices.firstObject; 296 | 297 | for ( AVCaptureDevice *device in devices ) { 298 | if ( device.position == position ) { 299 | captureDevice = device; 300 | break; 301 | } 302 | } 303 | 304 | return captureDevice; 305 | } 306 | 307 | //下面这2个也可以获取前后摄像头,不过有一定的风险,假如手机又问题,找不到对应的 UniqueID 设备,则呵呵了 308 | //- (AVCaptureDevice *)frontCamera 309 | //{ 310 | // return [AVCaptureDevice deviceWithUniqueID:@"com.apple.avfoundation.avcapturedevice.built-in_video:1"]; 311 | //} 312 | // 313 | //- (AVCaptureDevice *)backCamera 314 | //{ 315 | // return [AVCaptureDevice deviceWithUniqueID:@"com.apple.avfoundation.avcapturedevice.built-in_video:0"]; 316 | //} 317 | 318 | #pragma mark touchs 319 | 320 | -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 321 | { 322 | NSLog(@"touch"); 323 | 324 | UITouch *touch = [touches anyObject]; 325 | CGPoint point = [touch locationInView:self.view]; 326 | BOOL condition = [self isInBtnRect:point]; 327 | 328 | if (condition) { 329 | [self isFitCondition:condition]; 330 | [self startAnimation]; 331 | self.changeBtn.hidden= self.flashModelBtn.hidden = YES; 332 | } 333 | } 334 | 335 | - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 336 | { 337 | [super touchesMoved:touches withEvent:event]; 338 | 339 | UITouch *touch = [touches anyObject]; 340 | CGPoint point = [touch locationInView:self.view]; 341 | BOOL condition = [self isInBtnRect:point]; 342 | 343 | [self isFitCondition:condition]; 344 | } 345 | 346 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 347 | { 348 | UITouch *touch = [touches anyObject]; 349 | CGPoint point = [touch locationInView:self.view]; 350 | BOOL condition = [self isInBtnRect:point]; 351 | /* 352 | 结束时候咱们设定有两种情况依然算录制成功 353 | 1.抬手时,录制时长 > 1/3总时长 354 | 2.录制进度条完成时,就算手指超出按钮范围也算录制成功 -- 此时 end 方法不会调用,因为用户手指还在屏幕上,所以直接代码调用录制成功的方法,将控制器切换 355 | */ 356 | 357 | if (condition) { 358 | if (self.progressWidth.constant < SCREEN_WIDTH * 0.67) { 359 | //录制完成 360 | [self recordComplete]; 361 | } 362 | } 363 | 364 | [self stopAnimation]; 365 | self.changeBtn.hidden = self.flashModelBtn.hidden = NO; 366 | } 367 | 368 | - (BOOL)isInBtnRect:(CGPoint)point 369 | { 370 | CGFloat x = point.x; 371 | CGFloat y = point.y; 372 | return (x>self.tapBtn.left && x<=self.tapBtn.right) && (y>self.tapBtn.top && y<=self.tapBtn.bottom); 373 | } 374 | 375 | - (void)isFitCondition:(BOOL)condition 376 | { 377 | if (condition) { 378 | self.cancelTip.text = @"上滑取消"; 379 | self.cancelTip.backgroundColor = [UIColor orangeColor]; 380 | self.cancelTip.textColor = [UIColor blackColor]; 381 | }else{ 382 | self.cancelTip.text = @"松手取消录制"; 383 | self.cancelTip.backgroundColor = [UIColor redColor]; 384 | self.cancelTip.textColor = [UIColor whiteColor]; 385 | } 386 | } 387 | 388 | - (void)startAnimation 389 | { 390 | if (self.status == VideoStatusEnded) { 391 | self.status = VideoStatusStarted; 392 | [UIView animateWithDuration:0.5 animations:^{ 393 | self.cancelTip.alpha = self.progressView.alpha = 1.0; 394 | self.tapBtn.alpha = 0.0; 395 | self.tapBtn.transform = CGAffineTransformMakeScale(2.0, 2.0); 396 | } completion:^(BOOL finished) { 397 | [self stopLink]; 398 | [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; 399 | }]; 400 | } 401 | } 402 | 403 | - (void)stopAnimation{ 404 | if (self.status == VideoStatusStarted) { 405 | self.status = VideoStatusEnded; 406 | 407 | [self stopLink]; 408 | [self stopRecord]; 409 | 410 | [UIView animateWithDuration:0.5 animations:^{ 411 | self.cancelTip.alpha = self.progressView.alpha = 0.0; 412 | self.tapBtn.alpha = 1.0; 413 | self.tapBtn.transform = CGAffineTransformMakeScale(1.0, 1.0); 414 | } completion:^(BOOL finished) { 415 | self.progressWidth.constant = SCREEN_WIDTH; 416 | }]; 417 | } 418 | } 419 | 420 | - (CADisplayLink *)link 421 | { 422 | if (!_link) { 423 | _link = [CADisplayLink displayLinkWithTarget:self selector:@selector(refresh:)]; 424 | self.progressWidth.constant = SCREEN_WIDTH; 425 | [self startRecord]; 426 | } 427 | return _link; 428 | } 429 | 430 | - (void)stopLink 431 | { 432 | _link.paused = YES; 433 | [_link invalidate]; 434 | _link = nil; 435 | } 436 | 437 | - (void)refresh:(CADisplayLink *)link 438 | { 439 | if (self.progressWidth.constant <= 0) { 440 | self.progressWidth.constant = 0; 441 | [self recordComplete]; 442 | [self stopAnimation]; 443 | return; 444 | } 445 | self.progressWidth.constant -=kTrans; 446 | } 447 | 448 | #pragma mark 录制相关 449 | 450 | - (NSURL *)outPutFileURL 451 | { 452 | return [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@%@", NSTemporaryDirectory(), @"outPut.mov"]]; 453 | } 454 | 455 | - (void)startRecord 456 | { 457 | [_movieOutput startRecordingToOutputFileURL:[self outPutFileURL] recordingDelegate:self]; 458 | } 459 | 460 | - (void)stopRecord 461 | { 462 | // 取消视频拍摄 463 | [_movieOutput stopRecording]; 464 | } 465 | 466 | - (void)recordComplete 467 | { 468 | self.canSave = YES; 469 | } 470 | 471 | //这个在完全退出小视频时调用 472 | - (void)quit 473 | { 474 | [_captureSession stopRunning]; 475 | } 476 | 477 | - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didStartRecordingToOutputFileAtURL:(NSURL *)fileURL fromConnections:(NSArray *)connections 478 | { 479 | NSLog(@"---- 开始录制 ----"); 480 | } 481 | 482 | - (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error 483 | { 484 | NSLog(@"---- 录制结束 ---%@-%@ ",outputFileURL,captureOutput.outputFileURL); 485 | 486 | if (outputFileURL.absoluteString.length == 0 && captureOutput.outputFileURL.absoluteString.length == 0 ) { 487 | [self showMsgWithTitle:@"出错了" andContent:@"录制视频保存地址出错"]; 488 | return; 489 | } 490 | 491 | if (self.canSave) { 492 | [self pushToPlay:outputFileURL]; 493 | self.canSave = NO; 494 | } 495 | } 496 | 497 | - (void)pushToPlay:(NSURL *)url 498 | { 499 | PostVideoPlayerController *postVC = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"PostVideoPlayerController"]; 500 | postVC.videoUrl = url; 501 | [self.navigationController pushViewController:postVC animated:YES]; 502 | } 503 | 504 | 505 | #pragma mark 交互 506 | 507 | //切换闪光灯 闪光模式开启后,并无明显感觉,所以还需要开启手电筒 508 | - (IBAction)changeFlashlight:(UIButton *)sender { 509 | 510 | BOOL con1 = [_videoDevice hasTorch]; //支持手电筒模式 511 | BOOL con2 = [_videoDevice hasFlash]; //支持闪光模式 512 | 513 | if (con1 && con2) 514 | { 515 | [self changeDevicePropertySafety:^(AVCaptureDevice *captureDevice) { 516 | if (_videoDevice.flashMode == AVCaptureFlashModeOn) //闪光灯开 517 | { 518 | [_videoDevice setFlashMode:AVCaptureFlashModeOff]; 519 | [_videoDevice setTorchMode:AVCaptureTorchModeOff]; 520 | }else if (_videoDevice.flashMode == AVCaptureFlashModeOff) //闪光灯关 521 | { 522 | [_videoDevice setFlashMode:AVCaptureFlashModeOn]; 523 | [_videoDevice setTorchMode:AVCaptureTorchModeOn]; 524 | } 525 | // else{ //闪光灯自动 526 | // [_videoDevice setFlashMode:AVCaptureFlashModeAuto]; 527 | // [_videoDevice setTorchMode:AVCaptureTorchModeAuto]; 528 | // } 529 | NSLog(@"现在的闪光模式是AVCaptureFlashModeOn么?是你就扣1, %zd",_videoDevice.flashMode == AVCaptureFlashModeOn); 530 | }]; 531 | sender.selected=!sender.isSelected; 532 | }else{ 533 | NSLog(@"不能切换闪光模式"); 534 | } 535 | } 536 | 537 | //切换前后镜头 538 | - (IBAction)changeCamera{ 539 | 540 | switch (_videoDevice.position) { 541 | case AVCaptureDevicePositionBack: 542 | _videoDevice = [self deviceWithMediaType:AVMediaTypeVideo preferringPosition:AVCaptureDevicePositionFront]; 543 | break; 544 | case AVCaptureDevicePositionFront: 545 | _videoDevice = [self deviceWithMediaType:AVMediaTypeVideo preferringPosition:AVCaptureDevicePositionBack]; 546 | break; 547 | default: 548 | return; 549 | break; 550 | } 551 | 552 | [self changeDevicePropertySafety:^(AVCaptureDevice *captureDevice) { 553 | NSError *error; 554 | AVCaptureDeviceInput *newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:_videoDevice error:&error]; 555 | 556 | if (newVideoInput != nil) { 557 | //必选先 remove 才能询问 canAdd 558 | [_captureSession removeInput:_videoInput]; 559 | if ([_captureSession canAddInput:newVideoInput]) { 560 | [_captureSession addInput:newVideoInput]; 561 | _videoInput = newVideoInput; 562 | }else{ 563 | [_captureSession addInput:_videoInput]; 564 | } 565 | 566 | } else if (error) { 567 | NSLog(@"切换前/后摄像头失败, error = %@", error); 568 | } 569 | }]; 570 | 571 | } 572 | 573 | /** 574 | * 添加点按手势,点按时聚焦 575 | */ 576 | -(void)addGenstureRecognizer{ 577 | 578 | UITapGestureRecognizer *singleTapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(singleTap:)]; 579 | singleTapGesture.numberOfTapsRequired = 1; 580 | singleTapGesture.delaysTouchesBegan = YES; 581 | 582 | UITapGestureRecognizer *doubleTapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(doubleTap:)]; 583 | doubleTapGesture.numberOfTapsRequired = 2; 584 | doubleTapGesture.delaysTouchesBegan = YES; 585 | 586 | [singleTapGesture requireGestureRecognizerToFail:doubleTapGesture]; 587 | [self.videoView addGestureRecognizer:singleTapGesture]; 588 | [self.videoView addGestureRecognizer:doubleTapGesture]; 589 | } 590 | 591 | -(void)singleTap:(UITapGestureRecognizer *)tapGesture{ 592 | 593 | NSLog(@"单击"); 594 | 595 | CGPoint point= [tapGesture locationInView:self.videoView]; 596 | //将UI坐标转化为摄像头坐标,摄像头聚焦点范围0~1 597 | CGPoint cameraPoint= [_captureVideoPreviewLayer captureDevicePointOfInterestForPoint:point]; 598 | [self setFocusCursorAnimationWithPoint:point]; 599 | 600 | [self changeDevicePropertySafety:^(AVCaptureDevice *captureDevice) { 601 | 602 | /* 603 | @constant AVCaptureFocusModeLocked 锁定在当前焦距 604 | Indicates that the focus should be locked at the lens' current position. 605 | 606 | @constant AVCaptureFocusModeAutoFocus 自动对焦一次,然后切换到焦距锁定 607 | Indicates that the device should autofocus once and then change the focus mode to AVCaptureFocusModeLocked. 608 | 609 | @constant AVCaptureFocusModeContinuousAutoFocus 当需要时.自动调整焦距 610 | Indicates that the device should automatically focus when needed. 611 | */ 612 | //聚焦 613 | if ([captureDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) { 614 | [captureDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus]; 615 | NSLog(@"聚焦模式修改为%zd",AVCaptureFocusModeContinuousAutoFocus); 616 | }else{ 617 | NSLog(@"聚焦模式修改失败"); 618 | } 619 | 620 | //聚焦点的位置 621 | if ([captureDevice isFocusPointOfInterestSupported]) { 622 | [captureDevice setFocusPointOfInterest:cameraPoint]; 623 | } 624 | 625 | /* 626 | @constant AVCaptureExposureModeLocked 曝光锁定在当前值 627 | Indicates that the exposure should be locked at its current value. 628 | 629 | @constant AVCaptureExposureModeAutoExpose 曝光自动调整一次然后锁定 630 | Indicates that the device should automatically adjust exposure once and then change the exposure mode to AVCaptureExposureModeLocked. 631 | 632 | @constant AVCaptureExposureModeContinuousAutoExposure 曝光自动调整 633 | Indicates that the device should automatically adjust exposure when needed. 634 | 635 | @constant AVCaptureExposureModeCustom 曝光只根据设定的值来 636 | Indicates that the device should only adjust exposure according to user provided ISO, exposureDuration values. 637 | 638 | */ 639 | //曝光模式 640 | if ([captureDevice isExposureModeSupported:AVCaptureExposureModeAutoExpose]) { 641 | [captureDevice setExposureMode:AVCaptureExposureModeAutoExpose]; 642 | }else{ 643 | NSLog(@"曝光模式修改失败"); 644 | } 645 | 646 | //曝光点的位置 647 | if ([captureDevice isExposurePointOfInterestSupported]) { 648 | [captureDevice setExposurePointOfInterest:cameraPoint]; 649 | } 650 | 651 | 652 | }]; 653 | } 654 | 655 | 656 | //设置焦距 657 | -(void)doubleTap:(UITapGestureRecognizer *)tapGesture{ 658 | 659 | NSLog(@"双击"); 660 | 661 | [self changeDevicePropertySafety:^(AVCaptureDevice *captureDevice) { 662 | if (captureDevice.videoZoomFactor == 1.0) { 663 | CGFloat current = 1.5; 664 | if (current < captureDevice.activeFormat.videoMaxZoomFactor) { 665 | [captureDevice rampToVideoZoomFactor:current withRate:10]; 666 | } 667 | }else{ 668 | [captureDevice rampToVideoZoomFactor:1.0 withRate:10]; 669 | } 670 | }]; 671 | } 672 | 673 | //光圈动画 674 | -(void)setFocusCursorAnimationWithPoint:(CGPoint)point{ 675 | self.focusCircle.center = point; 676 | self.focusCircle.transform = CGAffineTransformIdentity; 677 | self.focusCircle.alpha = 1.0; 678 | [UIView animateWithDuration:0.5 animations:^{ 679 | self.focusCircle.transform=CGAffineTransformMakeScale(0.5, 0.5); 680 | self.focusCircle.alpha = 0.0; 681 | }]; 682 | } 683 | 684 | //光圈 685 | - (UIView *)focusCircle{ 686 | if (!_focusCircle) { 687 | UIView *focusCircle = [[UIView alloc] init]; 688 | focusCircle.frame = CGRectMake(0, 0, 100, 100); 689 | focusCircle.layer.borderColor = [UIColor orangeColor].CGColor; 690 | focusCircle.layer.borderWidth = 2; 691 | focusCircle.layer.cornerRadius = 50; 692 | focusCircle.layer.masksToBounds =YES; 693 | _focusCircle = focusCircle; 694 | [self.videoView addSubview:focusCircle]; 695 | } 696 | return _focusCircle; 697 | } 698 | 699 | //更改设备属性前一定要锁上 700 | -(void)changeDevicePropertySafety:(void (^)(AVCaptureDevice *captureDevice))propertyChange{ 701 | //也可以直接用_videoDevice,但是下面这种更好 702 | AVCaptureDevice *captureDevice= [_videoInput device]; 703 | NSError *error; 704 | //注意改变设备属性前一定要首先调用lockForConfiguration:调用完之后使用unlockForConfiguration方法解锁,意义是---进行修改期间,先锁定,防止多处同时修改 705 | BOOL lockAcquired = [captureDevice lockForConfiguration:&error]; 706 | if (!lockAcquired) { 707 | NSLog(@"锁定设备过程error,错误信息:%@",error.localizedDescription); 708 | }else{ 709 | [_captureSession beginConfiguration]; 710 | propertyChange(captureDevice); 711 | [captureDevice unlockForConfiguration]; 712 | [_captureSession commitConfiguration]; 713 | } 714 | } 715 | 716 | @end 717 | -------------------------------------------------------------------------------- /MKCustomCamera/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 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 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 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 | 94 | 101 | 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 | 174 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 202 | 220 | 226 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | --------------------------------------------------------------------------------