Skip to content

Custom button inherited from UIControl, which behavior like UIButton.

License

Notifications You must be signed in to change notification settings

xueqooy/ConfigurationBasedButton

Repository files navigation

ConfigurationBasedButton

Custom button inherited from UIControl, which behavior like UIButton.

  • Minimum support for iOS 11

  • Flexible configuration

  • Configuration reusable

  • RTL layout adaptation

Title/Subtitle/Image/Activity Indicator

截屏2022-09-01 18 09 24 截屏2022-09-01 18 10 11 截屏2022-09-01 18 10 38 截屏2022-09-01 18 11 07

Image Placement

截屏2022-09-01 17 58 24 截屏2022-09-01 17 59 09 截屏2022-09-01 17 59 31 截屏2022-09-01 17 59 40

Background

截屏2022-09-01 18 14 55 截屏2022-09-01 18 17 27 截屏2022-09-01 18 20 20 截屏2022-09-01 18 22 43

Start

// Create base configuration.
var baseConfiguration = ButtonConfiguration()
baseConfiguration.title = "Title"
baseConfiguration.subtitle = "Subtitle"
baseConfiguration.image = UIImage(systemName: "house.circle.fill")
baseConfiguration.imagePadding = 10
baseConfiguration.contentInsets = .nondirectional(.init(top: 10, left: 40, bottom: 10, right: 40))

// Create plain-style configuration provider.
let configurationProvider = PlainButtonConfigurationProvider()

// Create button1 with baseConfiguration, configurationProvider and action for `touchUpInside`.
let button1 = ConfigurationBasedButton(baseConfiguration: baseConfiguration, configurationProvider: configurationProvider) { _ in
    print("Button1 has been tapped")
}

// Create button2 with button1's base configuration.
let button2 = ConfigurationBasedButton()
button2.baseConfiguration = button1.baseConfiguration

// Create button3 with current button1's effective configuration.
button1.isHighlighted = true
let button3 = ConfigurationBasedButton(baseConfiguration: button1.effectiveConfiguration)

// Update configuration.
// The UI will not be updated immediately, multiple requests may be coalesced into a single update at the appropriate time.
button1.baseConfiguration.title = "Update Title"
button1.baseConfiguration.image = nil
button1.baseConfiguration.background?.fillColor = UIColor.white
button1.baseConfiguration.background?.cornerStyle = .capsule

Configuration

public struct ButtonConfiguration {
    public enum ImagePlacement: Int, Equatable {
        case leading, trailing, top, left, bottom, right
    }
    
    public enum TitleAlignment: Int, Equatable {
        /// Align title & subtitle automatically based on ImagePlacement
        case automatic
        case leading, center, trailing, left, right
    }
    
    public var image: UIImage?
    
    public var title: String?
    public var titleFont: UIFont?
    public var titleColor: UIColor?
    public var attributedTitle: NSAttributedString?
    
    public var subtitle: String?
    public var subtitleFont: UIFont?
    public var subtitleColor: UIColor?
    public var attributedSubtitle: NSAttributedString?
    
    /// Shows an activity indicator in place of an image. Its placement is controlled by `imagePlacement` .
    public var showsActivityIndicator: Bool = false

    /// Defaults to Leading.
    public var imagePlacement: ButtonConfiguration.ImagePlacement = .leading
    /// The alignment to use for relative layout between title & subtitle.
    public var titleAlignment: ButtonConfiguration.TitleAlignment = .automatic
    
    /// Insets from the bounds of the button to create the content region.
    public var contentInsets: EdgeInsets = .directional(.zero)
    /// When a button has both image and text content, this value is the padding between the image and the text.
    public var imagePadding: CGFloat = 0
    /// When a button has both a title & subtitle, this value is the padding between those titles.
    public var titlePadding: CGFloat = 0
        
    /// A BackgroundConfiguration describing the button's background.
    public var background: BackgroundConfiguration? = BackgroundConfiguration()
    
    /// The base color to use for background elements.
    public var foregroundColor: UIColor?
}
public enum CornerStyle: Equatable {
    case fixed(CGFloat), capsule
}

