Custom button inherited from UIControl, which behavior like UIButton.
-
Minimum support for iOS 11
-
Flexible configuration
-
Configuration reusable
-
RTL layout adaptation
// 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
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
}
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)
}
}
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.