How can I create a button with a background color for tvOS while still showing focus?

前端 未结 8 973
Happy的楠姐
Happy的楠姐 2021-01-02 05:02

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

相关标签:
8条回答
  • 2021-01-02 05:14

    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

    0 讨论(0)
  • 2021-01-02 05:15

    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
            })
        }
    }
    }
    
    0 讨论(0)
  • 2021-01-02 05:19

    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
        }
    }
    
    0 讨论(0)
  • 2021-01-02 05:26

    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
    }
    
    0 讨论(0)
  • 2021-01-02 05:30

    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];
    }
    
    0 讨论(0)
  • 2021-01-02 05:30

    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)
        }
    }
    
    0 讨论(0)
提交回复
热议问题