Dan Ray
Dan Ray

Reputation: 21893

Highlighting a custom UIButton

The app I'm building has LOTS of custom UIButtons laying over top of fairly precisely laid out images. Buttonish, controllish images and labels and what have you, but with a clear custom-style UIButton sitting over top of it to handle the tap.

My client yesterday says, "I want that to highlight when you tap it". Never mind that it immediately pushes on a new uinavigationcontroller view... it didn't blink, and so he's confused. Oy.

Here's what I've done to address it. I don't like it, but it's what I've done:

I subclassed UIButton (naming it FlashingUIButton).

For some reason I couldn't just configure it with a background image on control mode highlighted. It never seemed to hit the state "highlighted". Don't know why that is.

So instead I wrote:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self setBackgroundImage:[UIImage imageNamed:@"grey_screen"] forState:UIControlStateNormal];
    [self performSelector:@selector(resetImage) withObject:nil afterDelay:0.2];
    [super touchesBegan:touches withEvent:event];
}

-(void)resetImage
{
    [self setBackgroundImage:nil forState:UIControlStateNormal];
}

This happily lays my grey_screen.png (a 30% opaque black box) over the button when it's tapped and replaces it with happy emptyness .2 of a second later.

This is fine, but it means I have to go through all my many nibs and change all my buttons from UIButtons to FlashingUIButtons. Which isn't the end of the world, but I'd really hoped to address this as a UIButton category, and hit all birds with one stone.

Any suggestions for a better approach than this one?

Upvotes: 4

Views: 8312

Answers (3)

philippe
philippe

Reputation: 1957

I had the same problem after subclassing a UIButton (Swift 2). The subclassed "drawRect" function is NOT called when the user touches the UIButton, (even if the checkmark "Highlighted Adjusts Image" is checked in the storyboard). This may be due to the fact that it is a custom UIButton.

Anyway, it is simple to handle, by overriding "touchesBegan" and "touchesEnded" events. More could be done to handle all states of the button (like disabled). But here is the code.

Swift 2:

import Foundation
import UIKit

@IBDesignable

class ppButton: UIButton {
    override func drawRect(rect: CGRect) {
        if self.state != UIControlState.Highlighted  {
            //SKit is another class containing a function to draw the custom button
            SKit.drawPpButton(0, width: rect.width-2, height: rect.height-2)
        } else {
            //SKit is another class containing a function to draw the custom button
            SKit.drawPpButton(0, width: rect.width-20, height: rect.height-20)
        }
    }
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        super.touchesBegan(touches, withEvent: event)
        self.setNeedsDisplay()
    }
    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        super.touchesEnded(touches, withEvent: event)
        self.setNeedsDisplay()
    }
}

Upvotes: 0

Omar
Omar

Reputation: 1574

You can intercept the touches events with a gesture recognizer, and then programmatically add the recognizer to all your uibuttons. For instance:

//
//  HighlighterGestureRecognizer.h
//  Copyright 2011 PathwaySP. All rights reserved.
//

#import <Foundation/Foundation.h>


@interface HighlightGestureRecognizer : UIGestureRecognizer {
    id *beganButton;
}

@property(nonatomic, assign) id *beganButton;

@end

and the implementation:

//
//  HighlightGestureRecognizer.m
//  Copyright 2011 PathwaySP. All rights reserved.
//

#import "HighlightGestureRecognizer.h"


@implementation HighlightGestureRecognizer

@synthesize beganButton;

-(id) init{
    if (self = [super init])
    {
        self.cancelsTouchesInView = NO;
    }
    return self;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    self.beganButton = [[[event allTouches] anyObject] view];
    if ([beganButton isKindOfClass: [UIButton class]]) {
        [beganButton setBackgroundImage:[UIImage imageNamed:@"grey_screen"] forState:UIControlStateNormal];
        [self performSelector:@selector(resetImage) withObject:nil afterDelay:0.2];

    }
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{

}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
}

- (void)reset
{
}

- (void)ignoreTouch:(UITouch *)touch forEvent:(UIEvent *)event
{
}

- (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer *)preventingGestureRecognizer
{
    return NO;
}

- (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer
{
    return NO;
}

- (void)resetImage
{
    [beganButton setBackgroundImage: nil forState:UIControlStateNormal];
}

@end

Hmm. I didn't test this code, so if it doesn't work, don't sue me, just comment and we'll work it out. I just wrote it real quick for reference. Even if it doesn't work, the logic should still be sound.

edit:

Forgot to add, the way you'd add the gesture recognizer to your button would be like so:

HighlighterGestureRecognizer * tapHighlighter = [[HighlighterGestureRecognizer alloc] init];

[myButton addGestureRecognizer:tapHighlighter];
[tapHighlighter release];

So basically you're declaring it, initializing it, and then adding it. After that, you'll want to release it, since the addGestureRecognizer retains it.

Upvotes: 4

Nathan Eror
Nathan Eror

Reputation: 12438

Do you have adjustsImageWhenHighlighted = YES set on your buttons? The default is YES, but maybe you changed it in the xib. It's the "Highlighted Adjusts Image" checkbox in the attributes inspector:

IB Highlight Button

Upvotes: 2

Related Questions