NSCoding, save image path instead of actual image - objective-c

#property (strong) UIImage *thumbImage;
..
albumData *album1 = [[albumData alloc]initWithTitle:#"Eminem" style:#"123" thumbImage:[UIImage imageNamed:#"1.jpeg"]];
..
- (void)encodeWithCoder:(NSCoder *)coder {
NSData *image = UIImagePNGRepresentation(_thumbImage);
[coder encodeObject:(image) forKey:#"thumbImageData"];
}
- (id)initWithCoder:(NSCoder *)coder {
NSData *imgData = [coder decodeObjectForKey:#"thumbImageData"];
_thumbImage = [UIImage imageWithData:imgData ];
return self;
}
Right now I'm using the above code to save data inside a plist file. How should I change the code in order to save just the image path \ name instead of saving the actual picture.

If you have the file name, you can get its path using -[NSBundle pathForResource:ofType:], e.g.
[[NSBundle mainBundle] pathForResource:#"1" ofType:#"jpeg"];
As rmaddy said, you cannot get this from a UIImage, so you may have to get it and hang on to it when you create the image.

You should use
#property (copy) NSString *thumbImage;
and save only image name instead of
#property (strong) UIImage *thumbImage;
then code/encode as string. When you need an image just write
[UIImage imageNamed:album1.thumbImage];
Another solution is to subclass UIImage class, add image path property, owerride UIImage's initialization methods to support saving path and override coder/encoder methods
EDIT:
Here is some example code:
UIImageWithPath.h
#import <UIKit/UIKit.h>
#interface UIImageWithPath : UIImage{
NSString* filepath;
}
#property(nonatomic, readonly) NSString* filepath;
-(id)initWithImageFilePath:(NSString*) path;
#end
UIImageWithPath.m
#import "UIImageWithPath.h"
#implementation UIImageWithPath
#synthesize filepath;
-(id)initWithImageFilePath:(NSString*) path{
self = [super initWithContentsOfFile:path];
if(self){
[filepath release];
filepath = [path copy];
}
return self;
}
-(void)dealloc{
[filepath release];
[super dealloc];
}
#end
Sample using:
- (void)viewDidLoad
{
[super viewDidLoad];
img = [[UIImageWithPath alloc] initWithImageFilePath:[[NSBundle mainBundle] pathForResource:#"pause" ofType:#"png"]];
iv.image = img;
}
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
NSLog(#"image file path %#", img.filepath);
}
So now your code/encode methods should look like this:
-(void)encodeWithCoder:(NSCoder *)coder {
NSString *path = _thumbImage.filepath;
[coder encodeObject:(path) forKey:#"thumbImageData"];
}
- (id)initWithCoder:(NSCoder *)coder {
NSString *path = [coder decodeObjectForKey:#"thumbImageData"];
_thumbImage = [[UIImageWithPath alloc] initWithImageFilePath:path];
return self;
}

Related

cocoa - unarchivedObjectOfClass does not call initWithCoder

I am developing a macOS app using objective-C.
I tried to save a NSArray object in Core Data. I write
- (id)reverseTransformedValue:(id)value
{
return [NSKeyedUnarchiver unarchivedObjectOfClass:[NSArray class] fromData:value error:nil];
}
in the class that inherits NSValueTransformer.
And one of elements in my NSArray object is not of primary kind(which has properties called courseName and courseInfos), so I conform in this element's class. In this class ,I write:
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [[self class] new];
if (self = [super init])
{
self.courseName = [aDecoder decodeObjectForKey:#"courseName"];
self.courseInfos = [aDecoder decodeObjectForKey:#"courseInfos"];
}
return self;
}
When my apps runs, the reverseTransformedValue: method is called, and all elements but that special nonprimary element in my NSArray object are decoded. I put a breakpoint in the initWithCoder: method in that special element's class, and it never runs. I use some tools and find that element is successfully stored in my core data, so its encoding process has no problem.
I Fixed it, here is the solution:
My object is 'Address' (note: it is not NSArray)
Address.h
#interface Address : NSObject <NSSecureCoding>
#property (nonatomic, strong) NSString *street;
#property (nonatomic, strong) NSString *city;
#end
Address.m
#implementation Address
- (id) initWithCoder:(NSCoder *)aDecoder
{
self = [self init];
if (self == nil)
{
return nil;
}
self.street = [aDecoder decodeObjectOfClass:[Address class] forKey:#"street"];
self.city = [aDecoder decodeObjectOfClass:[Address class]
forKey:#"city"];
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:self.street forKey:#"street"];
[encoder encodeObject:self.city forKey:#"city"];
}
+ (BOOL)supportsSecureCoding
{
return YES;
}
Archive.h
#property (nonatomic, strong) Address *address;
Archive.m
UserDBO *userDBO = [[UserDBO alloc] init];
// Archive
NSError *error = nil;
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.address requiringSecureCoding:YES error:&error];
if(error)
{
NSLog(#"archivedDataWithRootObject: %#", error);
}
userDBO.address = data;
// UnArchive
Address *address = [NSKeyedUnarchiver unarchivedObjectOfClass:[Address class] fromData:userDBO.address error:&error];
if(error)
{
NSLog(#"unarchivedObjectOfClass: %#", error);
}
UserDBO.h
#property (nonatomic, strong) NSData *address;

Unbalanced calls to begin/end appearance transitions for <QLRemotePreviewContentController: 0x7d19a000> in UIDocumentInteraction

I am trying to load files in device by using UIDocumentInteractionController in a phonegap app. I have done by following.
#import <Foundation/Foundation.h>
#import <QuickLook/QuickLook.h>
#interface FileViewer : UIViewController <UIDocumentInteractionControllerDelegate>
#end
#implementation FileViewer
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (UIViewController *) documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller {
return self;
}
- (void)viewDidLoad
{
[self viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
In Launcher.m i have done by following to show files:
CDVViewController* mainController = (CDVViewController*)[ super viewController ];
UIDocumentInteractionController *documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:fileLocation]];
FileViewer *vController = [[FileViewer alloc]init];
[mainController addChildViewController:vController];
documentInteractionController.delegate = vController;
[documentInteractionController presentPreviewAnimated:YES];
This result by giving error message:
"Presenting view controllers on detached view controllers is discouraged.
Unbalanced calls to begin/end appearance transitions for QLRemotePreviewContentController"
Thanks in advance!!
I have solved my issue by following way. It might not be the best way, so please suggest the best way also.
#interface Launcher : CDVPlugin<UIDocumentInteractionControllerDelegate>
#property (strong, nonatomic)UIViewController *navigatorController;
#property (strong, nonatomic)Launcher *launcher;
#property (strong, nonatomic)NSString *callbackId;
-(void)openFile:(CDVInvokedUrlCommand*)command;
#end
#import "Launcher.h"
#import "FileViewer.h"
#import<QuickLook/QuickLook.h>
#import <MobileCoreServices/MobileCoreServices.h>
#implementation Launcher
-(void)openFile:(CDVInvokedUrlCommand*)command{
CDVPluginResult *pluginResult = nil;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSDictionary *params = [command.arguments objectAtIndex:0];
NSString *file = [params objectForKey:#"message"];
NSString *fileLocation = [documentsDirectory stringByAppendingFormat:#"/%#",file];
if (![fileLocation isEqual:[NSNull null]]) {
#try {
self.launcher = self;
CDVViewController *viewController = [CDVViewController new];
viewController.wwwFolderName = #"www";
viewController.startPage = #"main.html"; // page contains listview showing name of files
self.navigatorController = viewController;
UIDocumentInteractionController *documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:fileLocation]];
[[UIApplication sharedApplication].keyWindow setRootViewController:self.navigatorController];
documentInteractionController.delegate = self;
[documentInteractionController presentPreviewAnimated:YES];
}
#catch (NSException *exception) {
NSLog(#"dd : %#",[exception reason]);
}
}
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
- (UIViewController *)documentInteractionControllerViewControllerForPreview: (UIDocumentInteractionController *)controller
{
return self.navigatorController;
}
- (void)documentInteractionControllerDidEndPreview:(UIDocumentInteractionController *)controller
{
[[UIApplication sharedApplication].keyWindow setRootViewController:self.navigatorController];
self.launcher = nil;
}

How can i make a random sound array with objective c?

So i've done the tutorial where you code a button so that when you press it a sound plays. I'm trying to modify it so that when the button is pressed, a random sound plays.
here is the code:
viewcontroller.h
#import <UIKit/UIKit.h>
#import <AudioToolbox/AudioToolbox.h>
#interface STViewController : UIViewController
- (IBAction)playAudio:(id)sender;
#property (nonatomic, strong) NSArray *sounds;
#end
viewcontroller.m
#import <AVFoundation/AVFoundation.h>
#import "STViewController.h"
#interface STViewController ()
#property (weak, nonatomic) IBOutlet UIButton *playAudio;
#end
#implementation STViewController
- (IBAction)playAudio:(id)sender {
AVAudioPlayer *audioPlayer;
NSString *audioPath = [[NSBundle mainBundle] pathForResource:#"Woof" ofType:#"mp3"];
NSURL *audioURL = [NSURL fileURLWithPath:audioPath];
NSError *audioError = [[NSError alloc] init];
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioURL error:&audioError];
if (!audioError) {
[audioPlayer play];
NSLog(#"Woof!");
}
else {
NSLog(#"Error!");
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
I've been dabbling with something along these lines
- (NSArray *)sounds
{
NSArray *sounds = [NSArray arrayWithObjects:
#"Woof.mp3",
#"Meow.mp3",
#"tweet.mp3",
#"Squeak.mp3",
#"Moo.mp3",
#"Croak.mp3",
#"Toot.mp3",
#"Quack.mp3",
#"Blub.mp3",
#"OWOwOw.mp3",
#"Fox.mp3",
nil];
return sounds;
}
but i'm not really sure how to make it random or even implement in in the code that I have going right now. Anyone have any ideas?
Just make it random try below:-
NSMutableArray *array=[NSMutableArray
arrayWithObjects:
#"Woof.mp3",
#"Meow.mp3",
#"tweet.mp3",
#"Squeak.mp3",
#"Moo.mp3",
#"Croak.mp3",
#"Toot.mp3",
#"Quack.mp3",
#"Blub.mp3",
#"OWOwOw.mp3",
#"Fox.mp3",
nil];
// now use exchangeobject with index api
int i=0;
for(i=0;i<=[array count]; i++)
{
NSInteger rand=(arc4random() %10);
[array exchangeObjectAtIndex:i
withObjectAtIndex:rand];
}

Saving the title of a button so it can be accessed in another view (Objective-C)

I'm trying to save the name of a button using a singleton so that the name can be accessed in another view to play a video with the same name. However, I'm getting the error: SIGABRT. I don't really see what's wrong with my code. Any ideas?
#import "List.h"
#import "MyManager.h"
#import "Video.h"
#implementation ExerciseList
-(IBAction) goToVideo:(UIButton *) sender{
MyManager *sharedManager = [MyManager sharedManager];
sharedManager.vidName = [[sender titleLabel] text];
Video *videoGo = [[Video alloc] initWithNibName: #"Video" bundle: nil];
[self.navigationController pushViewController: videoGo animated: YES];
[videoGo release];
}
Here is my .h and .m for MyManager:
#import <foundation/Foundation.h>
#interface MyManager : NSObject {
NSMutableArray *workouts;
NSString *vidName;
}
#property (nonatomic, retain) NSMutableArray *workouts;
#property (nonatomic, retain) NSString *vidName;
+ (id)sharedManager;
#end
#import "MyManager.h"
static MyManager *sharedMyManager = nil;
#implementation MyManager
#synthesize workouts;
#synthesize vidName;
#pragma mark Singleton Methods
+ (id)sharedManager {
#synchronized(self) {
if (sharedMyManager == nil)
sharedMyManager = [[self alloc] init];
}
return sharedMyManager;
}
- (id)init {
if ((self = [super init])) {
workouts = [[NSMutableArray alloc] init];
vidName = [[NSString alloc] init];
}
return self;
}
-(void) dealloc{
self.workouts = nil;
self.vidName = nil;
[super dealloc];
}
#end
You should access the title of the button
sharedManger.vidName = [sender currentTitle];
However you are not using ARC so also check where your vidName property is retain or copy.
if it is not retain or copy then you can use this code also
if(sharedManger.vidname != nil){
[sharedManger.vidName release];
sharedManger.vidName = nil;
}
sharedManger.vidName = [[sender currentTitle] retain];

NSKeyedArchiver archivedDataWithRootObject: does not call encodeWithCoder

I cant get the archivedDataWithRootObject method to call encodeWithCoder, it seems pretty simple from what I understand but it doesn't work.
Here's the code:
#import <Foundation/Foundation.h>
#interface Details : NSObject <NSCoding>{
NSMutableArray *myArray;
}
#property(nonatomic,retain) NSMutableArray *myArray;
#end
#import "Details.h"
#implementation Details
#synthesize myArray;
-(id)init{
[super init];
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder{
NSLog(#"initWithCoder");
if (self = [super init])
{
self.myArray = [[aDecoder decodeObjectForKey:#"details"]retain];
}
return self;
}
- (NSMutableArray*)getDetails{
NSLog(#"getDetials");
// NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *details = [[NSUserDefaults standardUserDefaults] objectForKey:#"details"];
if (details != nil){
NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:details];
if (oldSavedArray != nil)
self.myArray = [[NSMutableArray alloc] initWithArray:oldSavedArray];
else
self.myArray = [[NSMutableArray alloc] initWithCapacity:1];
}
return self.myArray;
}
- (void) addDetails:(NSMutableArray *)details{
NSLog(#"add Details");
[self.myArray addObject:details];
[NSKeyedArchiver archivedDataWithRootObject:self.myArray];
}
- (void) encodeWithCoder:(NSCoder *)coder;
{
NSLog(#"encodeWithCoder");
[coder encodeObject:self.myArray forKey:#"details"];
}
#end
NSLog(#"encodeWithCoder") doesn't run.
I might be missing something, If anyone has any ideas it would be appreciated.
[NSKeyedArchiver archivedDataWithRootObject:self.myArray];
You are telling the NSKeyedArchiver to archive the myArray variable, not the object you have just implemented <NSCoding> with!
Also that method returns NSData * which you then have to write to disk separately if needed.
Try this:
NSData * archivedData = [NSKeyedArchiver archivedDataWithRootObject:self];