FUIButton

@MainActor
open class FUIButton : UIButton, FUIButtonClosureHandling, FUIStateSelectable, FUIBackgroundSchemeSupporting, PrepareForReuse

Subclass of UIButton, which provides for rounded edge and rounded fill styles. It also provides for per-state tint colors, persistent selection, and a closure-styled tap handler.

The FUIButton implements the Fiori Design Language in default configuration.

# Usage

## Initialization

Init button with expected style (for default, use .tertiary).

To use FUIButton style variants in Interface Builder, the convenience subclasses FUIRoundedButton (.tertiary) and FUIRoundedFillButton (.primary) are also available.

 // init with expected style.

 let normalButton = FUIButton() // equivalent to FUIButton(style: .tertiary)
 let roundedOutlineButton = FUIButton(style: .secondary)       // equivalent to `FUIRoundedButton()`
 let roundedFilledButton = FUIButton(style: .primary)  // equivalent to `FUIRoundedFillButton()`

## Setting button style and semantic attribute There are three button styles supported: primary, secondary, and tertiary. You can also configure the semantic attribute (normal, tint, negative) for the button.

 button.style = .primary
 button.semantic = .negative

## Setting title(s) UIButton provides two techniques for setting its title:

  1. setTitle(_:, for:)
  2. titleLabel.text

The behavior of the (2) is inconsistent, and discouraged by Apple. As a result, titleLabel.text should be avoided in favor of technique setTitle(_:_:). The FUIButton will correct the behavior of titleLabel.text, if setTitle(_:_:) is not used. However, if the developer at any point invokes setTitle(_:, for:) directly, FUIButton will cease to correct for titleLabel.text, and will defer to the developer to use the API correctly.

 button.setTitle("Follow", for: .normal)
 button.setTitle("Unfollow", for: .selected)

## Specify Selection Behavior The default behavior of UIButton is to toggle between .normal and .highlighted UIControlState, on user touches started and touches ended. The .selected state is never used.

FUIButton supports toggling between .normal and .selected UIControlState, by setting the isPersistentSelection flag to true. This is particularly useful, if using the button to toggle a property. The developer may use the isSelected: Bool API to toggle cause this transition.

 button.isPersistentSelection = true    // causes button to toggle between `.normal` and `.selected` states on user touch

When isPersistentSelection == true, the button may transition briefly through the .highlighted state, between .selected and .normal on user touches. Configure settings for [.selected, .highlighted], if implementing custom behavior here.

## Color Configuration In any style, you may choose to override the background color for state, which is useful for applying an inverted style.

 button.setBackgroundColor(UIColor.preferredFioriColor(forStyle: .tintColorLight), for: .highlighted)

In any style, you may choose to override the title color for state, which is useful for applying an inverted style.

 button.setTitleColor(UIColor.preferredFioriColor(forStyle: .header), for: .normal)

You can set whether the border color is applied based onUIControlState

 button.setIsApplyingBorderColor(true, for: .normal)

The FUIButton also implements the FUIBackgroundSchemeSupporting protocol, which enables developers to inform the control whether it is being presented against a ‘light’ background, or ‘dark’ background. The default style of the button will adapt to the background color scheme; defaults to .light.

## Size Calculation The FUIButton calculates an intrinsicContentSize from its imageView and titleLabel, its various insets, and its directionalLayoutMargins.

To manage the width of the text in the titleLabel, use the regular UILabel API to configure the wrapping behavior.

 button.titleLabel?.preferredMaxLayoutWidth = 200
 button.titleLabel?.lineBreakMode = .byWordWrapping

As an additional configuration option, the FUIButton provides a flag isPreservingPreferredMaxLayoutWidth, which uses the preferredMaxLayoutWidth as a minimum width for the titleLabel when calculating intrinsicContentSize. This is useful, when implementing a fixed-width button with variable texts, or toggling variable-width texts on state.

Note

This will not affect the titleLabel text wrapping behavior.

 button.isPreservingPreferredMaxLayoutWidth = true

## Selection handler: Optional closure-based substitute for addTarget(_:_:_:) method for handling user taps. Passes self as input parameter. Is compatible with addTarget(_:_:_:) selector registration.

If used in combination with the target-action selector registration, handlers will be invoked in the following order:

  1. didSelectHandler
  2. target-action selector

