UIImages /UIButtons behave differently every other time program is compiled - ios

I'm making a simple flashcard application. Here's a partial screenshot of one of my app storyboards:
It contains one UIImageView ("turnImage") on top of two buttons ("yesButton", "noButton"), and an additional third button which covers the whole view and overlaps with all the other elements (invisible in this screenshot). When this view is originally displayed, the images on the two buttons are set to nil and the UIImageView is set to display a "tap to change" graphic (with the third invisible button actually catching the tap):
[yesButton setImage:nil forState:UIControlStateDisabled];
[noButton setImage:nil forState:UIControlStateDisabled];
turnImage.image = [UIImage imageNamed:#"card-flip-01.png"];
yesButton.enabled = NO;
noButton.enabled = NO;
When the image is tapped (or the user touches anywhere else on the screen), the image changes to the "correct" and "incorrect" buttons, from which the user can pick whether or not they knew the word (not shown in these screenshots):
turnImage.image = nil;
yesButton.enabled = YES;
noButton.enabled = YES;
[yesButton setImage:[UIImage imageNamed:#"card-answer-correct-01.png"] forState:UIControlStateNormal];
[noButton setImage:[UIImage imageNamed:#"card-answer-wrong-01.png"] forState:UIControlStateNormal];
The above screenshots show the normal behaviour. However, I only get this behaviour on every other compilation - the rest of the time, the initial screen (which is supposed to display only the "tap to change" graphic) shows all three images, and the "tap to change" graphic is stretched differently from normal:
Aside for the graphical glitch, the program works fine: tapping anywhere gets the user to the next screen, where the two buttons are displayed correctly.
I get this both in the simulator and when run on a device. In the simulator, the behaviour changes every other time that the program is run; resetting the simulator content and settings does not affect this. On a device, the behaviour changes whenever I tell Xcode to run it on the device. If I unplug the device from a computer and start/shut down the application without changing its code, the behaviour does not change. (It is the combination of these factors that's making me assume the change in behaviour is linked to compilations.)
I tried closing down Xcode and starting it again, as well as rebooting the computer. These had no effect. I'm running the most recent version of Xcode.
The above code snippets aren't exactly the same as in my program: they've been simplified a bit for readability. There shouldn't be any functional changes, but just to be sure, here's the original code as well:
- (void)viewDidLoad{
// I've cut irrelevant stuff that happens before
[yesButton setImage:nil forState:UIControlStateDisabled];
[noButton setImage:nil forState:UIControlStateDisabled];
flipped = NO;
turnImage.image = [UIImage imageNamed:#"card-flip-01.png"];
[self setResponseNavigationButtons:flipped];
}
- (void) setResponseNavigationButtons:(BOOL)status{
if (status){
turnImage.image = nil;
[yesButton setImage:[UIImage imageNamed:#"card-answer-correct-01.png"] forState:UIControlStateNormal];
[noButton setImage:[UIImage imageNamed:#"card-answer-wrong-01.png"] forState:UIControlStateNormal];
} else {
turnImage.image = [UIImage imageNamed:#"card-flip-01.png"];
[yesButton setImage:nil forState:UIControlStateNormal];
[noButton setImage:nil forState:UIControlStateNormal];
}
yesButton.enabled = status;
noButton.enabled = status;
}
setResponseNavigationButtons is called with YES when changing to the other view.

D'oh, found the error. While rebuilding the project for the Nth time, I finally noticed the warning I had missed before:
Warning: Multiple build commands for output file ... card-flip-01.png
Apparently I had two different images with the same name in my resources, and the compiler would alternate between which one it would grab. Renaming the wrong one fixed the problem - it was transparent, and was letting the yes/no images show through.

Related

UIButton not work in Landscape mode

After rotation of iPad in my simulator i found a strange bug - my UIButton have visible title, it property enables is YES, but, its not work. I tried to set background colour of button to black, and found that after rotation it vanish. Please take a look at portrait mode (all work):
And landscape mode (not work):
This is how i create button (i doubt that it help though, because its pretty mundane):
self.readNextButton = [UIButton new];
self.readNextButton.backgroundColor = [UIColor blackColor];
// self.readNextButton.titleLabel.adjustsFontSizeToFitWidth = YES;
[self.readNextButton setTitleColor:[UIColor colorWithHexString:#"#60aabf"] forState:UIControlStateNormal];
self.readNextButton.titleLabel.lineBreakMode = NSLineBreakByClipping;
self.readNextButton.titleLabel.font = [UIFont fontWithName:#"Times New Roman" size:14];
[self.readNextButton setTitle:#"Читать дальше" forState:UIControlStateNormal];
[self.view addSubview:self.readNextButton];
Also, i want to notice that this bug appears even when i'm not rotating anything, but simply load app in landscape mode. And also, ENABLED property of button is 1 (mean YES), when i log it..
Button frame:
[self.readNextButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.containerForNewsText.mas_right).with.offset(-10.834);
make.bottom.equalTo(self.containerForNewsText.mas_bottom).with.offset(-10.834);
make.top.equalTo(self.truncatedNewsText.mas_bottom).with.offset(20);
}];
It is in a method 'createLandscapeConstraints', that called in end of viewDidLoad.

Vuforia IOS cylinder example - Add button

I have successfully built the sample application from Vuforia. I am interested in taking the Cylinder Tracking and replacing the soccer ball that rotates around the soda can with a button that does not rotate but sits at a certain position in relation to the can (the center of the tracking image).
I am new to iOS development and am not sure quite how to do this. Ideally this would be done programmatically.
I was able to add a button to the CylinderTargetsViewController.mm using the following code:
- (void)viewDidLoad
{
[super viewDidLoad];
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
[button setTitle:#"Gossamer Rules" forState:UIControlStateNormal];
[button sizeToFit];
button.center = CGPointMake(320/2, 60);
[button addTarget:self action:#selector(buttonPressed:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}
- (void)buttonPressed:(UIButton *)button {
UIApplication *mySafari = [UIApplication sharedApplication];
NSURL *myURL = [[NSURL alloc]initWithString:#"http://www.example.com"];
[mySafari openURL:myURL];
}
However this adds a button to the screen whether the target is found or not and it has no spatial relationship to the target.
I tried moving this code into CylinderTargetsEAGLView.mm but got an error on line [self.view addSubview:button]; saying view was not a property of self.
I realize this is probably not the right approach. I'm thinking the button needs to be rendered as an openGl object or something so that it is 3 dimensional and can be seen from an angle when moving the target.
Has anyone done this or have any clue how it may be done?
I have decided to use the Vuforia Unity Package as there is a much steeper learning curve trying to learn pure OpenGL programming. The Unity route has much larger community support, and more tutorials / information are available.

UIButton retains the glows after it has been touched

Hi i am making customUIButtons in my app by using the following piece of code.
+ (NSArray *) createButtonItemNormalImage:(UIImage *)normalImage
highlightImage:(UIImage *)highlightImage
disabledImage:(UIImage *)disabledImage
touchUpSelector:(SEL)selector
target:(id)target
{
// HighlightImage is not used. Highlight is shown using iOS glow
UIButton *uiButton = [UIButton buttonWithType:UIButtonTypeCustom];
uiButton.bounds = CGRectMake(0,
0,
normalImage.size.width,
normalImage.size.height);
[uiButton setImage:normalImage
forState:UIControlStateNormal];
if (disabledImage)
{
[uiButton setImage:disabledImage
forState:UIControlStateDisabled];
}
[uiButton addTarget:target
action:selector
forControlEvents:UIControlEventTouchUpInside];
uiButton.showsTouchWhenHighlighted = YES;
UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc] initWithCustomView:uiButton];
return [NSArray arrayWithObjects:buttonItem, uiButton, nil];
}
I have made a cancel button using the above function. The cancel button takes the user from one screen to another screen. The problem is when i come back to the first screen the cancel button is still glowing. I have seen this problem before also but a call to [self.view setNeedsLayout] used to solve it.
Why does it happen and what would be a correct way of solving it?
Thanks!
To solve this problem in not so standard way I now set the highlighted state to no for all the buttons when I enter the first screen. I use myButton.highlighted = NO;. However the documentation says following for highlighted property.
Specify YES if the control is highlighted; otherwise NO. By default, a control is not highlighted. UIControl automatically sets and clears this state automatically when a touch enters and exits during tracking and when there is a touch up.
Its not happening so in my case.I would love to know the reason behind it and standard ways of solving it

Disabled buttons not changing image in

I have a button that pushes to a settings viewController. On that settings viewController I has a switch to invert all the colors on the original view, an inversion which I've done programmatically. I made inverted button images to replace the original images.
When I return to the original view, I have viewWillAppear call the method to invert if the switch has been flipped. Everything changes accordingly except for two disabled buttons.
The switch value is saved under the default settings so that the user can exit the app and come back later and still have the colors inverted. When viewDidLoad calls the invert method, the buttons change just fine, and they even show up adjusted to show they are disabled.
Any idea what might be going on?
-(void)invert{
self.view.backgroundColor = [UIColor blackColor];
//Buttons
[self.stopButton setImage:[UIImage imageNamed:#"stopinverted"]
forState:UIControlStateNormal];
[self.playButton setImage:[UIImage imageNamed:#"playinverted"]
forState:UIControlStateNormal];
}
-(void)viewWillAppear:(BOOL)animated{
_inverted =
[[NSUserDefaults standardUserDefaults] boolForKey:#"isInvertedOn"];
[[NSUserDefaults standardUserDefaults] synchronize];
if(_inverted){
[self invert];
} else {
[self unInvert];
}
[super viewWillAppear:(BOOL)animated];
}
The issue seems to be that the image is not updated automatically, you'll need to reset the enabled value to make it work:
[self.stopButton setImage:[UIImage imageNamed:#"stopinverted"]
forState:UIControlStateNormal];
self.stopButton.enabled = ! self.stopButton.enabled;
self.stopButton.enabled = ! self.stopButton.enabled;
[self.playButton setImage:[UIImage imageNamed:#"playinverted"]
forState:UIControlStateNormal];
self.playButton.enabled = ! self.playButton.enabled;
self.playButton.enabled = ! self.playButton.enabled;
I think you are disabling stopButton and playButton before applying the image. And also you are applying UIImage for UIButton normal UIControlState. I would suggest do the following code changes
Change this
//Buttons
[self.stopButton setImage:[UIImage imageNamed:#"stopinverted"]
forState:UIControlStateNormal];
[self.playButton setImage:[UIImage imageNamed:#"playinverted"]
forState:UIControlStateNormal];
to
//Buttons
[self.stopButton setImage:[UIImage imageNamed:#"stopinverted"]
forState:UIControlStateDisabled];
[self.playButton setImage:[UIImage imageNamed:#"playinverted"]
forState:UIControlStateDisabled];
In short you have to set UIButton UIImages in disabled state.
For more reference have a look this questions answer Xcode: set image on button only for default state, not also selected
If you set the UIImages according to the state of the UIButton then in the function in which you want to invert the UIImage, you have to only need to handle the state of the UIButton and it will automatically change the image.
Thanks for the help. Here is the answer I came up with, inspired by A-Live's answer.
First I had to set the button as enabled, then set the image, and then disable the button again all within the same method. This has fixed my problem.
[self.playButton setEnabled:YES];
[self.playButton setImage:[UIImage imageNamed:#"playinverted"]forState:UIControlStateNormal];
[self.playButton setEnabled:NO];
try using :
theButton.adjustsImageWhenDisabled = false
this will use your custom image instead of the default dimming.

iOS - Create a Photo Viewer like Photo app

i'm trying to create a photo viewer like the Apple Photos app in iOS.
The layout is ok, but it receives memory warning and then crashes. Why? This happens even i load 7/8 images from the app documents folder. Have i to manage the memory with specific system? I use ARC with iOS 5.
EDIT :
The code :
for (int i=0; i<[dataSource count]; i++) {
UIButton *button=[UIButton buttonWithType:UIButtonTypeCustom];
[button setImage:[dataSource objectAtIndex:i] forState:UIControlStateNormal];
[[button titleLabel] setText:[NSString stringWithFormat:#"%i",i+1]];
[button addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
[[button layer] setBorderWidth:1];
[[button layer] setBorderColor:[UIColor darkGrayColor].CGColor];
if (i==0) {
[button setFrame:CGRectMake(x, y, width, height)];
} else {
if (i%5==0) {
nRow++;
x=18;
[button setFrame:CGRectMake(x, (y*nRow), width, height)];
} else {
[button setFrame:CGRectMake(x+space+width, (y*nRow), width, height)];
x=button.frame.origin.x;
}
}
[[self view] addSubview:button];
}
The main part of this code is the first 6 lines, after is all x and y.
dataSource is an NSArray declared as property (nonatomic, strong). It contains UIImage objects.
You should be lazily loading your images in conjunction with reusing your buttons to account for the possibility of a large number of images.
To implement:
Keep the paths to the image files in your data array instead of the UIImage objects. Get the image from the path using imageWithContentsOfFile: when you need it.
Load the first z buttons into the scroll view where z is the number that appear on the screen at a time plus one row's worth.
Set the UIViewController that we are currently in as the scrollview's delegate, and respond to changes in offset by repositioning buttons and setting appropriate images and targets.
Also, if 7/8 images is crashing your app, it sounds like you're dealing with some pretty large image files. Try to provide thumbnail-sized versions of the content within the documents directory(whatever the size of your buttons are EXACTLY), or if images are dynamic, see this post for a how-to.
If you are probably using ImageNamed, this article helped me alot:
http://www.alexcurylo.com/blog/2009/01/13/imagenamed-is-evil/
Mainly
DO NOT USE [UIImage imageNamed] for any significant amount of images. It is EVIL. It WILL bring down your application and/or Springboard, even when your application is putting along using just barely a nibble of memory on its own.
and
it is better to implement your own cache
and here's the proposed cached image example:
- (UIImage*)thumbnailImage:(NSString*)fileName
{
UIImage *thumbnail = [thumbnailCache objectForKey:fileName];
if (nil == thumbnail)
{
NSString *thumbnailFile = [NSString stringWithFormat:#"%#/thumbnails/%#.jpg", [[NSBundle mainBundle] resourcePath], fileName];
thumbnail = [UIImage imageWithContentsOfFile:thumbnailFile];
[thumbnailCache setObject:thumbnail forKey:fileName];
}
return thumbnail;
}

Resources