First published on the official account

UIButton provided by UIKit is already very useful in ordinary scenarios, but it also has a disadvantage, that is, the customization ability is not strong, developers often need to subclass and customize their own style to meet the needs of UI.

The UI has the following requirements for a Button:

  1. Picture on the left, text on the right
  2. Left text, right picture
  3. Picture above, text below
  4. Text on top, picture on bottom

Not only that, but it also requires a certain amount of space between the picture and the text.

The UIButton of the system can only support the first scene by default, and other cases need to be customized. However, customization based on UIButton also has some troubles to deal with, for example, it can adapt to the size under the automatic layout mechanism, and only the display style is different for the scenes listed above. UIButton would be much easier if it had an interface to set the style directly.

Based on the above analysis, I decided to re-implement a Button based on UIControl, basically using the same interface as UIButton, and support custom layout style, just need to set a simple parameter.

There are four permutations of pictures and text, two horizontal and two vertical, which are defined by an enumeration type as follows:

public enum SFButtonDirection: Int {
    case row
    case rowReverse
    case column
    case columnReverse
}
Copy the code

The horizontal arrangement is called row, and the vertical arrangement is called column, which correspond to the four scenarios mentioned above:

  1. Left image, right text (row)
  2. Text on the left, image on the right (rowReverse)
  3. Top picture, bottom column
  4. Text above, picture below (columnReverse)

You can also set the space between the image and the text, and add another property: space.

Sometimes the UI requires that the size of the Button be determined by the content, and that there should be a certain amount of space between the top, bottom, left and right. For this scenario, you only need to add one more property: contentInset.

With these three key attributes, you can do it for most application scenarios, and it’s pretty simple to use: Create a Button

func makeButton(a) -> SFButton {
    let btnNormalBGImage = UIImage(named: "button_normal")! .resizableImage(withCapInsets:UIEdgeInsets(top: 15.left: 15, bottom: 15.right: 15))
    let btnHighlightedBGImage = UIImage(named: "button_highlighted")! .resizableImage(withCapInsets:UIEdgeInsets(top: 15.left: 15, bottom: 15.right: 15))

    let button = SFButton(frame: .zero)
    button.setImage(UIImage(named: "icon-test"), for: .normal)
    button.setBackgroundImage(btnNormalBGImage, for: .normal)
    button.setBackgroundImage(btnHighlightedBGImage, for: .highlighted)

    return button
}
Copy the code
  • Picture left, text right
let button = makeButton()
button.space = 10
button.contentInset = UIEdgeInsets(top: 10.left: 5, bottom: 10.right: 5)
button.setTitle("direction: row".for: .normal)
Copy the code
  • Text left, picture right
let button2 = makeButton()
button2.space = 10
button2.contentInset = UIEdgeInsets(top: 10.left: 5, bottom: 10.right: 5)
button2.direction = .rowReverse
button2.setTitle("direction: rowReverse".for: .normal)
Copy the code
  • In pictures, in words
let button3 = makeButton()
button3.space = 5
button3.contentInset = UIEdgeInsets(top: 5.left: 10, bottom: 5.right: 10)
button3.direction = .column
button3.setTitle("direction: column".for: .normal)
Copy the code
  • In words, in pictures
let button4 = makeButton()
button4.space = 5
button4.contentInset = UIEdgeInsets(top: 5.left: 10, bottom: 5.right: 10)
button4.direction = .columnReverse
button4.setTitle("direction: columnReverse".for: .normal)
Copy the code

The following is the operation effect, complete implementation code please go to my Github: SFButton