How can I create a button with a background color

2019-02-09 06:46发布

问题:

All I want to do is add a background color to a button for all states. But I want to maintain the automatic focus shadow that you get "for free" when using a system button in the tvOS storyboard. So far I haven't been able to find a combination that allows this.

Alternatively, I would also be interested in a way to programmatically add the shadow when the button is focused, but short of subclassing the button (which I haven't yet tried), I don't know how to do that either.

回答1:

Override didUpdateFocusInContext method and check if next focus view is button, if yes then customize its UI, and to set it back to orignal state check context.previousFocusedView was that button, something like below

- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
{
    if (context.nextFocusedView == _button)
    {
        // set background color
    }
    else if (context.previousFocusedView == _button)
    {
        // set background color to background
    }
}


回答2:

You can add a shadow for your custom button like this:

- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
{
    context.nextFocusedView.layer.shadowOffset = CGSizeMake(0, 10);
    context.nextFocusedView.layer.shadowOpacity = 0.6;
    context.nextFocusedView.layer.shadowRadius = 15;
    context.nextFocusedView.layer.shadowColor = [UIColor blackColor].CGColor;
    context.previouslyFocusedView.layer.shadowOpacity = 0;
}


回答3:

Wasn't happy with a simple colour change, so I made a custom button subclass to look more like the default animation you get with system buttons -

class CustomButton: UIButton
{
    private var initialBackgroundColour: UIColor!


    required init?(coder aDecoder: NSCoder)
    {
        super.init(coder: aDecoder)

        initialBackgroundColour = backgroundColor
    }

    override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator)
    {
        coordinator.addCoordinatedAnimations(
        {
            if self.focused
            {
                self.backgroundColor = UIColor.whiteColor()

                UIView.animateWithDuration(0.2, animations:
                {
                    self.transform = CGAffineTransformMakeScale(1.1, 1.1)
                },
                completion: 
                {
                    finished in

                    UIView.animateWithDuration(0.2, animations:
                    {
                        self.transform = CGAffineTransformIdentity
                    },
                    completion: nil)
                })
            }
            else
            {
                self.backgroundColor = self.initialBackgroundColour
            }
        },
        completion: nil)
    }
}

Nothing too complicated, but gets the job done



回答4:

The Ultimate Solution with inspiration from SomaMan. Just subclass all your custom buttons and you Get this:

includes: on tap animation and release and drag away.

//
//  CustomFocusButton.swift
//

import UIKit

class CustomFocusButton: UIButton {


let focusedScaleFactor : CGFloat = 1.2
let focusedShadowRadius : CGFloat = 10
let focusedShadowOpacity : Float = 0.25
let shadowColor = UIColor.blackColor().CGColor
let shadowOffSetFocused = CGSizeMake(0, 27)
let animationDuration = 0.2

override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator)
{
    coordinator.addCoordinatedAnimations({
            if self.focused{
                UIView.animateWithDuration(self.animationDuration, animations:{ [weak self] () -> Void in

                        guard let weakSelf = self else {return}
                        weakSelf.transform = CGAffineTransformMakeScale(weakSelf.focusedScaleFactor, weakSelf.focusedScaleFactor)
                        weakSelf.clipsToBounds = false
                        weakSelf.layer.shadowOpacity = weakSelf.focusedShadowOpacity
                        weakSelf.layer.shadowRadius = weakSelf.focusedShadowRadius
                        weakSelf.layer.shadowColor = weakSelf.shadowColor
                        weakSelf.layer.shadowOffset = weakSelf.shadowOffSetFocused

                    },completion:{ [weak self] finished in

                        guard let weakSelf = self else {return}
                        if !finished{
                            weakSelf.transform = CGAffineTransformMakeScale(weakSelf.focusedScaleFactor, weakSelf.focusedScaleFactor)
                            weakSelf.clipsToBounds = false
                            weakSelf.layer.shadowOpacity = weakSelf.focusedShadowOpacity
                            weakSelf.layer.shadowRadius = weakSelf.focusedShadowRadius
                            weakSelf.layer.shadowColor = weakSelf.shadowColor
                            weakSelf.layer.shadowOffset = weakSelf.shadowOffSetFocused
                        }

                    })
            } else {
                UIView.animateWithDuration(self.animationDuration, animations:{  [weak self] () -> Void in
                    guard let weakSelf = self else {return}

                    weakSelf.clipsToBounds = true
                    weakSelf.transform = CGAffineTransformIdentity

                    }, completion: {[weak self] finished in
                        guard let weakSelf = self else {return}
                        if !finished{
                            weakSelf.clipsToBounds = true
                            weakSelf.transform = CGAffineTransformIdentity
                        }
                })
            }
        }, completion: nil)
}

override func pressesBegan(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {
    UIView.animateWithDuration(animationDuration, animations: { [weak self] () -> Void in

        guard let weakSelf = self else {return}
        weakSelf.transform = CGAffineTransformIdentity
        weakSelf.layer.shadowOffset = CGSizeMake(0, 10);

    })
}

override func pressesCancelled(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {

    if focused{
        UIView.animateWithDuration(animationDuration, animations: { [weak self] () -> Void in
            guard let weakSelf = self else {return}
            weakSelf.transform = CGAffineTransformMakeScale(weakSelf.focusedScaleFactor, weakSelf.focusedScaleFactor)
            weakSelf.layer.shadowOffset = weakSelf.shadowOffSetFocused
        })
    }

}

override func pressesEnded(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {

    if focused{
        UIView.animateWithDuration(animationDuration, animations: {[weak self] () -> Void in

            guard let weakSelf = self else {return}
            weakSelf.transform = CGAffineTransformMakeScale(weakSelf.focusedScaleFactor, weakSelf.focusedScaleFactor)
            weakSelf.layer.shadowOffset = weakSelf.shadowOffSetFocused
        })
    }
}
}


回答5:

I've found something better:

-(void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator {
    [coordinator addCoordinatedAnimations:^{
        if (self.focused) {
            self.backgroundColor = [UIColor whiteColor];
        }
        else {
            self.backgroundColor = [UIColor clearColor];
        }
    } completion:nil];
}


回答6:

You can use the UIButton method setBackgroundImage(image: UIImage?, forState state: UIControlState) and pass through an image that is a flat color and the state .Normal.

This image can easily be created programatically from a UIColor and a size of 1x1:

func getImageWithColor(color: UIColor, size: CGSize) -> UIImage {
    let rect = CGRectMake(0, 0, size.width, size.height)
    UIGraphicsBeginImageContextWithOptions(size, false, 0)
    color.setFill()
    UIRectFill(rect)
    let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image
}


回答7:

You can set the background image in storyboard to an image that contains the color you would like



回答8:

Swift 4 /tvOS11 and better:

Set the ButtonType in the Interface Builder button properties to "Plain".

Add this private extension to your class:

private extension UIImage {

    static func imageWithColor(color: UIColor, size: CGSize) -> UIImage? {
        let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
        UIGraphicsBeginImageContextWithOptions(size, false, 0)
        color.setFill()
        UIRectFill(rect)
        let image: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return image
    }
}

Then in your connected IBOutlet set the background image for the focused state of the button:

@IBOutlet weak var myButton: UIButton! {
    didSet {
        let backgroundImageSelected = UIImage.imageWithColor(color: .red, size: myButton.bounds.size)
        myButton.setBackgroundImage(backgroundImageSelected, for: .focused)
    }
}


标签: uibutton tvos