Need help to avoid repetition code

15%20PM

Is there any way to avoid those repetitive lines ?

Write a function that sets the properties how you want, and call it for each button in your view.

is it gonna be like this ?
func cornerRadiusFunc(_ button: UIButton!, _ radius: Int, _ bound: Boolean) {
button.layer.cornerRadius = radius
button.clipsToBounds = bound
}

I would personally do it with an extension and a default value for the clipped to bounds:

extension UIButton {
  func roundCorners(withRadius r: CGFloat, clippedToBounds: Bool = true) {
    layer.cornerRadius = r
    clipsToBounds = clippedToBounds
  }
}
2 Likes

In general, if the parameters to be set are the same for a bunch of objects...

for button in [button1, button2, ...] {
    button.backgroundColor = .white
    button.clipsToBounds = true
    button.layer.cornerRadius = 10
}
3 Likes

I only need to write that extension in one class, and I can implement that in the whole project. Am I right ?

How about use the technique of Lens on UIButton?
The following is code snap, detail lens support framework can see PaversUI-Lens
The original code is from Kickstarter-iOS-OpenSourceProject.

func >>> <A, B, C> (lhs: (A) -> B, rhs: (B) -> C) -> (A) -> C
func |> <A, B> (x: A, f: (A) -> B) -> B
func .~ <Whole, Part> (lens: Lens<Whole, Part>, part: Part) -> ((Whole) -> Whole)

let buttonStyle: (UIButton) -> UIButton =
  UIButton.lens.layer.cornerRadius .~ 10
  >>> UIButton.lens.clipsToBounds .~ true

_ = backButton |> buttonStyle
_ = playButton |> buttonStyle

Afterward, you can keep that style somewhere and apply it to which button you want, just like CSS to the HTML element.

Why not set these values in Interface Builder?

1 Like
[button1, button2].forEach{
    $0.cornerRadius = 10
    $0.clipsToBounds = true
}

If the values differ, I often go with an array of tuples of [(button1, "Hit me", 10), (button2, "Leave", 0)] or several arrays which I zip together before doing the assignments.

Yes, you can write that for the UIButton class then you can call it like so:

override func viewDidLoad() {
  super,viewDidLoad()
  backButton.roundCorners(withRadius: 10)
  playButton.roundCorners(withRadius: 10)
}

Note this will only work for UIButton's. If you want this for any UI component you can extend UIView. It's as simple as replacing UIButton in the extension with UIView

extension UIView {
  func roundCorners(withRadius r: CGFloat, clippedToBounds: Bool = true) {
    layer.cornerRadius = r
    clipsToBounds = clippedToBounds
  }
}

You can also create an outlet collection by selecting and dragging the buttons from storyboard to your Swift file.

@IBOutlet var buttons: [UIButton]!

override func viewDidLoad() {
    super.viewDidLoad()
    
    buttons.forEach {
        $0.layer.cornerRadius = 10
        $0.clipsToBounds = true
    }
}