SoatDev IT Consulting
SoatDev IT Consulting
  • About us
  • Expertise
  • Services
  • How it works
  • Contact Us
  • News
  • August 1, 2023
  • Rss Fetcher
Photo by Firmbee.com on Unsplash

Protocol-oriented programming is one of the most powerful and flexible tools for competent composition and distribution of responsibility in Swift. In one of the previous articles, protocol-oriented programming was used to manage the state and build a safe sequence of state transitions without additional checks. If you have not read the previous article, then it is recommended to read as material that will show one of the ways to use this wonderful approach.

In this article, we’ll explore another way to use protocol-oriented programming. As a bonus — we’ll write our extension for programming UI components in UIKit that mimics the SwiftUI experience.

What is the task before us? As we all know, all graphical components in UIKit are direct descendants of UIView, each with its own unique properties. A protocol-oriented approach will help us to endow each of the heirs with their own unique properties while making it possible to combine these properties in case we want to use the properties of the parent and the properties of the child. In addition to the protocol-oriented approach, we will also use the Decorator design pattern to bring the SwiftUI declarative syntax experience to UIKit.

Let’s start with the simplest. Select several basic UI classes with which we will start:

  1. UIView — all graphical components are inherited from it
  2. UIControl — all UIButton, UISegmentedControl and so on are inherited from it
  3. Final UI components like UILabel, UITextField, and so on

The inheritance diagram can be seen below:

Simplified inheritance scheme
Simplified inheritance scheme

Let’s start our task by creating an interface:

protocol Stylable {}

This protocol is the basis for all subsequent extensions for our case. Since all graphical components somehow inherit from UIView to cover all components — it is enough to extend UIView with this protocol:

extension UIView: Stylable {}

Some of the most used properties for customizing a UIView are cornerRadius, backgroundColor, clipsToBounds, contentMode, isHidden. Moreover, these properties are often used not only to configure the UIView, but also for its descendants.

Let’s extend the possibilities of Stylable for all UIView classes and their descendants:

extension Stylable where Self: UIView {
@discardableResult
func cornerRadius(_ value: CGFloat) -> Self {
self.layer.cornerRadius = value

return self
}

@discardableResult
func backgroundColor(_ value: UIColor) -> Self {
self.backgroundColor = value

return self
}

@discardableResult
func clipsToBounds(_ value: Bool) -> Self {
self.clipsToBounds = value

return self
}

@discardableResult
func contentMode(_ value: UIView.ContentMode) -> Self {
self.contentMode = value

return self
}

@discardableResult
func isHidden(_ value: Bool) -> Self {
self.isHidden = value

return self
}
}

Let’s check what this extension gave us:

let customView = UIView()
.backgroundColor(.red)
.clipsToBounds(true)
.cornerRadius(20)

let customButton = UIButton()
.backgroundColor(.red)
.clipsToBounds(true)
.cornerRadius(20)

let segmentedControl = UISegmentedControl(items: ["One", "Two"])
.backgroundColor(.red)
.clipsToBounds(true)
.cornerRadius(20)

let scrollView = UIScrollView()
.backgroundColor(.red)
.clipsToBounds(true)
.cornerRadius(20)

let textField = UITextField()
.backgroundColor(.red)
.clipsToBounds(true)
.cornerRadius(20)

As we can see, thanks to the extension, we can declaratively change properties not only for UIView but also for its descendants.

Let’s move on to configuring the UIControl. For all of its descendants, one of the most used customizable things is tap, properties — isEnabled, tintColor, isUserInteractionEnabled.

Let’s extend the possibilities of Stylable for all UIControl classes and their descendants:

extension Stylable where Self: UIControl {
@discardableResult
func action(_ value: (() -> Void)?, event: UIControl.Event = .touchUpInside) -> Self {
let identifier = UIAction.Identifier(String(describing: event.rawValue))
let action = UIAction(identifier: identifier) { _ in
value?()
}

self.removeAction(identifiedBy: identifier, for: event)
self.addAction(action, for: event)

return self
}

@discardableResult
func secondAction(_ value: ((Bool) -> Void)?, controlEvent: UIControl.Event = .valueChanged) -> Self {
let identifier = UIAction.Identifier(String(describing: controlEvent.rawValue))
let action = UIAction(identifier: identifier) { item in
guard let control = item.sender as? UIControl else {
return
}
value?(!control.isTracking)
}

self.removeAction(identifiedBy: identifier, for: controlEvent)
self.addAction(action, for: controlEvent)

return self
}

@discardableResult
func isEnabled(_ value: Bool) -> Self {
self.isEnabled = value

return self
}

@discardableResult
func isUserInteractionEnabled(_ value: Bool) -> Self {
self.isUserInteractionEnabled = value

return self
}

@discardableResult
func tintColor(_ value: UIColor) -> Self {
self.tintColor = value

return self
}
}

After the Stylable extension for UIControl, an additional customization option became available for all its descendants:

