In my app i want to get the image and video from particular album.so i use ALAssetsLibraryGroupsEnumerationResultsBlock function for get the images and video from the particular Album.here i store the asset value in a nsmutablearray,i get that array values on viewdidLoad, but in viewdidload the array value is null.So when ALAssetsLibraryGroupsEnumerationResultsBlock is trigger.
Below is my code help me..
- (void)viewDidLoad {
[super viewDidLoad];
if (self.assetLibrary == nil) {
_assetLibrary = [[ALAssetsLibrary alloc] init];
}
if (self.groups == nil) {
_groups = [[NSMutableArray alloc] init];
} else {
[self.groups removeAllObjects];
}
[self loadAssetImages];
}
-(void)loadAssetImages{
NSLog(#"Load asset Image ");
ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError *error) {
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:#"ERROR" message:[NSString stringWithFormat:#"No Albums Available"] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
};
ALAssetsLibraryGroupsEnumerationResultsBlock listGroupBlock = ^(ALAssetsGroup *group, BOOL *stop) {
NSLog(#"Block");
if (group == nil)
{
return;
}
if ([[group valueForProperty:ALAssetsGroupPropertyName] isEqualToString:#"FlipDemo"]) {
[self.groups addObject:group];
[self loadImages];
return ;
}
if (stop) {
return;
}
};
NSLog(#"Block Out");
[self.assetLibrary enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock: listGroupBlock
failureBlock:failureBlock];
}
-(void)loadImages{
NSLog(#"Load Image000");
ALAssetsGroup *assetGroup;
for (assetGroup in self.groups)
{
for (int i = 0; i<[self.groups count]; i++)
{
[ assetGroup enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop)
{
if (result) {
if ([[result valueForProperty:ALAssetPropertyType] isEqualToString:ALAssetTypeVideo])
{
[videoOnly addObject:result];
}
else if ([[result valueForProperty:ALAssetPropertyType] isEqualToString:ALAssetTypePhoto])
{
[imgOnly addObject:result];
// NSLog(#"Photo");
}
}
}
];
}
}
}
The method is asynchronous. You can find the function detail in help document:
help document
(void)enumerateGroupsWithTypes:(ALAssetsGroupType)types
usingBlock:(ALAssetsLibraryGroupsEnumerationResultsBlock)enumerationBlock
failureBlock:(ALAssetsLibraryAccessFailureBlock)failureBlock
This method is asynchronous. When groups are enumerated, the user may be asked to confirm the application's access to the data; the method, though, returns immediately. You should perform whatever work you want with the assets in enumerationBlock.
end of help document
So, according your code, you can't get anything in your main thread. Because iOS get assets in another thread.
You can set a key-value observation to make main thread waiting for this asynchronous function.
1) set a property, for example: handleOverFindFlag
2) before execute the function: set handleOverFindFlag=NO
3) add Observer
[self addObserver:self forKeyPath:#"handleOverFindFlag" options:0 context:NULL];
4) set observing function:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:
(NSDictionary *)change context:(void *)context
{
if([keyPath isEqualToString:#""])
{
//handle your findings
}
}
5) after doing this
[self removeObserver:self forKeyPath:#"handleOverFindFlag"];
You can find detail about Key-Value coding in help document.
Related
I want to record a video after a count down 3 2 1. But on first time without allowing the camera the video has started recording without showing to camera . I want to call the recording code in the block which is called when the user allow to camera on camera access alert.
self.recordingManager.previewLayer.frame = CGRectMake(47, 2000, 280, 154);
// set code for count down
imageArr = [[NSArray alloc]initWithObjects:#"countdown_three",#"countdown_two",#"countdown_one", nil];
[_countImageView setImage:[UIImage imageNamed:[imageArr objectAtIndex:0]]];
[_countImageView setHidden:NO];
NSLog(#"%#",[imageArr objectAtIndex:0]);
count = 4;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(setCountDownDuration) userInfo:nil repeats:YES];
// customize progressbar
_captureProgress.hidden = NO;
_captureProgress.progress = 0.0;
_captureProgress.layer.borderWidth = 0.2;
_captureProgress.layer.borderColor = [UIColor colorWithRed:167.0f/255 green:188.0f/255 blue:219.0f/255 alpha:1].CGColor;
CGAffineTransform transform = CGAffineTransformMakeScale(1.6f, 8.0f);
_captureProgress.transform = transform;
count--;
if (count == 3) {
[self.progressStatusLbl setText:#"GET READY"];
[_countImageView setImage:[UIImage imageNamed:[imageArr objectAtIndex:0]]];
}
if (count == 2) {
[_countImageView setImage:[UIImage imageNamed:[imageArr objectAtIndex:1]]];
}
if (count == 1) {
[_countImageView setImage:[UIImage imageNamed:[imageArr objectAtIndex:2]]];
}
if (count == 0) {
[timer invalidate];
timer = nil;
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if(authStatus == AVAuthorizationStatusAuthorized)
{
[self.progressStatusLbl setText:#"RECORDING"];
[self openCamera];
}
else if(authStatus == AVAuthorizationStatusNotDetermined)
{
NSLog(#"%#", #"Camera access not determined. Ask for permission.");
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted)
{
if(granted)
{
NSLog(#"Granted access to %#", AVMediaTypeVideo);
[self.progressStatusLbl setText:#"RECORDING"];
[self openCamera];
}
else
{
NSLog(#"Not granted access to %#", AVMediaTypeVideo);
[self camDenied];
}
}];
}
else if (authStatus == AVAuthorizationStatusRestricted)
{
// My own Helper class is used here to pop a dialog in one simple line.
//[Helper popAlertMessageWithTitle:#"Error" alertText:#"You've been restricted from using the camera on this device. Without camera access this feature won't work. Please contact the device owner so they can give you access."];
[Utils showAlertMessageWithTitle:#"Error" msg:#"You've been restricted from using the camera on this device. Without camera access this feature won't work. Please contact the device owner so they can give you access." firstButtonTitle:#"Ok" secontButtonTitle:nil];
}
else
{
[self camDenied];
}
For AVAuthorizationStatusNotDetermined state, add observer for a key (boolean), and update this flag in completion of requestAccessForMediaType then start camera or call camDenied deoending on user's actions.
So you code should look like as below:
Add a new private property in your class
#property (nonatomic, strong) NSNumber *granted;
then add following methods
- (void)askForPermission {
NSLog(#"%#", #"Camera access not determined. Ask for permission.");
[self addObserver:self forKeyPath:#"granted" options:NSKeyValueObservingOptionNew context:nil];
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted)
{
self.granted = #(granted);
}];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:#"granted"]) {
BOOL granted = [object boolValue];
if(granted)
{
NSLog(#"Granted access to %#", AVMediaTypeVideo);
[self.progressStatusLbl setText:#"RECORDING"];
[self openCamera];
}
else
{
NSLog(#"Not granted access to %#", AVMediaTypeVideo);
[self camDenied];
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
call askForPermission in AVAuthorizationStatusNotDetermined case.
I'm getting the error instance method '-callDelegateOnMainThread:withArg:error:' not found what should I do here? I merged in some achievement code into Ray Wenderlichs turn based multiplayer code.
The functions are part of the following interface:
#interface GCTurnBasedMatchHelper : NSObject <GKTurnBasedMatchmakerViewControllerDelegate, GKTurnBasedEventHandlerDelegate, GKAchievementViewControllerDelegate, MFMessageComposeViewControllerDelegate> {
BOOL gameCenterAvailable;
BOOL userAuthenticated;
UIViewController *presentingViewController;
NSMutableDictionary* earnedAchievementCache;
GKTurnBasedMatch *currentMatch;
//id <GCTurnBasedMatchHelperDelegate> delegate;
}
Here are the functions
- (void) submitAchievement: (NSString*) identifier percentComplete: (double) percentComplete
{
//GameCenter check for duplicate achievements when the achievement is submitted, but if you only want to report
// new achievements to the user, then you need to check if it's been earned
// before you submit. Otherwise you'll end up with a race condition between loadAchievementsWithCompletionHandler
// and reportAchievementWithCompletionHandler. To avoid this, we fetch the current achievement list once,
// then cache it and keep it updated with any new achievements.
if(self.earnedAchievementCache == NULL)
{
[GKAchievement loadAchievementsWithCompletionHandler: ^(NSArray *scores, NSError *error)
{
if(error == NULL)
{
NSMutableDictionary* tempCache = [NSMutableDictionary dictionaryWithCapacity: [scores count]];
for (GKAchievement* score in scores)
{
[tempCache setObject: score forKey: score.identifier];
}
self.earnedAchievementCache = tempCache;
[self submitAchievement: identifier percentComplete: percentComplete];
}
else
{
//Something broke loading the achievement list. Error out, and we'll try again the next time achievements submit.
[self callDelegateOnMainThread: #selector(achievementSubmitted:error:) withArg: NULL error: error];
}
}];
}
else
{
//Search the list for the ID we're using...
GKAchievement* achievement = [self.earnedAchievementCache objectForKey: identifier];
if(achievement != NULL)
{
if((achievement.percentComplete >= 100.0) || (achievement.percentComplete >= percentComplete))
{
//Achievement has already been earned so we're done.
achievement= NULL;
}
achievement.percentComplete= percentComplete;
}
else
{
achievement = [[[GKAchievement alloc] initWithIdentifier: identifier] autorelease];
achievement.percentComplete= percentComplete;
//Add achievement to achievement cache...
[self.earnedAchievementCache setObject: achievement forKey: achievement.identifier];
}
if(achievement != NULL)
{
//Submit the Achievement...
[achievement reportAchievementWithCompletionHandler: ^(NSError *error)
{
[self callDelegateOnMainThread: #selector(achievementSubmitted:error:) withArg: achievement error: error];
}];
}
}
}
- (void) achievementSubmitted: (GKAchievement*) ach error:(NSError*) error;
{
if((error == NULL) && (ach != NULL))
{
if (ach.percentComplete == 100.0) {
//UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Achievement Earned!"
// message:(#"%#",ach.identifier)
// delegate:nil
// cancelButtonTitle:#"OK"
// otherButtonTitles:nil];
//[alert show];
//[alert release];
TRACE("achievement submitted %s\n", [ach.identifier UTF8String]);
}
}
else
{
// Achievement Submission Failed.
printf("Achievement Submission Failed\n");
}
}
Looks like this is the missing code you are looking for. Just put it inside of your GCTurnBasedMatchHelper.m file.
- (void) callDelegateOnMainThread: (SEL) selector withArg: (id) arg error: (NSError*) err
{
dispatch_async(dispatch_get_main_queue(), ^(void)
{
[self callDelegate: selector withArg: arg error: err];
});
}
- (void) callDelegate: (SEL) selector withArg: (id) arg error: (NSError*) err
{
assert([NSThread isMainThread]);
if([delegate respondsToSelector: selector])
{
if(arg != NULL)
{
[delegate performSelector: selector withObject: arg withObject: err];
}
else
{
[delegate performSelector: selector withObject: err];
}
}
else
{
NSLog(#"Missed Method");
}
}
Just add the method which doesn't exist.
Am having a Custom folder in my ipad device. Am using this code to get image from Custom album:
self.assetGroups = [[NSMutableArray alloc] init];
// Group enumerator Block
dispatch_async(dispatch_get_main_queue(), ^
{
void (^assetGroupEnumerator)(ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop)
{
if (group == nil) {
return;
}
if ([[group valueForProperty:ALAssetsGroupPropertyName] isEqualToString:#"Ganesh"]) {
[self.assetGroups addObject:group];
[self loadImages];
return;
}
if (stop) {
return;
}
};
// Group Enumerator Failure Block
void (^assetGroupEnumberatorFailure)(NSError *) = ^(NSError *error) {
UIAlertView * alert = [[UIAlertView alloc]
initWithTitle:#"ERROR" message:[NSString stringWithFormat:#"No Albums Available"]
delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
[alert release];
};
// Enumerate Albums
self.library = [[ALAssetsLibrary alloc] init];
[library enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:assetGroupEnumerator
failureBlock:assetGroupEnumberatorFailure];
});
To retrieve all the images from my custom folder I am using this:
(void)loadImages {
ALAssetsGroup *assetGroup = [self.assetGroups objectAtIndex:0];
NSLog(#"asserts group %#",self.assetGroups);
NSLog(#"ALBUM NAME:;%#",[assetGroup valueForProperty:ALAssetsGroupPropertyName]);
[assetGroup enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop){
if(result == nil) {
return;
}
UIImage *img = [UIImage imageWithCGImage:[[result defaultRepresentation] fullScreenImage]
scale:1.0 orientation:(UIImageOrientation)[[result valueForProperty:#"ALAssetPropertyOrientation"] intValue]];
NSLog(#"image name%#",img);
[arrayOfImage addObject:img];
NSLog(#"arrayOfImage %#",arrayOfImage);
// [imageToAnimate setImage:img];
}];
}
its working fine when i am running with simulator but not in device: it executes Failure block and Says No Album Available.
Its works fine. I have switched Off my location service due to this am not able to access my media now it works fine.
I am developing an iPad application in which I have saved images to custom album using This.
Now I want to get all the images from that custom folder and I need to show all those in a animated UIImageView.
I know how to set animation but I want to know how to get all the images from particular custom folder.
See this code I used to load the images from the custom album. I have used the same sample code to store my images in custom album.
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
self.assetGroups = tempArray;
library = [[ALAssetsLibrary alloc] init];
// Load Albums into assetGroups
// Group enumerator Block
void (^assetGroupEnumerator)(ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop)
{
if (group == nil)
{
return;
}
if([[group valueForProperty:ALAssetsGroupPropertyName] isEqualToString:kAlbumName])
{
[self.assetGroups addObject:group];
[self reloadTableView];
return;
}
};
// Group Enumerator Failure Block
void (^assetGroupEnumberatorFailure)(NSError *) = ^(NSError *error) {
CustomAlertView * alert = [[CustomAlertView alloc] initWithTitle:#"Error" message:[NSString stringWithFormat:#"Album Error: %# - %#", [error localizedDescription], [error localizedRecoverySuggestion]] delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
NSLog(#"A problem occured %#", [error description]);
};
// Enumerate Albums
[library enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:assetGroupEnumerator
failureBlock:assetGroupEnumberatorFailure];
Here the kAlbumName is one string ivar which contains the custom album name.
EDIT:1
Above code just gives you the whole album selected with all it photos now to get those photos from album use the following code
[self.assetGroup enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop)
{
if(result == nil)
return;
CGRect viewFrames = kThumbSize;//CGRectMake(0, 0, 75, 75);
UIImageView *assetImageView = [[UIImageView alloc] initWithFrame:viewFrames];
[assetImageView setContentMode:UIViewContentModeScaleToFill];
[assetImageView setImage:[UIImage imageWithCGImage:(__bridge CGImageRef)([result originalAsset])]];
}];
NOTE: Instead of kThumbSize define your CGRectMake() as commented.
Enjoy coding :)
Happy Day :)
I have a class that is handling an AVPlayer (and AVPlayerItem) that reports back state, time, and timedMetadata to a delegate.
Works well except that about 70-80% of the time, the initial timedMetadata is not "key value observed". However after the first instance of timedMetadata being missed, all other timedMetadata seems to be observed without issue.
As a temporary fix, I've started to embed dummy timedMetadata tags in the beginning of videos that do nothing but "kick the tires" so to speak and everything works fine after that. Yet this seems pretty kludgy. I suspect that either I'm setting up the AVPlayerItem and KVO in a sub-optimal manner OR there's just a bug here.
Any ideas on why this might be happening are greatly appreciated! Code below....
// CL: Define constants for the key-value observation contexts.
static const NSString *ItemStatusContext;
static const NSString *ItemMetadataContext;
static const NSString *ItemPlaybackForcastContext;
- (id)initWithURL:(NSURL *)url
{
if (self = [super init]) {
__weak TFPAVController *_self = self;
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
NSString *tracksKey = #"tracks";
[asset loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:tracksKey] completionHandler:
^{
dispatch_async(dispatch_get_main_queue(),
^{
NSError *error = nil;
AVKeyValueStatus status = [asset statusOfValueForKey:tracksKey error:&error];
if (status == AVKeyValueStatusLoaded) {
AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:asset];
[item addObserver:_self forKeyPath:#"status" options:0 context:&ItemStatusContext];
[item addObserver:_self forKeyPath:#"timedMetadata" options:0 context:&ItemMetadataContext];
[item addObserver:_self forKeyPath:#"playbackLikelyToKeepUp" options:0 context:&ItemPlaybackForcastContext];
[[NSNotificationCenter defaultCenter] addObserver:_self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:item];
AVPlayer *player = [AVPlayer playerWithPlayerItem:item];
_self.totalRunTime = CMTimeGetSeconds(item.duration);
[_self.delegate avPlayerNeedsView:player];
_self.playerItem = item;
_self.player = player;
}
else {
NSLog(#"The asset's tracks were not loaded: %# // [%# %#]",
error.localizedDescription,
NSStringFromClass([self class]),
NSStringFromSelector(_cmd));
}
_self.playerObserver = [_self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, _FrameRate_)
queue:NULL
usingBlock: ^(CMTime time) {
_self.currentVideoTime = CMTimeGetSeconds([_self.playerItem currentTime]);
}];
});
}];
}
return self;
}
#pragma mark - KVO Response Methods
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
__weak TFPAVController *_self = self;
if (context == &ItemStatusContext) {
dispatch_async(dispatch_get_main_queue(),
^{
if (((AVPlayerItem *)object).status == AVPlayerItemStatusReadyToPlay) {
[_self.delegate videoIsLoadedInPlayer:_self];
}
});
return;
}
else if (context == &ItemMetadataContext) {
dispatch_async(dispatch_get_main_queue(),
^{
[_self checkMetaDataForPlayerItem: (AVPlayerItem *)object];
});
return;
}
else if (context == &ItemPlaybackForcastContext) {
dispatch_async(dispatch_get_main_queue(),
^{
AVPlayerItem *playerItem = object;
if (CMTimeGetSeconds([playerItem currentTime]) <= 0) return;
NSDictionary *notificationDictionary = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:playerItem.playbackLikelyToKeepUp]
forKey:kAVPlayerStateKey];
[[NSNotificationCenter defaultCenter] postNotificationName:kAVPlayerNotification
object:self
userInfo:notificationDictionary];
});
return;
}
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
- (void)checkMetaDataForPlayerItem:(AVPlayerItem *)item
{
NSMutableDictionary *metaDict = [NSMutableDictionary dictionary];
// CL: make sure there's stuff there
if (item.timedMetadata != nil && [item.timedMetadata count] > 0) {
// CL: if there is, cycle through the items and create a Dictionary
for (AVMetadataItem *metadata in item.timedMetadata) {
[metaDict setObject:[metadata valueForKey:#"value"] forKey:[metadata valueForKey:#"key"]];
}
// CL: pass it to the delegate
[self.delegate parseNewMetaData:[NSDictionary dictionaryWithDictionary:metaDict]];
}
}
Ahhh, KVO. Probably one of Apple's all-time worst design decisions.
I guess it's no longer relevant, but at a guess the problem you're having is that sometimes the value you're trying to observe has already been assigned to the key when you get around to adding yourself as an observer, so your observer selector isn't called.
To avoid this you can add NSKeyValueObservingOptionInitial to the options when calling addObserver:forKeyPath:options:context:, and your observer method will be invoked immediately with the current value.