Using a UIButton to update the image in a UIImageView - objective-c

I'm really new to app development and I am building an app (that will be more complex but this is just the basis of it which I will later expand). What I have currently is view with a UIImageView and UIButton, code below. I can get the button to set an image from the resources folder, but what I want is for the button to update the UIImageView with a new image each time it is press e.g. press once = image1, pressed twice = image2...etc From what I have been reading I think I should have the images in an array with a key or something then I can just update it with ++ I know this is really vague but as I said I'm really new so any help would be greatly appreciated!
#interface NextImageViewController : UIViewController {
IBOutlet UIImageView *imageView;
}
- (IBAction) nextImagePush;
#end
#implementation NextImageViewController
- (IBAction) nextImagePush {
UIImage *img = [UIImage imageNamed:#"image1"];
[imageView setImage:img];
}

#interface NextImageViewController : UIViewController {
IBOutlet UIImageView *imageView;
NSInteger imageCount;
NSArray * imageArray;
}
- (IBAction) nextImagePush;
#end
#implementation NextImageViewController
- (IBAction) nextImagePush {
UIImage *img = [UIImage imageNamed:[imageArray objectAtIndex:imageCount]];
[imageView setImage:img];
imageCount++;
if (imageCount >= [imageArray count]){
imageCount = 0;
}
}
- (void) viewDidLoad {
imageArray = [[NSArray alloc]
initWithObjects:#"image1", #"image2", #"image3", nil];
imageCount = 0;
}
Try something like that. You wil have to set the initial image and change the count depending on what you set it.

You can simply create a NSArray or NSMutableArray and fill it with all the images you want to show. Then, using a variable visible at global scope (ie a field of your interface), you can use
[imageView setImage:[arrayOfImages objectAtIndex:(index++ % [arrayOfImages count])]];
This code will also show the first image after the last one is displayed.

A really rudimentary way to do this is set a integer. Define it as 0,
and the first time you click the button you add 1 to it.
This definitely isn't working code, but its just to help you get a start. I strongly suggest a book like Big Nerd Ranch's book on iOS or Beginning IPhone 4 Development.
-(IBAction)pic
if variable == 0 {
//show the first image
variable++;
}
else if variable == 1 {
//show the second image
variable++;
}

Related

How to detect change in UISegmentedControl from a separate IBAction

I have a UISegmentedControl button with three segments.
In ViewController.m this is working just fine -- pressing the buttons fires the correct methods.
I have another separate UIButton that when it is pressed it needs to first CHECK the state of the UISegmentedControl (to see which button is currently pressed) and then fire a method according to that segment value.
Here is my code for that separate UIButton. The button itself is working, but I cannot seem to figure out how to GET the current value of the segment of the UISegmentedControl.
Many thanks for any assistance here.
I am new to OBJ-C. I know how to do this in VisualBasic, so answers that are on the more verbose side would be most appreciated as I need to know the 'why'. Thank you.
- (IBAction)decodeButton:(id)sender {
UISegmentedControl *segment = [UISegmentedControl alloc]; // THIS DOES NOT WORK.
if (segment.selectedSegmentIndex == 0) {
decode(textToDecode);
} else if(segment.selectedSegmentIndex == 1) {
decode1(textToDecode);
} else if(segment.selectedSegmentIndex == 2) {
decode2(textToDecode);
}
}
Here is a Tutorial using UISegmentedControl in iOS.
Just Create a Reference object and wire it properly to File Owner.
IBOutlet UISegmentedControl *segmentedControl;
Then set property
#property (strong, nonatomic) IBOutlet UISegmentedControl * segmentedControl;
Synthesize in .m file
#synthesize segmentedControl;
Now You can Access the selected index at any time.
- (IBAction)decodeButton:(id)sender {
if (segmentedControl.selectedSegmentIndex == 0) {
decode(textToDecode);
} else if(segmentedControl.selectedSegmentIndex == 1) {
decode1(textToDecode);
} else if(segmentedControl.selectedSegmentIndex == 2) {
decode2(textToDecode);
}
}
Your code alloc every time UISegmentedControl in the button press action. So use the following code for sUISegmentedControl creation and its action .
SegmentChangeView=[[UISegmentedControl alloc]initWithItems:[NSArray arrayWithObjects:#"Segment1",#"Segment2",#"Segment3",nil]];
SegmentChangeView.frame=CGRectMake(5, 44, self.view.bounds.size.width-10, 33);
SegmentChangeView.selectedSegmentIndex=0;
SegmentChangeView.segmentedControlStyle=UISegmentedControlStyleBar;
SegmentChangeView.momentary = YES;
[SegmentChangeView setTintColor:[UIColor blackColor]];
NSDictionary *attributes =[NSDictionary dictionaryWithObjectsAndKeys:[UIFont fontWithName:#"Arial" size:13],UITextAttributeFont,nil];
[SegmentChangeView setTitleTextAttributes:attributes forState:UIControlStateNormal];
[SegmentChangeView addTarget:self action:#selector(SegmentChangeViewValueChanged:) forControlEvents:UIControlEventValueChanged];
SegmentChangeView.autoresizingMask=UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleBottomMargin;
[self.view addSubview:SegmentChangeView];
-(IBAction)SegmentChangeViewValueChanged:(UISegmentedControl *)SControl
{
if (SControl.selectedSegmentIndex==0)
{
decode(textToDecode);
}
else if (SControl.selectedSegmentIndex==1)
{
decode1(textToDecode);
}
else if (SControl.selectedSegmentIndex==2)
{
decode2(textToDecode);
}
}
You should remove UISegmentedControl *segment = [UISegmentedControl alloc] ; from your code, as it allocs anew instance of your UISegmentedControl every time, instead,
create an outlet for you UISegmentController like
#property (strong, nonatomic) IBOutlet UISegmentedControl * segment;
and then later at any point in your viewcontroller.m file, you can get the currently selected segment by using
segment.selectedSegmentIndex;
Hope this make sense,
Regards
Try like this
- (IBAction)segmentedControlChanged:(id)sender
{
UISegmentedControl *s = (UISegmentedControl *)sender;
if (s.selectedSegmentIndex == 1)
{
//code
}
else
{
//code
}
}
This code means you are creating a new Object on every click
UISegmentedControl *segment = [UISegmentedControl alloc] ;
The thing you have to do take IBOutlet (Property) of your segmentedControl then I will work for you. dont create a new object in the button method. when you will make a IBOutlet it will be link with at segmentControl and your code will work that time . Thanks

Automatically click segmentControl with index 0 on ViewDidAppear

I am hoping to "automate" a click on the segmentController with the index of 0.
My tabBar-based app has multiple segmentControllers in a tab in the ViewDidAppear method, I would like to automatically have it "click" the first segmented controller.
if (segmentController.selectedSegmentIndex == 0) {
//stuff here
}
if (segmentController.selectedSegmentIndex == 1) {
//stuff here
}
Does anyone know how I might accomplish this? Thank you!
If you're creating it programmatically, you could lazy load it like this:
#interface ExampleViewController : UIViewController
#property (nonatomic, strong) UISegmentedControl *segmentedControl;
- (void)segmentedControlClicked:(UISegmentedControl *)segmentedControl;
#end
#implementation ExampleViewController
- (UISegmentedControl *)segmentedControl
{
if (!_segmentedControl)
{
NSArray *items = #[#"First", #"Second", #"Third"];
_segmentedControl = [[UISegmentedControl alloc] initWithItems:items];
[_segmentedControl addTarget:self
action:#selector(segmentedControlClicked:)
forControlEvents:UIControlEventValueChanged];
[_segmentedControl setSelectedSegmentIndex:0]; // Set Default selection
CGRect frame = _segmentedControl.frame;
frame.origin = CGPointMake(0.0f, 0.0f); // Move to wherever you need it
[self.view addSubview:_segmentedControl];
}
return _segmentedControl;
}
- (void)segmentedControlClicked:(UISegmentedControl *)segmentedControl
{
// Whatever your code is goes here...
}
#end
If you're wanting a method to be called also initially, you can call it within your viewDidLoad: method as such:
- (void)viewDidLoad
{
[self.segmentedControl setSelectedSegmentIndex:0]; // Set desired default index (optional if set in lazy load as shown above)
[self segmentedControlClicked:self.segmentedControl];
}
This would hence simulate a click on desired default index.
Be careful putting the above into viewDidAppear: (you could if you really wanted to) because anytime the view comes to the front, this method will be called (in example, if this view controller presents a modal view controller, once the modal is dismissed, this view controller's viewDidAppear: method will be called).
Cheers!
Set the selectedSegmentIndex property on your UISegmentedControl in your viewDidAppear (or viewDidLoad) method.
self.segmentedController.selectedSegemntIndex = 1;
UISegmentedControl Reference

Simple bool switching not working?

I am flipping the value of a simple BOOL like this:
active = !active;
NSLog(#"%#", active? #"active" : #"not active");
I've done this numerous times but right now, for some reason it's not working and it's really pissing me off. When ever the flow hits here, it always prints "active". It's not switching!!
Here's my interface:
#interface HeaderView : UIView {
UILabel *label;
UIButton *button;
UIImageView *background;
BOOL active;
int section;
__weak id<HeaderViewDelegate> delegate;
}
and function where the action is performed:
- (void)actionTUI:(id)sender {
active = !active;
NSLog(#"%#", active? #"active" : #"not active");
UIImage *image = nil;
if (active) {
image = [UIImage imageNamed:#"active.png"];
} else {
image = [UIImage imageNamed:#"unactive.png"];
}
[button setBackgroundImage:image forState:UIControlStateNormal];
[delegate headerView:self toggled:active];
}
Thanks
BOOLean values, as with all other types, are initialized to NULL (0, FALSE, NO) at runtime unless assigned a different value. You are assigning a not(NO) to your BOOL, which prints YES at every run.
Also, your NSLog() is weird, it's probably evaluating itself the wrong way, and most likely contributing to the problem. It should not include the literal:
NSLog(active? #"active" : #"not active");
Ok, the problem was that after toggling, I was reloading the tableview which apparently recalls:
- (UIView *)tableView:(UITableView *)_tableView viewForHeaderInSection:(NSInteger)section {
which caused a new header view to be allocated/initialized with active set to NO and right after toggling, set to YES. That's why every time, even after flipping the values it was set to active (because right after, it would get discarded and a new header view would come to it's place).

Property is null after adding subview

I'm launching a UIPopoverViewController that is supposed to draw two buttons (add and delete buttons). The ContentViewController for the UIPopover has a property called outputJackView that is set just before the UIPopoverViewController is launched. This property is necessary for the buttons to draw properly. The problem is right after the first button is added as a subview, the outputJackView is set to null somehow.
Here is the ContentViewController for UIPopoverViewController:
CableConnectionMenuController.h
#import <UIKit/UIKit.h>
#class JackView;
#interface CableConnectionMenuController : UIViewController
{
JackView *outputJackView;
}
#property (nonatomic, weak) id <CableConnectionDelegate> delegate;
#property (nonatomic, strong) JackView *outputJackView;
- (void)setButtonTextWithOutputJack:(JackView *)outputJack withInputArray:(NSMutableArray *)inputArray;
- (void)createAddConnectionButton;
- (void)createDeleteConnectionButton;
#end
CableConnectionMenuController.m
#import "CableConnectionMenuController.h"
#import "JackView.h"
#import "CableDisconnectButton.h"
#implementation CableConnectionMenuController
#synthesize delegate;
#synthesize outputJackView;
...
- (void)viewDidLoad
{
[super viewDidLoad];
//alloc output jack view
self.outputJackView = [[JackView alloc] init];
//set size of popover view in cables view
self.contentSizeForViewInPopover = CGSizeMake(200, 200);
//change view background color
self.view.backgroundColor = [UIColor whiteColor];
}
//this method is called from the class that launches the UIPopoverViewController
- (void)setButtonTextWithOutputJack:(JackView *)outputJack withInputArray:(NSMutableArray *)inputArray
{
//set output jack which will be the same for all inputs
self.outputJackView = outputJack;
//draw add connection button
[self createAddConnectionButton];
//draw delete connection button - not working
//[self createDeleteConnectionButton];
}
- (void)createAddConnectionButton
{
CableDisconnectButton *addConnectionButton = [CableDisconnectButton buttonWithType:UIButtonTypeCustom];
addConnectionButton.frame = CGRectMake(0, 0, 190, 40);
[addConnectionButton setBackgroundImage:[UIImage imageNamed:#"images/cable_connect_button.png"] forState:UIControlStateNormal];
[addConnectionButton setBackgroundImage:[UIImage imageNamed:#"images/cable_connect_button_over.png"] forState:UIControlStateHighlighted];
//add output jack
addConnectionButton.outputJack = self.outputJackView;
//add action to button
[addConnectionButton addTarget:self action:#selector(addConnectionButtonTarget:) forControlEvents:UIControlEventTouchUpInside];
NSLog(#"output jack name before: %#", self.outputJackView.jackName);
[self.view addSubview:addConnectionButton];
NSLog(#"output jack name after: %#", self.outputJackView.jackName);
}
The two NSLog's at the end return the name correctly on the first one (before) and return null on the second (after). The jackName properties are NSString's. It's obvious that the property is being set to null after a subview is added, but I can't figure out why that would happen.
Here is the method from the class that launches the UIPopoverViewController in case it matters:
- (void)editCableConnectionsWith:(JackView *)outputJack
{
//launches the note menu popover
self.cableConnectionMenuController = [[CableConnectionMenuController alloc] init];
self.cableConnectionMenuController.delegate = (id)self;
//find appropriate connection to edit
for (JackView *currentJack in jackArray)
{
if (currentJack == outputJack)
{
//create temp array of input jacks to send to cable connection controller
NSMutableArray *inputButtonTempArray = [self returnInputJackArrayWithOutputJack:currentJack];
//set information for creating disconnect buttons in popover
[self.cableConnectionMenuController setButtonTextWithOutputJack:currentJack withInputArray:inputButtonTempArray];
}
}
self.editConnectionsPopoverController = [[UIPopoverController alloc] initWithContentViewController:self.cableConnectionMenuController];
[self.editConnectionsPopoverController presentPopoverFromRect:pulseRing.frame inView:self permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
How is the jackName property declared? My guess is that it's a weak reference.
I had a similar issue where a weak reference on a view was reset after the view was added as a subview. My understanding is that a weak reference should only be used when there's a potential retain cycle (e.g. you have a backpack with a reference to a calculator and the calculator points back to the backpack--see the Big Nerd Ranch book).
I'm not quite sure why that's an issue here, but I encountered something similar and figured I'd share.

Subclassing UIImageView - animation images do not display or appear

I'm trying to subclass UIImageView in order to create an instance of the subclass which plays different animations, depending on a variable I send it. My initial code (before subclassing) for playing a specific 2 frame animation looked like this. 'bubbleAnimationTemp' is just a UIImageView object I declared in the header:
UIImage *animImage1 = [UIImage imageNamed:#"HappyAnim1.png"];
UIImage *animImage2 = [UIImage imageNamed:#"HappyAnim2.png"];
NSArray *images = [[NSArray alloc] initWithObjects:animImage1, animImage2, nil];
bubbleAnimationTemp.animationImages = images;
[images release];
bubbleAnimationTemp.animationDuration = 0.5;
bubbleAnimationTemp.animationRepeatCount = 5;
[bubbleAnimationTemp startAnimating];
So then I tried subclassing UIImageView like so:
#import <UIKit/UIKit.h>
#import "Interaction.h"
#interface BubbleAnimation : UIImageView {
UIImage *emotAnim1;
UIImage *emotAnim2;
}
#property (nonatomic, retain) UIImage *emotAnim1;
#property (nonatomic, retain) UIImage *emotAnim2;
- (BubbleAnimation *)initWithMood:(NSString *)mood;
#end
#import "BubbleAnimation.h"
#implementation BubbleAnimation
#synthesize emotAnim1;
#synthesize emotAnim2;
- (BubbleAnimation *)initWithMood:(NSString *)mood {
if (self = [super init]) {
NSLog(#"Mood: %#", mood);
if ([mood isEqualToString:kGood]) {
emotAnim1 = [[UIImage alloc] initWithContentsOfFile:([[NSBundle mainBundle] pathForResource:#"HappyAnim1" ofType:#"png"])];
emotAnim2 = [[UIImage alloc] initWithContentsOfFile:([[NSBundle mainBundle] pathForResource:#"HappyAnim2" ofType:#"png"])];
//emotAnim1 = [UIImage imageNamed:#"HappyAnim1.png"];
//emotAnim2 = [UIImage imageNamed:#"HappyAnim2.png"];
}
else if ([mood isEqualToString:kNeutral]) {
emotAnim1 = [UIImage imageNamed:#"NeutralAnim1.png"];
emotAnim2 = [UIImage imageNamed:#"NeutralAnim2.png"];
}
else {
emotAnim1 = [UIImage imageNamed:#"SadAnim1.png"];
emotAnim2 = [UIImage imageNamed:#"SadAnim2.png"];
}
NSArray *images = [[NSArray alloc] initWithObjects:emotAnim1, emotAnim2, nil];
self.animationImages = images;
[images release];
}
return self;
}
As you can see, I tried two different approaches for creating the UIImages to add to the UIImageView. But the problem I'm having is that nothing shows up when the animation plays.
I also tried simply copying the code from the first method into this subclass, so the process is essentially the same, but still nothing appears.
I've checked the documentation for notes on subclassing UIImageView but there doesn't seem to be anything I'm missing. I've made sure to change the 'UIImageView' object I placed in Interface Builder into a 'BubbleAnimation' object, so it's not that.
Any help as to why nothing appears would be very much appreciated. Thanks as always!
Michael
****************UPDATE****************
Well, thanks to Kalle's advice below, this is all fixed. However, now a similar issue is reoccurring and I wonder what I'm doing wrong.
Basically, I want to have a small heart that appears in the thought bubble, alongside the animation. I've added a UIImage to the BubbleAnimation class like so:
#interface BubbleAnimation : UIImageView {
UIImage *emotAnim1;
UIImage *emotAnim2;
UIImage *heart;
}
#property (nonatomic, retain) UIImage *heart;
- (void)setMood:(NSString *)mood;
#end
And synthesise it in the implementation as usual. Then I set the heart to the correct colour in the setMood method:
- (void)setMood:(NSString *)mood {
if ([mood isEqualToString:kGood]) {
emotAnim1 = [UIImage imageNamed:#"Good1.png"];
emotAnim2 = [UIImage imageNamed:#"Good2.png"];
self.heart = [UIImage imageNamed:#"HeartRed.png"];
}
else if ...
In IB, I've added a hidden UIImageView object and linked it to a UIImageView IBOutlet in my ViewController called bubbleHeart. When the thought bubble appears, I use the following code to display the animations and the heart:
[bubbleAnimation setMood:charachter.mood];
self.bubbleHeart.image = bubbleAnimation.heart;
bubbleAnimation.animationDuration = kAnimationDuration;
bubbleAnimation.animationRepeatCount = kAnimationRepeatCount;
[bubbleAnimation startAnimating];
bubbleHeart.hidden = FALSE;
The problem is, the animation appears, but the little heart doesn't. I've tried various approaches - I created the UIImageView in the BubbleAnimation class, instead of using a UIImage, and tried initialising it in various different ways, but no joy. If I call something like self.bubbleHeart = [[UIImageView alloc] initWithImage:bubbleAnimation.heart]; then presumably I'm reinitialising the variable so that doesn't work. Any idea why it's not appearing?
Many thanks!
Michael
In your original code, you never allocated a new instance of BubbleAnimation, and that's why it worked fine.
You have an object in the XIB file which is the BubbleAnimation object, and that's all fine, but the problem is that you're allocating a new instance of BubbleAnimation instead of using the one you have in your revised code.
Even if you were to use the one you have, you'd have to change the init method, since the system will call initWithCoder:, not initWithMood:.
The best thing is most likely to change the init function to something else, e.g. a setMood:, which modifies the image view. Then you re-use the same image view every time.
Then you'd just set it up using something like...
[bubbleAnimation setMood:character.mood];
bubbleAnimation.animationDuration = 0.5;
bubbleAnimation.animationRepeatCount = 5;
[bubbleAnimation startAnimating];