// // Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // import SignalServiceKit import UIKit open class RoundMediaButton: UIButton { public enum BackgroundStyle { case none case solid(UIColor) case blur(UIBlurEffect.Style) public static let blur: BackgroundStyle = .blur(.dark) public static let blurLight: BackgroundStyle = .blur(.light) } let backgroundStyle: BackgroundStyle private var backgroundContainerView: UIView? private let backgroundView: UIView? private var backgroundDimmerView: UIView? public static let defaultBackgroundColor = UIColor.ows_gray80 static let visibleButtonSize: CGFloat = 42 private static let defaultInset: CGFloat = UIDevice.current.isNarrowerThanIPhone6 ? 4 : 8 private static let defaultContentInset: CGFloat = UIDevice.current.isNarrowerThanIPhone6 ? 11 : 15 public convenience init(image: UIImage?, backgroundStyle: BackgroundStyle) { self.init(image: image, backgroundStyle: backgroundStyle, customView: nil) } public convenience init(customView: UIView, backgroundStyle: BackgroundStyle) { self.init(image: nil, backgroundStyle: backgroundStyle, customView: customView) } public init(image: UIImage?, backgroundStyle: BackgroundStyle, customView: UIView?) { self.backgroundStyle = backgroundStyle self.backgroundView = { switch backgroundStyle { case .none: return nil case .solid: return UIView() case .blur(let style): return UIVisualEffectView(effect: UIBlurEffect(style: style)) } }() super.init(frame: CGRect(origin: .zero, size: .square(Self.visibleButtonSize + 2 * Self.defaultInset))) ows_contentEdgeInsets = UIEdgeInsets(margin: Self.defaultContentInset) layoutMargins = UIEdgeInsets(margin: Self.defaultInset) tintColor = Theme.darkThemePrimaryColor insetsLayoutMarginsFromSafeArea = false setCompressionResistanceHigh() if backgroundView != nil || customView != nil { let backgroundContainerView = PillView() backgroundContainerView.isUserInteractionEnabled = false addSubview(backgroundContainerView) backgroundContainerView.autoPinEdgesToSuperviewMargins() self.backgroundContainerView = backgroundContainerView if let backgroundView { backgroundView.isUserInteractionEnabled = false backgroundContainerView.addSubview(backgroundView) backgroundView.autoPinEdgesToSuperviewEdges() } if let customView { backgroundContainerView.addSubview(customView) customView.autoCenterInSuperview() } let backgroundDimmerView = UIView(frame: backgroundContainerView.bounds) // Match color of the highlighted white button image. backgroundDimmerView.backgroundColor = UIColor(white: 0, alpha: 0.467) backgroundDimmerView.alpha = 0 backgroundContainerView.addSubview(backgroundDimmerView) backgroundDimmerView.autoPinEdgesToSuperviewEdges() self.backgroundDimmerView = backgroundDimmerView } setImage(image, for: .normal) if case .solid(let color) = backgroundStyle { setBackgroundColor(color, for: .normal) } } @available(*, unavailable, message: "Use init(image:backgroundStyle:) instead") override init(frame: CGRect) { fatalError("init(frame:) has not been implemented") } @available(*, unavailable, message: "Use init(image:backgroundStyle:) instead") public required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override public func layoutSubviews() { super.layoutSubviews() if let backgroundContainerView { sendSubviewToBack(backgroundContainerView) } } private var backgroundColors: [UIControl.State.RawValue: UIColor] = [:] public func setBackgroundColor(_ color: UIColor?, for state: UIControl.State) { if let color { backgroundColors[state.rawValue] = color } else { backgroundColors.removeValue(forKey: state.rawValue) } if self.state == state { updateBackgroundColor() } } public func backgroundColor(for state: UIControl.State) -> UIColor? { return backgroundColors[state.rawValue] } private func updateBackgroundColor() { // Use default dimming if separate background color for 'highlighted' isn't specified. if backgroundColor(for: .highlighted) == nil { backgroundDimmerView?.alpha = isHighlighted ? 1 : 0 } switch backgroundStyle { case .solid: backgroundView?.backgroundColor = backgroundColor(for: state) ?? backgroundColor(for: .normal) default: break } } override public var isHighlighted: Bool { didSet { updateBackgroundColor() } } override public var isSelected: Bool { didSet { updateBackgroundColor() } } override public var isEnabled: Bool { didSet { updateBackgroundColor() } } }