Swift UI provides a modern declarative way to build UI. Learn to integrate with UIKit and create powerful hybrid apps.
Swift UI Fundamentals
Declarative UI
swift
import SwiftUI
struct ContentView: View {
@State private var isOn = false
var body: some View {
VStack {
Image(systemName: "swift")
.imageScale(.large)
.foregroundColor(.orange)
.padding()
Text("Hello, SwiftUI!")
.font(.title)
.fontWeight(.bold)
Toggle("Enable", isOn: $isOn)
.padding()
}
}
}UIKit Integration
UIViewControllerRepresentable
swift
struct UIKitControllerView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIKitView {
return UIKitView()
}
func updateUIViewController(_ uiViewController: UIKitView, context: Context) {}
}
class UIKitView: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let label = UILabel()
label.text = "UIKit View"
label.textAlignment = .center
label.frame = view.bounds
view.addSubview(label)
}
}UIViewRepresentable
struct CustomUIView: UIViewRepresentable {
var body: some View {
CustomUIViewRepresentable()
}
}
struct CustomUIViewRepresentable: UIViewRepresentable {
func makeUIView(context: Context) -> UICustomView {
UICustomView(frame: .zero)
}
func updateUIView(_ uiView: UICustomView, context: Context) {
uiView.updateContent()
}
}
class UICustomView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
private func setupView() {
backgroundColor = .systemBackground
}
func updateContent() {
setNeedsDisplay()
}
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let context = UIGraphicsGetCurrentContext() else { return }
context.setFillColor(UIColor.blue.cgColor)
context.fill(CGRect(x: 50, y: 50, width: 100, height: 100))
}
}Hosting Controllers
UIHostingController
swift
import SwiftUI
class HostingControllerManager {
func createHostingController() -> UIHostingController<ContentView> {
let contentView = ContentView()
let hostingController = UIHostingController(rootView: contentView)
hostingController.title = "Swift UI View"
return hostingController
}
func presentSwiftUIView(in viewController: UIViewController) {
let contentView = ContentView()
let hostingController = UIHostingController(rootView: contentView)
hostingController.modalPresentationStyle = .pageSheet
viewController.present(hostingController, animated: true)
}
}State Management
ObservableObject
swift
class AppStateManager: ObservableObject {
@Published var isLoggedIn = false
@Published var user: User?
@Published var isLoading = false
func login() async throws {
isLoading = true
defer { isLoading = false }
// Simulate login
try await Task.sleep(nanoseconds: 2 * 1_000_000_000)
await MainActor.run {
self.user = User(id: "123", name: "John Doe")
self.isLoggedIn = true
}
}
func logout() {
isLoggedIn = false
user = nil
}
func refreshUser() async throws {
// Refresh user data
}
}
struct User {
let id: String
let name: String
}
struct LoginView: View {
@StateObject private var stateManager = AppStateManager()
var body: some View {
VStack(spacing: 20) {
if stateManager.isLoading {
ProgressView("Logging in...")
} else if stateManager.isLoggedIn {
VStack {
Text("Welcome, \(stateManager.user?.name ?? "")")
Button("Logout") {
stateManager.logout()
}
}
} else {
Button("Login") {
Task {
try? await stateManager.login()
}
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
}
}
}Environment Objects
Environment Values
swift
struct EnvironmentView: View {
@Environment(\.colorScheme) var colorScheme
@Environment(\.horizontalSizeClass) var horizontalSizeClass
@Environment(\.sizeCategory) var sizeCategory
var body: some View {
VStack {
Text("Color Scheme: \(colorScheme == .dark ? "Dark" : "Light")")
Text("Size Class: \(horizontalSizeClass)")
Text("Size Category: \(sizeCategory)")
}
}
}
struct CustomEnvironmentKey: EnvironmentKey {
typealias Value = String
}
struct EnvironmentKeyView: View {
@Environment(CustomEnvironmentKey.self) var customValue
var body: some View {
Text("Custom Value: \(customValue)")
}
}Preference Keys
UserDefaults Integration
swift
extension UserDefaults {
struct Keys {
static let hasCompletedOnboarding = "hasCompletedOnboarding"
static let userTheme = "userTheme"
}
var hasCompletedOnboarding: Bool {
get { bool(forKey: Keys.hasCompletedOnboarding) }
set { set(newValue, forKey: Keys.hasCompletedOnboarding) }
}
}
struct SettingsView: View {
@AppStorage("hasCompletedOnboarding") private var hasCompletedOnboarding = false
@AppStorage("userTheme") private var userTheme = "light"
var body: some View {
Form {
Section {
Toggle("Onboarding Completed", isOn: $hasCompletedOnboarding)
Picker("Theme", selection: $userTheme) {
Text("Light").tag("light")
Text("Dark").tag("dark")
Text("System").tag("system")
}
}
}
}
}Combine Integration
Publisher
swift
import Combine
class ViewModel: ObservableObject {
@Published var items: [Item] = []
@Published var isLoading = false
private var cancellables = Set<AnyCancellable>()
func loadItems() {
isLoading = true
// Simulate network request
Just([Item(id: "1", name: "Item 1"), Item(id: "2", name: "Item 2")])
.delay(for: .seconds(2))
.receive(on: DispatchQueue.main)
.sink { [weak self] items in
self?.items = items
self?.isLoading = false
}
.store(in: &cancellables)
}
func refresh() {
loadItems()
}
}
struct Item: Identifiable, Equatable {
let id: String
let name: String
}
struct ItemsView: View {
@StateObject private var viewModel = ViewModel()
var body: some View {
List {
if viewModel.isLoading {
ProgressView()
} else {
ForEach(viewModel.items) { item in
Text(item.name)
}
}
}
.task {
viewModel.loadItems()
}
.refreshable {
viewModel.refresh()
}
}
}Navigation
NavigationView
swift
struct NavigationExampleView: View {
var body: some View {
NavigationStack {
List {
NavigationLink("Detail View") {
DetailView()
}
NavigationLink("Settings") {
SettingsView()
}
}
.navigationTitle("Home")
}
}
}
struct DetailView: View {
@Environment(\.dismiss) var dismiss
var body: some View {
VStack {
Text("Detail View")
.font(.title)
Button("Back") {
dismiss()
}
}
.navigationTitle("Detail")
}
}
struct SettingsView: View {
var body: some View {
VStack {
Text("Settings")
.font(.title)
}
.navigationTitle("Settings")
}
}Animations
SwiftUI Animations
swift
struct AnimationView: View {
@State private var isAnimating = false
var body: some View {
VStack {
Circle()
.fill(isAnimating ? Color.blue : Color.gray)
.frame(width: 100, height: 100)
.rotationEffect(.degrees(isAnimating ? 360 : 0))
.animation(.spring(response: 0.6, dampingFraction: 0.7), value: isAnimating)
Button("Animate") {
withAnimation {
isAnimating.toggle()
}
}
}
}
}Best Practices
- 1Declarative: Use declarative syntax
- 2State Management: Use appropriate patterns
- 3Performance: Optimize for 60 FPS
- 4Accessibility: Support accessibility
- 5Testing: Test SwiftUI views
- 6Hybrid: Integrate with UIKit when needed
- 7Data Flow: Use unidirectional data flow
- 8Localization: Support multiple languages
Swift UI provides modern, declarative UI development!