## custom configuration configurationUpdateHandler can be used to do custom configuration:

 button.configurationUpdateHandler = { [unowned self] button in
     var configuration = UIButton.Configuration.plain()
     if let currentConfig = button.configuration {
         configuration = currentConfig
     }
     var container = AttributeContainer()
     container.font = UIFont.preferredFioriFont(forTextStyle: .headline, weight: .semibold)
 //  named color(like .red, .blue, etc) not working for foregroundColor, backgroundColor on AttributeContainer, you can use fioriColor, hexStringColor, RGB Color, or System Color instead.
     container.foregroundColor = .preferredFioriColor(forStyle: .tintColor)
     configuration.attributedTitle = AttributedString("FUIButton", attributes: container)
     configuration.imagePadding = 10
     configuration.showsActivityIndicator = true
     configuration.activityIndicatorColorTransformer = UIConfigurationColorTransformer({ _ in
         return .red
     })
     configuration.image = FUIIconLibrary.actions.actionSettingsFill
     configuration.imageColorTransformer = UIConfigurationColorTransformer({ [unowned self] color in
         return button.titleColor(for: button.state) ?? .white
     })
     configuration.imagePlacement = .trailing
     // set customized contentInsets here
     configuration.contentInsets = NSDirectionalEdgeInsets(top: 20, leading: 40, bottom: 20, trailing: 40)

//   configuration must be reset to be effective.
     button.configuration = configuration
 }

When configuration is assigned for the first time or assigned to nil, the following settings will be reset to SDK’s default values: titleLabel?.font = UIFont.preferredFioriFont(forTextStyle: .body, weight: .semibold) titleLabel?.lineBreakMode = .byWordWrapping titleLabel?.textAlignment = .center titleLabel?.numberOfLines = 0 imageView?.contentMode = .scaleAspectFit

When configuration is assigned to nil, application should call setNeedsUpdateConfiguration. Otherwise, the button layout will not be refreshed.

When using UIButtonConfiguration, the properties (include contentEdgeInsets, titleEdgeInsets, imageEdgeInsets, adjustsImageWhenHighlighted and adjustsImageWhenDisabled) are ignored, please use configuration.contentInsets to do contentInsets customization.

## Theming

Support Button class paths:

Button {} fdlFUIButton {}

Supported Button attributes:

tint-color { -selected | -highlighted | -selected-highlighted | -selected-disabled | -disabled } (Color) background-color { -normal | -selected | -highlighted | -selected-highlighted | -selected-disabled | -disabled } { -primary| -secondary| -tertiary } (Color) background-image { -selected | -highlighted | -selected-highlighted | -selected-disabled | -disabled } (Image) content-insets (Box) image-insets (Box) title-insets (Box) shadow-color (Color) shadow-offset (Offset) shadow-opacity (Number) shadow-radius (Number)

Supported Text attributes:

font-color { -selected | -highlighted | -selected-highlighted | -selected-disabled | -disabled } (Color) font-name (FontName) font-style (UIFontTextStyle) font-size (Number) text-align (TextAlign) text-alpha (Number) text-auto-fit (Boolean) text-line-clamp (Integer) text-shadow-color { -selected | -highlighted | -selected-highlighted | -selected-disabled | -disabled } (Color) text-shadow-offset (Offset)

Supported ImageView attributes:

image { -highlighted | -selected | -selected-highlightecd | -selected-disabled | -disabled } (Image)

Remark