public struct BackgroundConfiguration {
    /// Configures the color of the background
    public var fillColor: UIColor?
    /// Configures the color of the stroke. A nil value uses the view's tint color.
    public var strokeColor: UIColor?
    /// The width of the stroke. Default is 0.
    public var strokeWidth: CGFloat = 0
    /// Outset (or inset, if negative) for the stroke. Default is 0.
    /// The corner radius of the stroke is adjusted for any outset to remain concentric with the background.
    public var strokeOutset: CGFloat = 0
    /// The corner style for the background and stroke. This is also applied to the custom view. Default is .fixed(0).
    public var cornerStyle: CornerStyle?
    /// The visual effect to apply to the background. Default is nil.
    public var visualEffect: UIVisualEffect?
    /// A custom view for the background.
    public var customView: UIView?
    /// The image to use. Default is nil.
    public var image: UIImage?
    /// The content mode to use when rendering the image. Default is UIViewContentModeScaleToFill.
    public var imageContentMode: UIView.ContentMode = .scaleToFill
}

Custom Configuration Provider

class MyStyleButtonConfigurationProvider: PlainButtonConfigurationProvider {
    enum Style {
    case primaryOutline
    case primaryFilled
    }
    
    private lazy var primaryOutlineConfiguration: ButtonConfiguration = {
        var config = ButtonConfiguration()
        config.contentInsets = .nondirectional(UIEdgeInsets(top: 10, left: 40, bottom: 10, right: 40))
        config.foregroundColor = UIColor(named: "purple")
        config.imagePadding = 10
        config.background?.fillColor = UIColor.clear
        config.background?.strokeColor = UIColor(named: "purple")
        config.background?.strokeWidth = 1
        config.background?.cornerStyle = .capsule
        return config
    }()
    
    private lazy var primaryFilledConfiguration: ButtonConfiguration = {
        var config = ButtonConfiguration()
        config.contentInsets = .nondirectional(UIEdgeInsets(top: 10, left: 40, bottom: 10, right: 40))
        config.foregroundColor = UIColor.white
        config.imagePadding = 10
        config.background?.fillColor = UIColor(named: "purple")
        config.background?.cornerStyle = .capsule
        return config
    }()
    
  
    public let style: Style
    
    init(style: Style) {
        self.style = style
    }
    
    override func update(_ configuration: inout ButtonConfiguration, for button: ConfigurationBasedButton) {
        switch style {
        case .primaryOutline:
            configuration.contentInsets = primaryOutlineConfiguration.contentInsets
            configuration.foregroundColor = primaryOutlineConfiguration.foregroundColor
            configuration.imagePadding = primaryOutlineConfiguration.imagePadding
            configuration.background?.fillColor = primaryOutlineConfiguration.background?.fillColor
            configuration.background?.strokeColor = primaryOutlineConfiguration.background?.strokeColor
            configuration.background?.strokeWidth = primaryOutlineConfiguration.background?.strokeWidth ?? 1
            configuration.background?.cornerStyle = primaryOutlineConfiguration.background?.cornerStyle
        case .primaryFilled:
            configuration.contentInsets = primaryFilledConfiguration.contentInsets
            configuration.foregroundColor = primaryFilledConfiguration.foregroundColor
            configuration.imagePadding = primaryFilledConfiguration.imagePadding
            configuration.background?.fillColor = primaryFilledConfiguration.background?.fillColor
            configuration.background?.strokeColor = primaryFilledConfiguration.background?.strokeColor
            configuration.background?.strokeWidth = primaryFilledConfiguration.background?.strokeWidth ?? 1
            configuration.background?.cornerStyle = primaryFilledConfiguration.background?.cornerStyle
        }
        super.update(&configuration, for: button)
    }
}

Note

This repository does not provide any installation methods like CocoaPods or Swift Package Manager. You are free to copy the codebase and make any custom modifications as per your requirements.

About

Custom button inherited from UIControl, which behavior like UIButton.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •