Grabbing album art for current song and using it to change a certain imageView.image generates an error, but no longer crashes. (It did before because I left out the if (!artwork) error handling. Eheh.)
This method:
- (void)handleNowPlayingItemChanged:(id)notification {
MPMediaItem *item = self.musicPlayer.nowPlayingItem;
CGSize albumCoverSize = self.albumCover.bounds.size;
MPMediaItemArtwork *artwork =
[item valueForProperty:MPMediaItemPropertyArtwork];
if (artwork) {
self.albumCover.image = [artwork imageWithSize:albumCoverSize];
} else {
self.albumCover.image = nil;
}
}
Explodes like this:
CPSqliteStatementPerform: attempt to write a readonly database for
UPDATE ddd.ext_container SET orig_date_modified = (SELECT date_modified
FROM container WHERE pid=container_pid) WHERE orig_date_modified=0
CPSqliteStatementReset: attempt to write a readonly database for
UPDATE ddd.ext_container SET orig_date_modified = (SELECT date_modified
FROM container WHERE pid=container_pid) WHERE orig_date_modified=0
But only on launch. And it still shows the image (or lack thereof). Weird.
Edit: The iPod Library is readonly (apps can't change anything, only iTunes), so maybe it's yelling at
me for writing a readonly something, but still allowing it because nothing readonly is being modified?
And after that's fixed, I need to get resizing working (for Landscape support) instead of IB's stretching.
Not vital, but still a nice thing to have.
Here's what I do. It creates no errors, and produces an image every time. If the song doesn't have an image, it defaults to the one I provide. I think because you're not checking for an image with a specific size (320 by 320, matching the screen width for me), it fails to figure it out correctly. I don't know why you're getting the SQLite error, but hopefully this fixes it!
MPMediaItemArtwork *artworkItem = [self.musicPlayer.nowPlayingItem valueForProperty: MPMediaItemPropertyArtwork];
if ([artworkItem imageWithSize:CGSizeMake(320, 320)]) {
[self.currentlyPlayingArtworkView setImage:[artworkItem imageWithSize:CGSizeMake (320, 320)]];
}
else {
[self.currentlyPlayingArtworkView setImage:[UIImage imageNamed:#"NoArtworkImage"]];
}
Link here - Why am I getting this CPSqliteStatementPerform error in xcode console
Putting this here so the question can be marked as Answered.
Related
I have been testing an IOS app on multiple platforms and have observed some weird behaviours while testing on the iPhone 4S. The app crashes after performing a task that works on both the iPhone 5 and iPhone 3S. When the crash happens there is nothing displayed on the debugger, absolutely no crash log or memory warnings appear in Xcode. I isolated the block of code, and the specific line responsible for the crash. I still don't understand why it is happening or how to find a solution. The code is part of a larger method that downloads a number of images and displays them in UIImageViews. Here is the code:
//fill image tiles with images
for (int i = 0; i < [objects count]; ++i)
{
PFObject *imageObject = [objects objectAtIndex:i];
PFFile *imageFile = (PFFile *)[imageObject objectForKey:#"image"];
UIImageView *imageHolder = (UIImageView *)[self.view viewWithTag:(100 + i)];
imageHolder.image = [UIImage imageWithData:imageFile.getData];
}
The code loops through an object that has been loaded with image files from a server. The UIImageViews that each image is meant to be assigned to are tagged from 100 - 104, so they can be accessed with the loop index variable. The line UIImageView *imageHolder = (UIImageView *)[self.view viewWithTag:(100 + i)] extracts the UIImageViews from the main view. In the next line the imageHolder view is assigned an image, this is the line that causes the crash, and when commented out the view loads without fail. I haven't been able to determine why this is happening or if it is the imageFile or imageHolder view that is not being set up properly. Perhaps someone here can shed some light on the problem.
You should have some protection around imageFile.getData as it could return nil. You should preferable use the getData: method which returns an error that you can use if the data can't be accessed.
NSError *error = nil;
NSData *imageData = [imageFile getData:&error];
if (imageData != nil) {
imageHolder.image = [UIImage imageWithData:imageData];
} else {
NSLog(#"OMG!! %#", error);
}
Im attempting to save some images to a camera roll, all the images are in a array.
if (buttonIndex == 1) {
int done = 0;
NSMutableArray *copyOfImages = [[NSMutableArray alloc]initWithArray:saveImagesToArray];
while (!done == 1){
if (copyOfImages.count == 0) {
done = 1;
}
if (copyOfImages.count > 0){
[copyOfImages removeLastObject];
}
UIImage *image = [[UIImage alloc]init];
image = [copyOfImages lastObject];
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
}
}
because i dont know how many images there can be i use a while loop
Im testing this with 8 images, the array shows a count of 8 so thats good.
When i try saving the images this comes up in the console.
-[NSKeyedUnarchiver initForReadingWithData:]: data is NULL
Out of the 8 images im trying, only 5 show up in the camera roll.
Any help would be great.
Thanks :)
Why are you calling [copyOfImages removeLastObject]?
Every time you go through that look are you destroying the last object, which is strange because you haven't added it to the roll yet. Take out that line and see if you look works.
Also, rather than using a for loop, use the following pattern:
for (id object in array) {
// do something with object
}
This will enumerate though the objects in the array, stopping when it reaches the end. Just be sure not to modify the array while you are doing this.
I had this same issue and I resolved it by ensuring that the images are saved sequentially. I think there may be some kind of race condition going on.
Basically, I did:
UIImageWriteToSavedPhotosAlbum([self.images objectAtIndex:self.currentIndex], self,
#selector(image:didFinishSavingWithError:contextInfo:), nil);
Then in image:didFinishSavingWithError:contextInfo:, I increment currentIndex and try the next image, if there are any left.
This has worked for me in every case so far.
I had the same exact problem. No matter how much pictures i added to the share activityController, maximum of 5 were saved.
i have found that the problem was to send the real UIImage and not URL:
NSMutableArray *activityItems = [[NSMutableArray alloc] init];
for(UIImage *image in imagesArray) {
[activityItems addObject:image];
}
UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:nil];
To expand on Bill's answer, UIImageWriteToSavedPhotosAlbum seems to do its writing on some unknown thread asynchronously - but also there is some hidden limit to the number of images it can write at once. You can even tease out write busy-type errors if you dig in deep.
Tons more info here:
Objective-C: UIImageWriteToSavedPhotosAlbum() + asynchronous = problems
I also agree with Bill that serializing your writes is the only reasonable/reliable answer
I have seen.
I am writing a simple application for an artist who wants to market his work on iTunes.
Because there are only twelve images, which can be deleted more rapidly than they could be selected from an image picker, the user does not select the images individually. Instead, the images are copied programmatically from the application bundle into the photo library. This minimizes the size and complexity of the application and saves the end-user from the tedium of having to select each image individually.
Note: this problem occurs only on the physical device (iPhone 3Gs/3.1).
Not surprisingly, my implementation involves calling UIImageWriteToSavedPhotosAlbum inside a loop that iterates over an array of 12 full paths to the corresponding image files (all are JPEGs). Minus the details (e.g., error checking, logging, optional callback parameters),
the loop boils down to:
for ( NSString* path in paths ) {
image = [UIImage imageWithContentsOfFile:path];
if (image) {
[NSThread sleepForTimeInterval:1.0]; // unacceptable
UIImageWriteToSavedPhotosAlbum(image, ...);
}
}
Note the line marked "unacceptable." Why must the current thread sleep for a full second prior to each invocation of UIImageWriteToSavedPhotosAlbum(image, ...)? Well, because that is quite literally how long it takes for iPhoneOS 3.1 (running on my 3Gs device) to get around to the business of actually writing a buffered UIImage object to the persistent store that represents the user's photo library! How do I know this? Well, because without the call to NSThread sleepForTimeInterval:, no error of any kind is ever reported even though only a fraction of the 12 images (which appears to vary randomly between 1 and 9) are in fact copied to the photo library. In addition, results do improve as the sleep interval increases, but, as I have discovered, the interval must be increased dramatically before the improvement becomes noticeable. Even at as high a value as 0.8 seconds, I have observed missing files following repeated trials. At 1 second, I have observed up to three consecutive "correct" runs in which all files are fully transferred before losing all patience.
I have googled this issue to death, checked every posting on this board that mentions UIImageWriteToSavedPhotosAlbum, and turned up nothing. Am I the only person in the world who has ever called that function inside a loop? Is there some reason why it should be self-evident to me that I should not do so?
Facing the same issue I found a way to get around it.
When you call UIImageWriteToSavedPhotosAlbum you can pass completion selector as an argument. Thus no need to call UIImageWriteToSavedPhotosAlbum in a loop. When the first invocation completely finished its saving (your completion method is called), proceed further. Use some counter to control the process.
Here is my code:
-(IBAction) SaveAllPictures {
UIImage *the_image;
if (images_to_photos_album < numberOfPhotos) {
the_image= [UIImage //get your image here]
UIImageWriteToSavedPhotosAlbum(the_image, self, #selector(imageSaved: error: context:), the_image);
}
if (images_to_photos_album == numberOfPhotos) {
the_image= [UIImage //get your image here
UIImageWriteToSavedPhotosAlbum(the_image, self, #selector(image: didFinishSavingWithError: contextInfo:), the_image);
}
}
-(void) imageSaved: (UIImage *) image error:(NSError *) error context: (void *) contextInfo {
NSLog(#"image saved, error = %#",error);
if (!error) {
images_to_photos_album = images_to_photos_album+1;
[self SaveAllPictures];
} else {
//handle error here
}
}
- (void) image: (UIImage *)image didFinishSavingWithError: (NSError *)error contextInfo: (void *)contextInfo {
images_to_photos_album =0;
if (error) {
// handle error here
} else {
// handle success here
}
}
A quick search of the developer forums at Apple turned up the following post:
https://devforums.apple.com/message/112119#112119 for the full thread.
This thread suggests to me that there is no safe way to write an image to the photo library programmatically.
The UI of my iPhone application has lots of static labels, and I have set accessibility hints for them in Interface Builder. I want to access these programmatically so I can provide help bubbles - a custom subclass of UILabel recognises a touch and displays a bubble with the value of [self accessibilityHint].
However, [self accessibilityHint] returns nil. If I set the value programmatically [self setAccessibilityHint: #"Hello"] then I can access that value from my program, but the initial value from the NIB isn't available.
If I turn on the Accessibility Inspector before launching my application, the hints from the NIB files are available via the accessibilityHint property. Is there some flag somewhere that determines whether the system loads these properties; and if there is, is there some way I can set it?
My backup option is to have my controllers keep references to each UI label and set the accessibilityHint in code, but that's pretty ugly and cumbersome.
Hmm, if I open Library/Preferences/com.apple.Accessibility.plist and change ApplicationAccessibilityEnabled from false to true, then it works. (That path being in ~/Library/Application Support/iPhone Simulator/)
I tried adding this at the start of main():
CFPreferencesSetAppValue(#"AccessibilityEnabled", kCFBooleanTrue, #"com.apple.Accessibility");
CFPreferencesSetAppValue(#"ApplicationAccessibilityEnabled", kCFBooleanTrue, #"com.apple.Accessibility");
CFPreferencesAppSynchronize(#"com.apple.Accessibility");
but it didn't work. (It wrote a file to Applications/{UUID}/Library/Preferences/com.apple.Accessibility.plist)
EDIT: After stepping through the UIKit code, the call that determines whether accessibility is turned on or off is
CFPreferencesGetBooleanValue(#"ApplicationAccessibilityEnabled", #"/Users/sam/Library/Application Support/iPhone Simulator/User/Library/Preferences/com.apple.Accessibility", NULL);
Note the bizarre app key, I'm not yet sure where this value comes from (my knowledge of 386 assembly is very weak!) but I can pass this key to CFPreferencesSetAppValue and it works, at least on the simulator (I don't have access to an actual device at the moment).
Also this will turn on app accessibility for all applications (since it writes it to the global plist). I can set a flag from main() if the value should be set back to false once the application has launched.
This works on simulator and device. Taken from http://sgleadow.github.com/blog/2011/11/16/enabling-accessibility-programatically-on-ios-devices/
#import <dlfcn.h>
NSAutoreleasePool *autoreleasePool = [[NSAutoreleasePool alloc] init];
NSString *appSupportLocation = #"/System/Library/PrivateFrameworks/AppSupport.framework/AppSupport";
NSDictionary *environment = [[NSProcessInfo processInfo] environment];
NSString *simulatorRoot = [environment objectForKey:#"IPHONE_SIMULATOR_ROOT"];
if (simulatorRoot) {
appSupportLocation = [simulatorRoot stringByAppendingString:appSupportLocation];
}
void *appSupportLibrary = dlopen([appSupportLocation fileSystemRepresentation], RTLD_LAZY);
CFStringRef (*copySharedResourcesPreferencesDomainForDomain)(CFStringRef domain) = dlsym(appSupportLibrary, "CPCopySharedResourcesPreferencesDomainForDomain");
if (copySharedResourcesPreferencesDomainForDomain) {
CFStringRef accessibilityDomain = copySharedResourcesPreferencesDomainForDomain(CFSTR("com.apple.Accessibility"));
if (accessibilityDomain) {
CFPreferencesSetValue(CFSTR("ApplicationAccessibilityEnabled"), kCFBooleanTrue, accessibilityDomain, kCFPreferencesAnyUser, kCFPreferencesAnyHost);
CFRelease(accessibilityDomain);
}
}
[autoreleasePool drain];
I am developing an game in which i have to load images on finger swipe. For this i loaded all image 22 of size (788x525) at loading of the view itself. Then on finger swipe i just added image on view.
This works fine on iTouch but on iPhone game crashes after showing near about 12 images.
This is how i added images, graphicsArray is NSMUTableArray.
for ( int i=0; i<=totalNoofPages;i++){
NSString * graphicsFileName = #"Page";
graphicsFileName =
[graphicsFileName
stringByAppendingString:[NSString stringWithFormat:#"%d", i]];
UIImage *image =
[[UIImage alloc] initWithContentsOfFile:
[[NSBundle mainBundle] pathForResource:graphicsFileName
ofType:#"jpg"]];
GraphicsImages =
[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 480,320 )];
GraphicsImages.image = image;
[image release];
[GraphicsImages setCenter:center];
GraphicsImages.transform = CGAffineTransformMakeRotation(M_PI/2);
[graphicsArray insertObject:GraphicsImages atIndex:i];
}
and on finger swipe i write,pageNo is number of page that will be displayed.
GraphicsImages = [graphicsArray objectAtIndex:pageNo];
[self addSubview:GraphicsImages];
Is there any way to release the previous loaded image from sub view as it might be kept on stack or is it due to large image size????
Please help me in this situation.
Thanks in advance.
Regards,
Vishal.
Are you crashing due to lack of memory?
You might try pre-loading the previous, current & next images only, and release them as soon as possible. Loading 22 images in memory takes up quite a lot of space.
Can you provide the error that you're getting when the app crashes? Knowing what exception is being triggered will help a lot in figuring out exactly what's going on.
I can tell you that you aren't releasing the GraphicsImages variable in your for loop, so that's causing memory leaks. I imagine that that's not enough to cause a crash, however. In any case, do this:
[ graphicsArray insertObject:GraphicsImages ];
[ GraphicsImages release ];
Note that you don't need to specify the insertion index in your case because the items will be added sequentially. Also, when you insert an object into an array, it is retained, so you can safely release it afterwards.