let customButton = UIButton()
.backgroundColor(.red)
.clipsToBounds(true)
.cornerRadius(20)
.tintColor(.red)
.action {
print(#function)
}
.isEnabled(true)
.isUserInteractionEnabled(true)


let segmentedControl = UISegmentedControl(items: ["One", "Two"])
.backgroundColor(.red)
.clipsToBounds(true)
.cornerRadius(20)
.tintColor(.red)
.action {
print(#function)
}
.isEnabled(true)
.isUserInteractionEnabled(true)

It is worth noting that when calling the action method using a class method, you should initialize this component lazily to ensure that the class (self) is initialized before the component is initialized.

lazy var customButton = UIButton()
.backgroundColor(.red)
.clipsToBounds(true)
.cornerRadius(20)
.tintColor(.red)
.action { [weak self] in
self?.actionTest()
}
.isEnabled(true)
.isUserInteractionEnabled(true)

private func actionTest() {
print(#function)
}

Let’s also extend UITextField with some of the most popular custom properties:

extension Stylable where Self: UITextField {
@discardableResult
func text(_ value: String?) -> Self {
self.text = value

return self
}

@discardableResult
func font(_ value: UIFont) -> Self {
self.font = value

return self
}

@discardableResult
func textAlignment(_ value: NSTextAlignment) -> Self {
self.textAlignment = value

return self
}

@discardableResult
func textColor(_ value: UIColor) -> Self {
self.textColor = value

return self
}

@discardableResult
func capitalizationType(_ value: UITextAutocapitalizationType) -> Self {
self.autocapitalizationType = value

return self
}

@discardableResult
func keyboardType(_ value: UIKeyboardType) -> Self {
self.keyboardType = value

return self
}

@discardableResult
func isSecureTextEntry(_ value: Bool) -> Self {
self.isSecureTextEntry = value

return self
}

@discardableResult
func autocorrectionType(_ value: UITextAutocorrectionType) -> Self {
self.autocorrectionType = value

return self
}

@discardableResult
func contentType(_ value: UITextContentType?) -> Self {
self.textContentType = value

return self
}

@discardableResult
func clearButtonMode(_ value: UITextField.ViewMode) -> Self {
self.clearButtonMode = value

return self
}

@discardableResult
func placeholder(_ value: String?) -> Self {
self.placeholder = value

return self
}

@discardableResult
func returnKeyType(_ value: UIReturnKeyType) -> Self {
self.returnKeyType = value

return self
}

@discardableResult
func delegate(_ value: UITextFieldDelegate) -> Self {
self.delegate = value

return self
}

@discardableResult
func atributedPlaceholder(
_ value: String,
textColor: UIColor,
textFont: UIFont
) -> Self {
let attributedString = NSAttributedString(
string: value,
attributes: [
NSAttributedString.Key.foregroundColor: textColor,
NSAttributedString.Key.font: textFont
]
)

self.attributedPlaceholder = attributedString

return self
}
}

Thanks to this extension, now customizing UITextField has become even easier. To customize the GUI, the methods of its parents are available, as well as its own methods:

lazy var textField = UITextField()
.placeholder("Placeholder")
.textColor(.red)
.text("Text")
.contentType(.URL)
.autocorrectionType(.yes)
.font(.boldSystemFont(ofSize: 12))
.delegate(self)

It’s worth noting that, similar to capturing self in the UIControl’s action method, assigning a delegate also requires textField to be lazy-initialized.

By analogy, the rest of the graphical components are expanded with properties that will be used for customization.

As a bonus for my readers, I’ve compiled some of the most requested properties in this repository. You need to copy the files to your project; they are ready to use.

Don’t hesitate to contact me on Twitter if you have any questions. Also, you can always buy me a coffee.


Creating and Modifying UIKit Components Like in SwiftUI was originally published in Better Programming on Medium, where people are continuing the conversation by highlighting and responding to this story.

Previous Post
Next Post

Recent Posts

  • PwC Introduces AI Operating Systems to Power Scalable AI Workflows
  • Figma moves closer to a blockbuster IPO that could raise $1.5B
  • Road to Battlefield: Central Eurasia’s gateway to TechCrunch Startup Battlefield
  • X is piloting a program that lets AI chatbots generate Community Notes
  • The GOP’s big spending bill could kill renewable energy projects

Categories

  • Industry News
  • Programming
  • RSS Fetched Articles
  • Uncategorized

Archives

  • July 2025
  • June 2025
  • May 2025
  • April 2025
  • February 2025
  • January 2025
  • December 2024
  • November 2024
  • October 2024
  • September 2024
  • August 2024
  • July 2024
  • June 2024
  • May 2024
  • April 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • November 2023
  • October 2023
  • September 2023
  • August 2023
  • July 2023
  • June 2023
  • May 2023
  • April 2023

Tap into the power of Microservices, MVC Architecture, Cloud, Containers, UML, and Scrum methodologies to bolster your project planning, execution, and application development processes.

Solutions

  • IT Consultation
  • Agile Transformation
  • Software Development
  • DevOps & CI/CD

Regions Covered

  • Montreal
  • New York
  • Paris
  • Mauritius
  • Abidjan
  • Dakar

Subscribe to Newsletter

Join our monthly newsletter subscribers to get the latest news and insights.

© Copyright 2023. All Rights Reserved by Soatdev IT Consulting Inc.