Swift UIKit Basics
Introduction
UIKit is Apple's framework for building graphical, event-driven applications for iOS, iPadOS, and tvOS. It provides the infrastructure for constructing and managing your app's user interface, handling user interactions, and managing the flow of information through your application.
In this tutorial, we'll explore the fundamental components of UIKit and how to use Swift to build iOS applications with intuitive and responsive user interfaces. UIKit has been the backbone of iOS development since the platform's inception, and even with the introduction of SwiftUI, understanding UIKit remains crucial for iOS developers.
UIKit vs. SwiftUI
Before diving in, let's clarify the relationship between UIKit and SwiftUI:
- UIKit is the mature, established framework used in most iOS apps in production today
- SwiftUI is Apple's newer, declarative UI framework introduced in 2019
- Many applications use both frameworks together
- UIKit knowledge remains essential for iOS developers
Setting Up Your First UIKit Project
Let's start by creating a basic UIKit project:
- Open Xcode and select "Create a new Xcode project"
- Choose the "App" template under iOS
- Enter your project details
- Make sure the interface is set to "Storyboard" (for UIKit)
- Select Swift as the language
Once your project is created, you'll notice several important files:
- AppDelegate.swift: Manages application lifecycle
- SceneDelegate.swift: Manages scene lifecycle (iOS 13+)
- ViewController.swift: Your main view controller
- Main.storyboard: Visual interface builder
- Info.plist: App configuration
UIKit Core Components
UIView
UIView
is the fundamental building block for all visual elements in UIKit. Views represent rectangular areas that can display content, handle user interactions, and participate in the view hierarchy.
// Creating a simple UIView programmatically
let myView = UIView(frame: CGRect(x: 50, y: 100, width: 200, height: 200))
myView.backgroundColor = .blue
view.addSubview(myView)
This code creates a 200×200 blue square positioned at (50, 100) from the top-left corner of the parent view.
UIViewController
UIViewController
manages a view hierarchy, handles transitions between user interface screens, and coordinates the flow of information between your app's data model and the views that display that data.
Here's a basic example of a view controller:
import UIKit
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Setup your view
view.backgroundColor = .white
let welcomeLabel = UILabel(frame: CGRect(x: 20, y: 100, width: view.bounds.width - 40, height: 50))
welcomeLabel.text = "Welcome to UIKit!"
welcomeLabel.textAlignment = .center
welcomeLabel.font = UIFont.systemFont(ofSize: 24, weight: .bold)
view.addSubview(welcomeLabel)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Called when view is about to appear
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Called after view has appeared
}
}
Common UIKit Controls
Let's explore some of the most frequently used UI controls in UIKit:
UILabel
UILabel
displays read-only text:
let label = UILabel(frame: CGRect(x: 20, y: 50, width: 200, height: 30))
label.text = "Hello UIKit!"
label.textColor = .darkGray
label.font = UIFont.systemFont(ofSize: 18)
label.textAlignment = .center
view.addSubview(label)
UIButton
UIButton
responds to user taps and triggers actions:
let button = UIButton(type: .system)
button.frame = CGRect(x: 100, y: 200, width: 200, height: 50)
button.setTitle("Tap Me", for: .normal)
button.backgroundColor = .systemBlue
button.setTitleColor(.white, for: .normal)
button.layer.cornerRadius = 10
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
view.addSubview(button)
// Action method that will be called when button is tapped
@objc func buttonTapped() {
print("Button was tapped!")
// Do something in response to button tap
}
UITextField
UITextField
allows users to input text:
let textField = UITextField(frame: CGRect(x: 20, y: 300, width: 300, height: 40))
textField.placeholder = "Enter your name"
textField.borderStyle = .roundedRect
textField.delegate = self // You need to conform to UITextFieldDelegate
view.addSubview(textField)
To handle text field events, you need to conform to UITextFieldDelegate
:
extension ViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder() // Hide the keyboard
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
if let text = textField.text, !text.isEmpty {
print("User entered: \(text)")
}
}
}
UIImageView
UIImageView
displays images:
let imageView = UIImageView(frame: CGRect(x: 50, y: 350, width: 300, height: 200))
imageView.image = UIImage(named: "sample_image")
imageView.contentMode = .scaleAspectFit
view.addSubview(imageView)
Auto Layout Basics
Auto Layout dynamically calculates the size and position of views based on constraints. This allows interfaces to adapt to different screen sizes and orientations.
Adding Constraints Programmatically
let redBox = UIView()
redBox.translatesAutoresizingMaskIntoConstraints = false
redBox.backgroundColor = .red
view.addSubview(redBox)
// Add constraints
NSLayoutConstraint.activate([
redBox.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
redBox.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
redBox.widthAnchor.constraint(equalToConstant: 100),
redBox.heightAnchor.constraint(equalToConstant: 100)
])
Handling User Interactions
UIKit uses a target-action pattern for handling control events (like button taps) and delegation for more complex interactions.
Target-Action Example
let actionButton = UIButton(type: .system)
actionButton.setTitle("Show Alert", for: .normal)
actionButton.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(actionButton)
// Position the button
NSLayoutConstraint.activate([
actionButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
actionButton.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
// Add action
actionButton.addTarget(self, action: #selector(showAlert), for: .touchUpInside)
// Alert action method
@objc func showAlert() {
let alert = UIAlertController(
title: "Hello!",
message: "This is a UIKit alert",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
UITableView - A Fundamental UIKit Component
UITableView
is one of the most used components in iOS apps, displaying scrollable lists of data.
class FruitsTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let tableView = UITableView()
let fruits = ["Apple", "Banana", "Cherry", "Date", "Elderberry", "Fig", "Grape"]
override func viewDidLoad() {
super.viewDidLoad()
// Setup tableView
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
tableView.dataSource = self
tableView.delegate = self
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
}
// MARK: - UITableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fruits.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = fruits[indexPath.row]
return cell
}
// MARK: - UITableViewDelegate
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
print("Selected \(fruits[indexPath.row])")
}
}
Navigation in UIKit
Navigation between view controllers typically uses UINavigationController
:
// In SceneDelegate.swift or AppDelegate.swift
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
// Create main view controller
let mainViewController = ViewController()
// Wrap it in a navigation controller
let navigationController = UINavigationController(rootViewController: mainViewController)
// Set the navigation controller as the root
window.rootViewController = navigationController
self.window = window
window.makeKeyAndVisible()
}
To navigate to another view controller:
@objc func navigateToDetail() {
let detailViewController = DetailViewController()
navigationController?.pushViewController(detailViewController, animated: true)
}
Practical Example: Contact List App
Let's put everything together by creating a simple contact list app:
// Contact model
struct Contact {
let name: String
let phone: String
}
// ContactListViewController
class ContactListViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let tableView = UITableView()
var contacts = [
Contact(name: "John Smith", phone: "(555) 123-4567"),
Contact(name: "Jane Doe", phone: "(555) 987-6543"),
Contact(name: "Bob Johnson", phone: "(555) 456-7890")
]
override func viewDidLoad() {
super.viewDidLoad()
title = "Contacts"
// Setup add button
navigationItem.rightBarButtonItem = UIBarButtonItem(
barButtonSystemItem: .add,
target: self,
action: #selector(addContactTapped)
)
// Configure tableView
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "ContactCell")
tableView.dataSource = self
tableView.delegate = self
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}
@objc func addContactTapped() {
let alertController = UIAlertController(
title: "Add Contact",
message: "Enter contact details",
preferredStyle: .alert
)
alertController.addTextField { textField in
textField.placeholder = "Name"
}
alertController.addTextField { textField in
textField.placeholder = "Phone Number"
textField.keyboardType = .phonePad
}
let addAction = UIAlertAction(title: "Add", style: .default) { [weak self] _ in
guard let name = alertController.textFields?[0].text, !name.isEmpty,
let phone = alertController.textFields?[1].text, !phone.isEmpty else {
return
}
let newContact = Contact(name: name, phone: phone)
self?.contacts.append(newContact)
self?.tableView.reloadData()
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
alertController.addAction(addAction)
alertController.addAction(cancelAction)
present(alertController, animated: true)
}
// MARK: - UITableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return contacts.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ContactCell", for: indexPath)
let contact = contacts[indexPath.row]
cell.textLabel?.text = contact.name
cell.detailTextLabel?.text = contact.phone
return cell
}
// MARK: - UITableViewDelegate
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let contact = contacts[indexPath.row]
let detailVC = ContactDetailViewController(contact: contact)
navigationController?.pushViewController(detailVC, animated: true)
}
}
// Contact Detail View Controller
class ContactDetailViewController: UIViewController {
let contact: Contact
init(contact: Contact) {
self.contact = contact
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
title = contact.name
view.backgroundColor = .white
// Create UI
let nameLabel = UILabel()
nameLabel.translatesAutoresizingMaskIntoConstraints = false
nameLabel.font = UIFont.boldSystemFont(ofSize: 24)
nameLabel.text = contact.name
let phoneLabel = UILabel()
phoneLabel.translatesAutoresizingMaskIntoConstraints = false
phoneLabel.font = UIFont.systemFont(ofSize: 18)
phoneLabel.text = contact.phone
let callButton = UIButton(type: .system)
callButton.translatesAutoresizingMaskIntoConstraints = false
callButton.setTitle("Call", for: .normal)
callButton.backgroundColor = .systemGreen
callButton.setTitleColor(.white, for: .normal)
callButton.layer.cornerRadius = 10
callButton.addTarget(self, action: #selector(callTapped), for: .touchUpInside)
view.addSubview(nameLabel)
view.addSubview(phoneLabel)
view.addSubview(callButton)
NSLayoutConstraint.activate([
nameLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
nameLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
phoneLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: 10),
phoneLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
callButton.topAnchor.constraint(equalTo: phoneLabel.bottomAnchor, constant: 30),
callButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
callButton.widthAnchor.constraint(equalToConstant: 200),
callButton.heightAnchor.constraint(equalToConstant: 50)
])
}
@objc func callTapped() {
let phoneNumber = contact.phone.replacingOccurrences(of: " ", with: "")
.replacingOccurrences(of: "-", with: "")
.replacingOccurrences(of: "(", with: "")
.replacingOccurrences(of: ")", with: "")
if let url = URL(string: "tel://\(phoneNumber)"), UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
} else {
let alert = UIAlertController(
title: "Cannot Make Call",
message: "This device cannot make phone calls.",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
}
}
Summary
In this tutorial, we've covered:
- The basics of UIKit and its role in iOS development
- Core components like UIView and UIViewController
- Common UI controls (UILabel, UIButton, UITextField, UIImageView)
- Auto Layout for responsive interfaces
- Handling user interactions with target-action and delegation
- Working with UITableView for displaying lists
- Navigation between view controllers
- A practical example building a contacts app
UIKit is an extensive framework with many more components and capabilities than we could cover here. With these fundamentals, you're now ready to explore more advanced topics like custom view controllers, collection views, animations, and more.
Additional Resources
- Apple's UIKit Documentation
- Human Interface Guidelines
- UIKit Catalog: Creating and Customizing Views and Controls
Practice Exercises
- Modify the contact list app to use custom table view cells with a more polished UI
- Add the ability to edit existing contacts
- Create a settings screen that lets users customize the app's appearance
- Add data persistence to the contacts app using UserDefaults or Core Data
- Create a simple calculator app using UIButtons and Auto Layout
By mastering UIKit fundamentals, you'll be well on your way to building professional iOS applications!
If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)