I am having some trouble loading images from a file into an array. I have used a combination of questions I have found on here, and am out of ideas.... I am new to objective-c and rusty on the rest.
My viewDidLoad simply calls my showPics method, and for testing sake I have the _imgView just show the image at position 1 in the array.
It could very well be a problem with the way I am showing the images as well. I have a ViewController and one ImageView (titled: imgView) in my Storyboard.
here is my showPics method:
-(void)showPics
{
NSArray *PhotoArray = [[NSBundle mainBundle] pathsForResourcesOfType:#"jpg" inDirectory:#"Otter_Images"];
NSMutableArray *imgQueue = [[NSMutableArray alloc] initWithCapacity:PhotoArray.count];
for (NSString* path in PhotoArray)
{
[imgQueue addObject:[UIImage imageWithContentsOfFile:path]];
}
UIImage *currentPic = _imgView.image;
int i = -1;
if (currentPic != nil && [PhotoArray containsObject:currentPic]) {
i = [PhotoArray indexOfObject:currentPic];
}
i++;
if(i < PhotoArray.count)
_imgView.image= [PhotoArray objectAtIndex:1];
}
Here is my viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self showPics];
}
Here is my ViewController.h
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UIImageView *imgView;
#end
Please let me know if you need anything else and thank you in advance!
In your showPics method, other than the initial 'for-loop', all of your references to PhotoArray should instead be references to imgQueue. PhotoArray is a list of pathnames. imgQueue is the array of actual UIImage objects.
-(void)showPics {
NSArray *PhotoArray = [[NSBundle mainBundle] pathsForResourcesOfType:#"jpg" inDirectory:#"Otter_Images"];
NSMutableArray *imgQueue = [[NSMutableArray alloc] initWithCapacity:PhotoArray.count];
for (NSString* path in PhotoArray) {
[imgQueue addObject:[UIImage imageWithContentsOfFile:path]];
}
UIImage *currentPic = _imgView.image;
int i = -1;
if (currentPic != nil && [imgQueue containsObject:currentPic]) {
i = [imgQueue indexOfObject:currentPic];
}
i++;
if(i < imgQueue.count) {
_imgView.image = [imgQueue objectAtIndex:1];
}
}
Related
I have an array of NSStrings, what I'd like to use as a source for a UIImageView's image names.
When the user taps a button I load a new image - next object from the array - into the same image view, that would be the final goal. Actually I have a working, but silly solution, and that's not what I want. I would like to load the string names from an array to the UIImage, because this if statement can grow really big with 30-40 object and that's not so reliable. I'm not so fine with for loops so I would really appreciate if somebody could show me how can I get the same result with a loop or any other way.
- (IBAction)changeImage:(id)sender {
if (!self.userImageView.image) {
UIImage *image = [UIImage imageNamed:#"img1.png"];
self.userImageView.image = image;
self.currentDisplayedImageString = #"img1.png";
// self.currentDisplayedImageString is an ivar, type of NSString
}
else {
if ([self.currentDisplayedImageString isEqualToString:#"img1.png"]) {
UIImage *image = [UIImage imageNamed:#"img2.png"];
self.userImageView.image = image;
self.currentDisplayedImageString = #"img2.png";
}
if ([self.currentDisplayedImageString isEqualToString:#"img2.png"]) {
UIImage *image = [UIImage imageNamed:#"img3.png"];
self.userImageView.image = image;
self.currentDisplayedImageString = #"img3.png";
}
// AND SO ON...
}
}
something like:
#interface ViewController ()
#property (strong, nonatomic) UIImageView *imageView;
#property (strong, nonatomic) NSArray *imageNames;
#property (assign, nonatomic) int currentImageIndex;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.imageNames = #[#"img1", #"img2", #"img3", #"img4"];
self.currentImageIndex = -1;
}
- (void)changeImage {
if (++self.currentImageIndex == self.imageNames.count) {
self.currentImageIndex = 0;
}
self.imageView.image = [UIImage imageNamed:self.imageNames[self.currentImageIndex]];
}
#end
hope it helps!
If your image names are in fact "img1," "img2," "img3," etc. you don't actually need an array to store them since the names essentially index themselves. Instead I'd recommend doing something like:
- (IBAction)changeImage:(id)sender {
if (!self.userImageView.image ||
[self.currentDisplayedImageString isEqualToString:#"img40.png"]) {
self.currentDisplayedImageString = #"img1.png";
}
else {
// Get the filename's numerical index by parsing out the numerical component
NSString *index = [[self.currentDisplayedImageString componentsSeparatedByCharactersInSet:
[[NSCharacterSet decimalDigitCharacterSet] invertedSet]]
componentsJoinedByString:#""];
// "Increment" the currentDisplayedImageString
self.currentDisplayedImageString = [NSString stringWithFormat:#"img%#.png", index];
}
// Then update the image
UIImage *image = [UIImage imageNamed:currentDisplayedImageString];
self.userImageView.image = image;
}
Have a question about blocks in objective-c.
For example I have a list of actions.
I'm initializing an array of blocks:
self.actions = #[
^() { [self showObject:self.object_1]; },
^() { [self showObject:self.object_2]; },
^() { [self showObject:self.object_3]; }
];
And calling them when some row is pressed:
- (void)pressedRowAtIndex:(NSInteger)index {
if (index < actions.count) {
void (^action)() = [actions objectAtIndex:index];
if (action != nil) {
action();
}
}
}
And all works fine without problem. But when I init my actions array by using initWithObjects method:
self.actions = [NSArray alloc] initWithObjects:
^() { [self showObject:self.object_1]; },
^() { [self showObject:self.object_2]; },
^() { [self showObject:self.object_3]; },
nil
];
Than I get crash trying to get action by index by using objectAtIndex method of NSArray class.
I understand the difference between this inits. First one don't increase reference count like first do. But can someone explain why it crash?
Edit:
All that I've found. Maybe I'm nub and somewhere else is another useful information.
There is no crash info in terminal:
Code for Onik IV:
Small example:
#interface ViewController () {
NSArray *actions;
}
#property (nonatomic, strong) NSString *object1;
#property (nonatomic, strong) NSString *object2;
#property (nonatomic, strong) NSString *object3;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
actions = [[NSArray alloc] initWithObjects:
^() { [self showObject:self.object1];},
^() { [self showObject:self.object2]; },
^() {[self showObject:self.object3]; },
nil];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.object1 = #"object 1";
self.object2 = #"object 2";
self.object3 = #"object 3";
void(^firsSimpleBlock)(void) = [actions lastObject];
firsSimpleBlock();
void(^simpleBlock)(void) = [actions firstObject];
simpleBlock();
}
-(void)showObject:(NSString *)object
{
NSLog(#"Show: %#",object);
}
#end
Try something like this.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
(^someBlock)(void) = ^void(void){
self.object1;
};
actions = [[NSArray alloc] initWithObjects:
[someBlock copy],
[someOtherBlock copy],
[anotherBlock copy],
nil];
}
Blocks are allocated on the stack and are therefor removed when the frame is removed from the stack leading to sail pointers for all pointers pointing to that block. When you allocate a object with the literal "#" sign the object is allocated in a pool so all literals that are the "same" point to the same instance and are never deallocated.
NSString *a = #"A";
NSString *b = #"A";
points to the same instance of a string, while:
NSString *a = [NSString stringWithFormat:#"A"];
NSString *b = [NSString stringWithFormat:#"A"];
are two different objects.
So it works when you are creating a literal array but when you add the blocks dynamically they will be removed when its time to use them therefor the BAD_ACCESS. Solution is to send "copy" message to the block that will copy it to the heap and the block will not be released.
It´s the same, you must have another kind of problem (sintax?).
Try this:
#interface ViewController ()
#property (nonatomic, strong) NSString *object1;
#property (nonatomic, strong) NSString *object2;
#property (nonatomic, strong) NSString *object3;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.object1 = #"object 1";
self.object2 = #"object 2";
self.object3 = #"object 3";
NSArray *actions = #[^() { [self showObject:self.object1];},
^() { [self showObject:self.object2]; },
^() {[self showObject:self.object3]; }
];
NSArray *secondActions = [[NSArray alloc] initWithObjects:
^() { [self showObject:self.object1];},
^() { [self showObject:self.object2]; },
^() { [self showObject:self.object3];},
nil
];
void(^firsSimpleBlock)(void) = [actions lastObject];
firsSimpleBlock();
void(^simpleBlock)(void) = [secondActions firstObject];
simpleBlock();
}
-(void)showObject:(NSString *)object
{
NSLog(#"Show: %#",object);
}
#end
I have an NSArray that I fill with SKTexture to animate an animal leaping:
#interface PSFrogNode ()
#property (strong, nonatomic) NSArray *leapFrames;
#end
#implementation PSFrogNode {
}
- (instancetype)init
{
self = [super init];
if (self) {
SKTextureAtlas *frogLeapAtlas = [SKTextureAtlas atlasNamed:kFrogLeapAtlas];
int numImages = frogLeapAtlas.textureNames.count;
NSMutableArray *tempArray = [NSMutableArray arrayWithCapacity:numImages];
for (int i=1; i <= numImages/2; i++) {
NSString *textureName = [NSString stringWithFormat:#"frogLeap%d", i];
SKTexture *temp = [frogLeapAtlas textureNamed:textureName];
[tempArray addObject:temp];
}
self.leapFrames = [NSArray arrayWithArray:tempArray];
self = [PSFrogNode spriteNodeWithTexture:[self.leapFrames objectAtIndex:0]];
[self setUserInteractionEnabled:NO];
}
return self;
}
- (void)animateFrogLeap
{
[self runAction:[SKAction animateWithTextures:self.leapFrames timePerFrame:0.09 resize:NO restore:YES]];
}
The array self.leapFrames is allocated in the init method but when the scene calls the animateFrogLeap method it's empty (meaning nil).
Why is that?
You want to create the array outside of the init method and then pass it in:
When creating the view
SKTextureAtlas *frogLeapAtlas = [SKTextureAtlas atlasNamed:kFrogLeapAtlas];
int numImages = frogLeapAtlas.textureNames.count;
NSMutableArray *images = [NSMutableArray arrayWithCapacity:numImages];
for (int i=1; i <= numImages/2; i++) {
NSString *textureName = [NSString stringWithFormat:#"frogLeap%d", i];
SKTexture *temp = [frogLeapAtlas textureNamed:textureName];
[images addObject:temp];
}
PSFrogNode *node = [[PSFrogNode alloc] initWithImages:images];
// Now use the node
PSFrogNode.h
-(instancetype)initWithImages:(NSArray *)images;
PSFrogNode.m
#interface PSFrogNode ()
#property (nonatomic, copy) NSArray *leapFrames;
#end
#implementation PSFrogNode
-(instancetype)initWithImages:(NSArray *)images
{
self = [self initWithTexture:[images firstObject]];
if (self) {
_leapFrames = [images copy];
[self setUserInteractionEnabled:NO];
}
return self;
}
As mentioned in my comment, the below line in your original implementation would mean that self would be reassigned, so the line before it (assigning the array to the property) would then be meaning less.
self = [PSFrogNode spriteNodeWithTexture:[self.leapFrames objectAtIndex:0]];
I have a list of strings in two arrays truth & dare that are loaded from a plist then randomised and displayed to the user in a label these could be truth or dares. I would like to display a image for both truth and dare when each one is displayed dependant on which array it is loaded from.
As i load the plist file into an array on load im not sure how to go about doing this? Im very new to xcode and am excited at the progress im making so far however.
This is the full code from my .m file
#interface ViewController ()
#end
#implementation ViewController
- (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.
}
#synthesize plistArray;
- (void)viewDidAppear:(BOOL)animated {
[self becomeFirstResponder];
NSUserDefaults *defaults =[NSUserDefaults standardUserDefaults];
NSString *path = [[NSBundle mainBundle] pathForResource:
#"data" ofType:#"plist"];
if ([[defaults objectForKey:#"truthonoff"] isEqualToString:#"YES"] && [[defaults objectForKey:#"dareonoff"] isEqualToString:#"YES"] ) {
self.text.text =#"Are you ready for this?";
NSDictionary *plistDict1 = [[NSDictionary alloc] initWithContentsOfFile:path];
NSArray * plistArray1 = plistDict1[#"truth"];
NSDictionary *plistDict2 = [[NSDictionary alloc] initWithContentsOfFile:path];
NSArray *plistArray2 = plistDict2[#"dare"];
self.plistArray = [[plistArray1 arrayByAddingObjectsFromArray:plistArray2] mutableCopy];
}
else if ([[defaults objectForKey:#"truthonoff"] isEqualToString:#"YES"] ) {
self.text.text =#"I hope you are feeling brave!";
NSDictionary *plistDict3 = [[NSDictionary alloc] initWithContentsOfFile:path];
NSArray *plistArray3 = plistDict3[#"truth"] ;
self.plistArray = [plistArray3 mutableCopy];
NSLog(#"%#", plistArray);
}
else if ([[defaults objectForKey:#"dareonoff"] isEqualToString:#"YES"] ) {
self.text.text =#"This could be interesting!";
NSDictionary *plistDict4 = [[NSDictionary alloc] initWithContentsOfFile:path];
NSMutableArray *plistArray4 = plistDict4[#"dare"];
self.plistArray = [plistArray4 mutableCopy];
NSLog(#"%#", plistArray);
}
else {
self.text.text =#"Please turn on Truth or Dare";
}
}
- (void)viewDidDisappear:(BOOL)animated {
[self becomeFirstResponder];
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
// Do your thing after shaking device
if ([plistArray count] == 0) {
self.text.text = #"Please Upgrade for more";
}
else {
////display random quote from array
int randV = arc4random() % self.plistArray.count;
self.text.text = self.plistArray[randV];
[self.plistArray removeObjectAtIndex:randV];
}
}
-(IBAction)modal:(id)sender{
}
- (IBAction)container:(id)sender {
}
- (IBAction)shownext:(id)sender {
if ([plistArray count] == 0) {
self.text.text = #"Please Upgrade for more";
}
else {
////display random quote from array
int randV = arc4random() % self.plistArray.count;
self.text.text = self.plistArray[randV];
[self.plistArray removeObjectAtIndex:randV];
}
}
#end
And here is my plist structure there will be alot more data added here when finished tho
It's a little tough to understand the code, so I'll state this in general terms. If you have two arrays of strings (maybe your truth array and dare array), and you have a string that the user selected (this is the part that's unclear in your code -- is it self.text.text?) Let's just call it selectedString, then you can test:
if ([self.truthArray containsObject:selectedString]) {
// selectedString is from the truth array
} else if ([self.dareArray containsObject:selectedString]) {
// selectedString is from the dare array
} else {
// it's in neither array. maybe this is an error
}
I am using this code to generate random image to UIImageView. When I click the button, it apears another image. I would like to ask you how to create four imageviews - click button -> apears four different random images.
ViewController.h
#interface ViewController : UIViewController {
IBOutlet UIImageView *imageView;
// IBOutlet UIImageView *imageView2;
}
-(IBAction) randomImage;
ViewController.m
- (IBAction) randomImage {
NSArray *images = [[NSArray alloc] initWithObjects:#"image1.jpg", #"image2.jpg", #"image3.jpg", #"image4.jpg", nil];
int count = [images count]
int index = arc4random() % count;
imageView.image = [UIImage imageNamed:[images objectAtIndex:index]];
// imageView2.image = ....
}
I created four imageViews in storyboard and I was trying something like above // imageView2.image... but it it not the right solution :-)
Thank you for your help
Code to list 4 random image:
NSMutableArray *images = [[NSMutableArray alloc] initWithArray: #[#"image1.jpg", #"image2.jpg", #"image3.jpg", #"image4.jpg"]];
int countImg = images.count;
for (int i = 0; i < countImg; i++) {
NSInteger index = arc4random() % images.count;
NSLog(#"Image%d name = %#",i , [images objectAtIndex:index]);
[images removeObjectAtIndex:index];
}
Rather than using IBOutlet UIImageView *imageView;, use IBOutletCollection(UIImageView) NSArray *imageViews; and connect all of your image views to this.
Then, in your code:
- (IBAction) randomImage {
NSArray *images = [[NSArray alloc] initWithObjects:#"image1.jpg", #"image2.jpg", #"image3.jpg", #"image4.jpg", nil];
NSInteger count = [images count];
for (UIImageView *imageView in self.imageViews) {
NSInteger index = arc4random() % count;
imageView.image = [UIImage imageNamed:[images objectAtIndex:index]];
}
}