Search code examples
iosuiviewuibuttonhighlight

How to highlight a UIView like Apple does


When the user touches a UIButton it gets grayed-out a bit. What apple does to get such an effect? I need the same effect on my custom UIButton when it is highlighted.

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    AppDelegate* appDelegate=(AppDelegate*)[UIApplication sharedApplication].delegate;
    if ([keyPath isEqualToString:@"highlighted"]){
        UIButton *button = object;
        if (button.isHighlighted) {
            self.backgroundColor=[UIColor colorWithRed:36.0/255.0 green:153.0/255.0 blue:116.0/255.0 alpha:1];
        }else{
            self.backgroundColor=appDelegate.currentAppColor;
        }
    }
}

Im using this code, but changing background color wont affect any subviews. I need them to be grayed out too.


Solution

  • I don't think there is an easy or definitive answer to that.

    DISCLAIMER: All the code in this answer was written from the top of my head, so please excuse mistakes.

    I would suggest doing something like this:

    Proposal 1: decrease opacity for all the view's subviews, which would not affect color...

    Objective C:

    -(void)buttonTouched{
        for(UIView *subview in self.subviews){ 
            subview.alpha = 0.5;
        }
    }
    

    Swift:

    func buttonTouched() {
        for subview in subviews {
            subview.alpha = 0.5
        }
    }
    

    Proposal 2 (not tested): try to cover all subviews by doing it kind of manually (drawback: you would have to set the colors back manually as well, which would be insane if it is not monochromatic):

    Objective C:

    -(void)buttonTouched{
        for(UIView *subview in self.subviews){
    
            if([subview respondsToSelector:@selector(setBackgroundColor:)]){ 
                //for generic views - changes UILabel's backgroundColor too, though
                subview.backgroundColor = [UIColor grayColor];
            }
    
            if([subview respondsToSelector:@selector(setTextColor:)]){ 
                //reverse effect of upper if statement(if needed)
                subview.backgroundColor = [UIColor clearColor];
                subview.textColor = [UIColor grayColor];
            }
        }
    }
    

    Swift:

    func buttonTouched() {
        for subview in subviews {
            if subview.responds(to: #selector(setter: AVMutableVideoCompositionInstruction.backgroundColor)) {
                //for generic views - changes UILabel's backgroundColor too, though
                subview.backgroundColor = UIColor.gray
            }
    
            if subview.responds(to: #selector(setter: UILabel.textColor)) {
                //reverse effect of upper if statement(if needed)
                subview.backgroundColor = UIColor.clear
                subview.textColor = UIColor.gray
            }
        }
    }
    

    This is really bad design and will probably cause a lot of problems, but it could potentially help you. The example above needs a lot of improvement, I just want to give you a hint. You would have to revert the colors in the touchesEnded: method. Maybe it helps you...

    Proposal 3: would be to overlay your whole view with a transparent view (if it is rectangular)

    Objective C:

    -(void)buttonTouched{
        UIView *overlay = [[UIView alloc]initWithFrame:self.bounds];
        overlay.backgroundColor = [[UIColor grayColor]colorWithAlphaComponent:0.5];
        [self addSubview: overlay];
    }
    

    Swift:

    func buttonTouched() {
        let overlay = UIView(frame: bounds)
        overlay.backgroundColor = UIColor.gray.withAlphaComponent(0.5)
        addSubview(overlay)
    }
    

    You would have to remove it when the user releases the finger of course.

    Proposal 4 : Another option would be to create a bitmap of your current view and modify the pixels to your liking, which is quite a bit of work, so I will omit code here.

    Apple probably does a mix of the last two. When a button is touched it will go over the pixels and overlay a gray pixel over every pixel that has an alpha component.

    I hope I could help.