├── uimac.png ├── UIMedia.m ├── UIMedia.h ├── LICENSE ├── UIMediaAlertController.h ├── README.md └── UIMediaAlertController.m /uimac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iosdec/UIMediaAlertController/HEAD/uimac.png -------------------------------------------------------------------------------- /UIMedia.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIMedia.m 3 | // UIMediaAlertController 4 | // 5 | // Created by R3V0 on 25/05/2018. 6 | // Copyright © 2018 iosdec. All rights reserved. 7 | // 8 | 9 | #import "UIMedia.h" 10 | 11 | @implementation UIMedia 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /UIMedia.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIMedia.h 3 | // UIMediaAlertController 4 | // 5 | // Created by R3V0 on 25/05/2018. 6 | // Copyright © 2018 iosdec. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface UIMedia : NSObject 13 | 14 | @property (strong, nonatomic) UIImage *image; 15 | @property (strong, nonatomic) NSURL *imageURL; 16 | @property (strong, nonatomic) NSURL *videoURL; 17 | @property (strong, nonatomic) UIImage *videoThumb; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Declan Land 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /UIMediaAlertController.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIMediaAlertController.h 3 | // UIMediaAlertController 4 | // 5 | // Created by R3V0 on 25/05/2018. 6 | // Copyright © 2018 iosdec. All rights reserved. 7 | // 8 | 9 | #import "UIMedia.h" 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | 17 | #define kUIM_MediaWidth 250 18 | #define kUIM_MediaHeight 230 19 | #define kUIM_LineSpacing 16.5 20 | 21 | typedef enum { 22 | MediaTypeImage = 0, 23 | MediaTypeVideo = 1, 24 | }MediaType; 25 | 26 | @interface UIMediaAlertController : NSObject 27 | 28 | typedef void (^mediaPicked)(void); 29 | typedef void (^mediaRemoved)(void); 30 | 31 | @property (strong, nonatomic) UIViewController *controller; 32 | @property (nonatomic, assign) MediaType type; 33 | @property (strong, nonatomic) UIMedia *media; 34 | @property (nonatomic, copy) mediaPicked picked; 35 | @property (nonatomic, copy) mediaRemoved removed; 36 | @property (nonatomic, assign) BOOL autoplayDisabled; 37 | @property (nonatomic, assign) BOOL soundDisabled; 38 | @property (strong, nonatomic) NSString *title; 39 | @property (strong, nonatomic) NSString *message; 40 | @property (strong, nonatomic) UIImageView *imageView; 41 | @property (strong, nonatomic) UIView *videoView; 42 | @property (strong, nonatomic) AVPlayer *playerRef; 43 | @property (nonatomic, assign) NSUInteger delay; 44 | 45 | // Presentation Options: 46 | 47 | + (void)presentWithType:(MediaType)type picked:(void(^)(void))picked; 48 | + (void)presentWithType:(MediaType)type picked:(void (^)(void))picked removed:(void(^)(void))removed; 49 | + (void)resetMedia; 50 | + (UIMedia *)media; 51 | - (void)presentWithType:(MediaType)type picked:(void(^)(void))picked; 52 | - (void)presentWithType:(MediaType)type picked:(void(^)(void))picked removed:(void(^)(void))removed; 53 | - (void)resetMedia; 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UIMediaAlertController 2 | 3 | UIAlertController extension for selecting images / videos.
4 | Written in *Objective-C*. 5 | 6 | *Created By: [Declan Land](https://twitter.com/declanland)* 7 | 8 | **Features**
9 | * Completion handlers
10 | * Ease of use
11 | * Media Preview
12 | * Customisable
13 | 14 | ![Screenshot](uimac.png) 15 | 16 | --- 17 | 18 | ### Installation 19 | 20 | Download the contents of this repo and add these files to your project:
21 | ``` 22 | UIMediaAlertController.h 23 | UIMediaAlertController.m 24 | UIMedia.h 25 | UIMedia.m 26 | ``` 27 | Include these frameworks in your project: 28 | ```objc 29 | #import 30 | #import 31 | #import 32 | ``` 33 | --- 34 | 35 | ### Usage 36 | 37 | First, import the *UIMediaAlertController.h* file into the place you want to 38 | use it: 39 | ```objc 40 | #import "UIMediaAlertController.h" 41 | ``` 42 | 43 | There are 2 methods of using the UIMediaAlertController: 44 | - Shared Instance 45 | - Stored Property 46 | 47 | The benefits of using the shared instance is that it's really simple to call, 48 | and that the same UIMedia object is used throughout the app - unless you call: 49 | [UIMediaAlertController resetMedia]; 50 | For example: 51 | 52 | ```objc 53 | [UIMediaAlertController presentWithType:MediaTypeImage picked:^{ 54 | // Retreive global media by using: 55 | UIMedia *media = [UIMediaAlertController media]; 56 | // Reset global media: 57 | [UIMediaAlertController resetMedia]; 58 | }]; 59 | ``` 60 | 61 | The other method, is storing the UIMediaAlertController property in your 62 | header file *MyViewController.h* like so: 63 | 64 | ```objc 65 | // MyViewController.h 66 | 67 | #import "UIMediaAlertController.h" 68 | 69 | @implementation MyViewController : UIViewController 70 | 71 | @property (strong, nonatomic) UIMediaAlertController *uimac; 72 | 73 | @end 74 | 75 | ``` 76 | 77 | *MyViewController.m*": 78 | 79 | ```objc 80 | 81 | @implementation MyViewController 82 | 83 | - (IBAction)chooseImageButtonClicked:(UIButton *)sender { 84 | self.uimac = [[UIMediaAlertController alloc] init]; 85 | [self.uimac presentWithType:MediaTypeImage picked:^{ 86 | // get media: 87 | UIMedia *media = self.uimac.media; 88 | // resent media: 89 | [self.uimac resetMedia]; 90 | }]; 91 | } 92 | 93 | @end 94 | 95 | ``` 96 | -------------------------------------------------------------------------------- /UIMediaAlertController.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIMediaAlertController.m 3 | // UIMediaAlertController 4 | // 5 | // Created by R3V0 on 25/05/2018. 6 | // Copyright © 2018 iosdec. All rights reserved. 7 | // 8 | 9 | #import "UIMediaAlertController.h" 10 | 11 | @implementation UIMediaAlertController 12 | 13 | __strong UIMediaAlertController *sharedController; 14 | __strong UIMedia *sharedMedia; 15 | 16 | + (void)presentWithType:(MediaType)type picked:(void (^)(void))picked { 17 | if (!sharedController) { 18 | sharedController = [[UIMediaAlertController alloc] init]; 19 | } if (!sharedMedia) { 20 | sharedMedia = [[UIMedia alloc] init]; 21 | } 22 | [sharedController setController:[sharedController findTopViewController]]; 23 | [sharedController setType:type]; 24 | [sharedController setPicked:picked]; 25 | [sharedController setRemoved:nil]; 26 | [sharedController removeObjects]; 27 | if (!sharedController.media) { 28 | sharedController.media = sharedMedia; 29 | } 30 | [sharedController showAlertController_pre:sharedController]; 31 | } 32 | 33 | + (void)presentWithType:(MediaType)type picked:(void (^)(void))picked removed:(void (^)(void))removed { 34 | if (!sharedController) { 35 | sharedController = [[UIMediaAlertController alloc] init]; 36 | } if (!sharedMedia) { 37 | sharedMedia = [[UIMedia alloc] init]; 38 | } 39 | [sharedController setController:[sharedController findTopViewController]]; 40 | [sharedController setType:type]; 41 | [sharedController setPicked:picked]; 42 | [sharedController setRemoved:removed]; 43 | [sharedController removeObjects]; 44 | if (!sharedController.media) { 45 | sharedController.media = sharedMedia; 46 | } 47 | [sharedController showAlertController_pre:sharedController]; 48 | } 49 | 50 | + (void)resetMedia { 51 | sharedMedia.image = nil; 52 | sharedMedia.imageURL = nil; 53 | sharedMedia.videoURL = nil; 54 | sharedMedia.videoThumb = nil; 55 | } 56 | 57 | + (UIMedia *)media { 58 | if (sharedMedia) { return sharedMedia; } 59 | else { sharedMedia = [[UIMedia alloc] init]; return sharedMedia; } 60 | } 61 | 62 | - (void)presentWithType:(MediaType)type picked:(void (^)(void))picked { 63 | [self setController:[self findTopViewController]]; 64 | [self setType:type]; 65 | [self setPicked:picked]; 66 | [self setRemoved:nil]; 67 | [self removeObjects]; 68 | if (!self.media) { 69 | self.media = [[UIMedia alloc] init]; 70 | } 71 | [self showAlertController_pre:self]; 72 | } 73 | 74 | - (void)presentWithType:(MediaType)type picked:(void (^)(void))picked removed:(void (^)(void))removed { 75 | [self setController:[self findTopViewController]]; 76 | [self setType:type]; 77 | [self setPicked:picked]; 78 | [self setRemoved:removed]; 79 | [self removeObjects]; 80 | if (!self.media) { 81 | self.media = [[UIMedia alloc] init]; 82 | } 83 | [self showAlertController_pre:self]; 84 | } 85 | 86 | - (void)resetMedia { 87 | self.media.image = nil; 88 | self.media.imageURL = nil; 89 | self.media.videoURL = nil; 90 | self.media.videoThumb = nil; 91 | } 92 | 93 | #pragma mark - Present Preperation: 94 | 95 | - (void)showAlertController_pre:(UIMediaAlertController *)instance { 96 | if (self.delay) { 97 | [instance performSelector:@selector(showAlertController) withObject:nil afterDelay:self.delay]; 98 | return; 99 | } else { 100 | [self showAlertController]; 101 | } 102 | } 103 | 104 | - (void)showAlertController { 105 | 106 | // check if type is image: 107 | if (self.type == MediaTypeImage) { 108 | 109 | self.title = @"Add Image"; 110 | self.message = @"\n\nNo Image\n\n"; 111 | 112 | if (self.media.image) { 113 | self.title = @"Image Options"; 114 | self.message = @""; 115 | NSUInteger lines = round(kUIM_MediaHeight/kUIM_LineSpacing); 116 | for (int i = 0; i < lines; i++) { 117 | self.message = [self.message stringByAppendingString:@"\n"]; 118 | } 119 | } 120 | 121 | self.imageView = [[UIImageView alloc] init]; 122 | self.imageView.frame = CGRectMake(0, 62, kUIM_MediaWidth, kUIM_MediaHeight); 123 | self.imageView.contentMode = UIViewContentModeScaleAspectFill; 124 | self.imageView.center = CGPointMake(self.controller.view.center.x-10, self.imageView.center.y); 125 | self.imageView.image = self.media.image; 126 | self.imageView.layer.cornerRadius = 5; 127 | self.imageView.clipsToBounds = YES; 128 | 129 | } 130 | 131 | // check if type is video: 132 | if (self.type == MediaTypeVideo) { 133 | 134 | self.title = @"Add Video"; 135 | self.message = @"\n\nNo Video\n\n"; 136 | 137 | if (self.media.videoURL) { 138 | self.title = @"Video Options"; 139 | self.message = @""; 140 | NSUInteger lines = round(kUIM_MediaHeight/kUIM_LineSpacing); 141 | for (int i = 0; i < lines; i++) { 142 | self.message = [self.message stringByAppendingString:@"\n"]; 143 | } 144 | } 145 | 146 | self.videoView = [[UIView alloc] init]; 147 | self.videoView.frame = CGRectMake(0, 62, kUIM_MediaWidth, kUIM_MediaHeight); 148 | self.videoView.center = CGPointMake(self.controller.view.center.x-10, self.videoView.center.y); 149 | self.videoView.layer.cornerRadius = 5; 150 | self.videoView.clipsToBounds = YES; 151 | 152 | AVPlayer *player = [[AVPlayer alloc] init]; 153 | if (self.media.videoURL) { player = [[AVPlayer alloc] initWithURL:self.media.videoURL]; } 154 | AVPlayerLayer *layer = [AVPlayerLayer playerLayerWithPlayer:player]; 155 | layer.frame = CGRectMake(0, 0, self.videoView.frame.size.width, self.videoView.frame.size.height); 156 | [self.videoView.layer insertSublayer:layer atIndex:0]; 157 | self.playerRef = player; 158 | 159 | } 160 | 161 | // 162 | // ** present UIAlertController: 163 | // 164 | 165 | UIAlertController *ac = [UIAlertController alertControllerWithTitle:self.title message:self.message preferredStyle:UIAlertControllerStyleActionSheet]; 166 | 167 | // check if the image isn't nil.. if so add it: 168 | if (self.media.image) { 169 | [ac.view addSubview:self.imageView]; 170 | } 171 | 172 | // check if the media url isn't nil.. if so add view: 173 | if (self.media.videoURL) { 174 | [ac.view addSubview:self.videoView]; 175 | } 176 | 177 | // create actions: 178 | UIAlertAction *add = [UIAlertAction actionWithTitle:[NSString stringWithFormat:@"Add %@",[self typeString]] style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 179 | [self presentPickerWithType:self.type]; 180 | }]; 181 | 182 | if (self.type == MediaTypeImage) { 183 | if (!self.media.image) { 184 | [ac addAction:add]; 185 | } 186 | } 187 | if (self.type == MediaTypeVideo) { 188 | if (!self.media.videoURL) { 189 | [ac addAction:add]; 190 | } 191 | } 192 | 193 | UIAlertAction *replace = [UIAlertAction actionWithTitle:[NSString stringWithFormat:@"Replace %@",[self typeString]] style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 194 | if (self.type == MediaTypeImage) { self.media.image = nil; } 195 | if (self.type == MediaTypeVideo) { self.media.videoURL = nil; } 196 | [self presentPickerWithType:self.type]; 197 | }]; 198 | 199 | if (self.type == MediaTypeImage) { 200 | if (self.media.image) { 201 | [ac addAction:replace]; 202 | } 203 | } 204 | if (self.type == MediaTypeVideo) { 205 | if (self.media.videoURL) { 206 | [ac addAction:replace]; 207 | } 208 | } 209 | 210 | UIAlertAction *removeImage = [UIAlertAction actionWithTitle:[NSString stringWithFormat:@"Remove %@",[self typeString]] style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 211 | 212 | if (self.type == MediaTypeImage) { self.media.image = nil; } 213 | if (self.type == MediaTypeVideo) { self.media.videoURL = nil; } 214 | 215 | if (self.removed) { 216 | self.removed(); 217 | [self presentWithType:self.type picked:self.picked removed:self.removed]; 218 | } else { 219 | [self presentWithType:self.type picked:self.picked removed:nil]; 220 | } 221 | 222 | }]; 223 | 224 | if (self.type == MediaTypeImage) { 225 | if (self.media.image) { 226 | [ac addAction:removeImage]; 227 | } 228 | } 229 | 230 | if (self.type == MediaTypeVideo) { 231 | if (self.media.videoURL) { 232 | [ac addAction:removeImage]; 233 | } 234 | } 235 | 236 | UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { 237 | if (self.playerRef) { 238 | [self.playerRef seekToTime:CMTimeMake(0, 1)]; 239 | [self.playerRef pause]; 240 | } 241 | }]; 242 | [ac addAction:cancel]; 243 | 244 | // add popover for iPad 245 | if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { 246 | [ac setModalPresentationStyle:UIModalPresentationPopover]; 247 | UIPopoverPresentationController *popPresenter = [ac popoverPresentationController]; 248 | popPresenter.sourceView = self.controller.view; 249 | popPresenter.sourceRect = CGRectMake(self.controller.view.frame.size.width/2, 200, 2, 2); 250 | } 251 | 252 | // present alert controller: 253 | [self.controller presentViewController:ac animated:YES completion:^{ 254 | if (self.media.videoURL) { 255 | if (self.playerRef) { 256 | [self.playerRef play]; 257 | } 258 | } 259 | }]; 260 | 261 | } 262 | 263 | #pragma mark - Present: 264 | 265 | - (void)presentPickerWithType:(MediaType)type { 266 | 267 | UIImagePickerController *picker = [[UIImagePickerController alloc] init]; 268 | 269 | if (type == MediaTypeImage) { picker.mediaTypes = @[(NSString *)kUTTypeImage]; } 270 | if (type == MediaTypeVideo) { picker.mediaTypes = @[(NSString *)kUTTypeMovie]; } 271 | 272 | picker.delegate = self; 273 | picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; 274 | picker.allowsEditing = YES; 275 | [self.controller presentViewController:picker animated:YES completion:nil]; 276 | 277 | } 278 | 279 | #pragma mark - UIImagePickerController Delegate: 280 | 281 | - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { 282 | 283 | if (self.type == MediaTypeImage) { 284 | UIImage *image = info[UIImagePickerControllerEditedImage]; 285 | self.media.image = image; 286 | self.picked(); 287 | } 288 | 289 | if (self.type == MediaTypeVideo) { 290 | 291 | NSString *mediaType = [info objectForKey: UIImagePickerControllerMediaType]; 292 | if (CFStringCompare ((__bridge CFStringRef) mediaType, kUTTypeMovie, 0) == kCFCompareEqualTo) { 293 | NSURL *videoURL = (NSURL*)[info objectForKey:UIImagePickerControllerMediaURL]; 294 | AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:videoURL options:nil]; 295 | AVAssetImageGenerator *gen = [[AVAssetImageGenerator alloc] initWithAsset:asset]; 296 | gen.appliesPreferredTrackTransform = YES; 297 | CMTime time = CMTimeMakeWithSeconds(0.0, 600); 298 | NSError *error = nil; 299 | CMTime actualTime; 300 | CGImageRef image = [gen copyCGImageAtTime:time actualTime:&actualTime error:&error]; 301 | UIImage *thumb = [[UIImage alloc] initWithCGImage:image]; 302 | CGImageRelease(image); 303 | self.media.videoThumb = thumb; 304 | self.media.videoURL = videoURL; 305 | self.picked(); 306 | } 307 | 308 | } 309 | 310 | [picker dismissViewControllerAnimated:YES completion:nil]; 311 | 312 | } 313 | 314 | - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { 315 | [picker dismissViewControllerAnimated:YES completion:nil]; 316 | } 317 | 318 | #pragma mark - Extras: 319 | 320 | - (UIViewController *)findTopViewController { 321 | UIViewController *rootController = [UIApplication sharedApplication].keyWindow.rootViewController; 322 | while (rootController.presentedViewController) { 323 | rootController = rootController.presentedViewController; 324 | } return rootController; 325 | } 326 | 327 | - (NSString *)typeString { 328 | if (self.type == MediaTypeImage) { return @"Image"; } 329 | else if (self.type == MediaTypeVideo) { return @"Video"; } 330 | else { return @"Media"; } 331 | } 332 | 333 | - (void)removeObjects { 334 | if (self.imageView) { 335 | [self.imageView removeFromSuperview]; 336 | self.imageView = nil; 337 | } if (self.videoView) { 338 | [self.videoView removeFromSuperview]; 339 | self.videoView = nil; 340 | } if (self.playerRef) { 341 | self.playerRef = nil; 342 | } 343 | } 344 | 345 | @end 346 | --------------------------------------------------------------------------------