The tint-color { -* } attribute will override the font-color { -* } attribute on a state-by-state basis. E.g. tint-color-highlighted will override font-color-highlighted, but will not affect font-color.
  • Specialized init method, implementing the common ‘rounded’ button style. Applies default contentEdgeInsets = UIEdgeInsets(top: 7, left: 16, bottom: 7, right: 16)

    Declaration

    Swift

    @MainActor
    convenience public init(style: FUIButtonStyle)

    Parameters

    style

    { .primary, .secondary, .tertiary }

  • Workaround for the compile issue where empty initializer is not accessible in xcframework.

    Declaration

    Swift

    @MainActor
    required public init()
  • Update configuration when state change. SDK follows the following rules if application defines the two properties at the same time: titleLabel?.textAlignment will override configuration.titleAlignment titleLabel?.lineBreakMode will override configuration.titleLineBreakMode titleLabel?.font will override titleTextAttributes’s font titleColor(for:) will override titleTextAttributes’s foregroundColor titleLabel?.backgroundColor will override titleTextAttributes’s backgroundColor imagePosition will override configuration.imagePlacement imageTitleSpacing will override configuration.imagePadding title priority: attributedTitle(for:) > title(for:) > configuration.title image priority: image(for:) > configuration.image

    Declaration

    Swift

    @MainActor
    open override func updateConfiguration()
  • Sets background color for UIControlState for most button configurations.

    Declaration

    Swift

    @MainActor
    public func setBackgroundColor(_ color: UIColor, for state: UIControlState)

    Parameters

    color

    background color

    state

    control state (as determined by UIButton superclass)

  • Sets tint color for UIControlState provided. When style == { .none | .fuiRounded} , invokes setTitleColor(_:_:) for state, and if style is not .none, applies tint color to imageView. When style == .fuiRoundedFilled, invokes the setBackgroundColor(_:_:) for state.

    Declaration

    Swift

    @MainActor
    open func setTintColor(_ color: UIColor, for state: UIControlState)

    Parameters

    color

    tint color

    state

    control state (as determined by UIButton superclass)

  • Sets button style. It is tertiary by default.

    Declaration

    Swift

    @MainActor
    public var style: FUIButtonStyle { get set }
  • Sets button background color scheme. It is device by default.

    Declaration

    Swift

    @MainActor
    public var backgroundColorScheme: FUIBackgroundColorScheme { get set }
  • Sets button semantic attribute. It is normal by default.

    Declaration

    Swift

    @MainActor
    public var semantic: FUIButtonSemantic { get set }
  • Applies blur effect for disabled or secondary style.

    The default is true.

    Declaration

    Swift

    @MainActor
    public var appliesBlurEffect: Bool { get set }
  • Enable shadow for the button. It is false by default.

    Declaration

    Swift

    @MainActor
    public var isShadowEnabled: Bool { get set }
  • Sets whether the border color is appled based onUIControlState

    • value: Bool
    • state: control state (as determined by UIButton superclass)

    Declaration

    Swift

    @MainActor
    public func setIsApplyingBorderColor(_ value: Bool, for state: UIControlState)
  • SDK’s implementation extends the touch area by 3 points on top and bottom.

    Declaration

    Swift

    @MainActor
    open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView?
  • Optional closure handler, for responding to UIControlEvent.primaryActionTriggered.

    Declaration

    Swift

    @MainActor
    open var didSelectHandler: ((FUIButton) -> Void)?
  • Enables UIControlState.selected to be persisted after user tap. Requires that user re-tap button to de-select, or that developer invoke isSelected = false setter. Defaults to false.

    Note

    If set to true, button will skip UIControlState.highlighted on tap, and return UIControlState.selected, while isSelected == true.

    Declaration

    Swift

    @MainActor
    public var isPersistentSelection: Bool { get set }
  • Set spacing between the image and the title when both the image and the title are on the button. The default value is LayoutParams.titleImageSpacing

    Declaration

    Swift

    @MainActor
    public var imageTitleSpacing: CGFloat { get set }
  • Undocumented

    Declaration

    Swift

    @MainActor
    public var imagePosition: NSDirectionalRectEdge { get set }
  • Instructs the intrinsicContentSize calculation to use the button’s titleLabel?.preferredMaxLayoutWidth as the constant width of the titleLabel subview.

    Declaration

    Swift

    @MainActor
    public var isPreservingPreferredMaxLayoutWidth: Bool
  • A Boolean that indicates whether the button automatically updates title font when the device’s content size category changes. It is true by default

    Declaration

    Swift

    @MainActor
    open var adjustsContentSizeForAccessibilityContentSizeCategory: Bool { get set }
  • .processing and .success State have specified icons, call setImage(_ image:for) under the two states will don’t make any sense;

    Declaration

    Swift

    @MainActor
    override open func setImage(_ image: UIImage?, for state: UIControl.State)
  • The color of image, priority: imageColor > titleColor(for:) > styleTintColor > .white

    Declaration

    Swift

    @MainActor
    public var imageColor: UIColor { get set }
  • When loading State is .processing, add an activity indicator to button. When loading State is .success, add an success icon to button. When loading State is .unspecified, remove the button’s image. User actions are blocked during the loading & success states.

    Declaration

    Swift

    @MainActor
    public func setLoadingState(_ loadingState: FUIButtonLoadingState, for state: UIControl.State)
  • The icon for loading state .success, default is “fiori.sys.enter”.

    Declaration

    Swift

    @MainActor
    public var successIcon: UIImage { get set }