logo
hero image

Integrasi UIKit ke dalam SwiftUI

Tutorial penggunaan UIViewRepresentable dan UIViewControllerRepresentable.
17 April 2022 · 11 Minutes

Pada event WWDC tahun 2019, Apple memperkenalkan sebuah framework baru untuk membuat User Interface pada aplikasi iOS, watchOS, tvOS, dan macOS bernama SwiftUI.

WWDC 2019WWDC 2019

Sangat mungkin di masa depan nanti framework ini akan menggantikan framework pendahulunya yaitu UIKit -- seperti halnya bahasa pemrograman Swift yang telah menggantikan Objective C. Karena ada banyak sekali kelebihan yang dimiliki oleh SwiftUI, misalnya Syntax-nya yang declarative (tidak seperti UIKit yang imperative), fitur Live Preview, dan masih banyak lagi.

Meskipun begitu, SwiftUI tentunya mempunyai limitasi-limitasi karena framework ini memang masih sangat baru. Salah satu limitasi yang paling krusial menurut saya adalah karena UI Components di SwiftUI tidak selengkap di UIKit. Setidaknya sampai artikel ini ditulis, Di SwiftUI belum ada Component yang sepadan dengan UIImagePickerController, UISearchController, dll. padahal Component-Component tersebut akan sangat sering kita gunakan dalam membuat aplikasi.

kamu bisa cek lebih lengkapnya di sini : https://fuckingswiftui.com

Untuk mengatasi masalah tersebut, untungnya di SwiftUI kita tetap bisa menggunakan UI Component dari UIKit! 😃 yaitu dengan cara wrapping View yang berasal dari UIKit menggunakan UIViewRepresentable atau UIViewControllerRepresentable untuk View Controller. Setelah di-wrap, nanti kita bisa menampilkan View-nya seperti biasa ke dalam body SwiftUI.

UIViewRepresentable & UIViewControllerRepresentableUIViewRepresentable & UIViewControllerRepresentable


UIViewRepresentable

UIViewRepresentable adalah protocol yang dapat kita gunakan untuk me-wrap View yang berasal dari UIKit agar bisa ditampilkan ke dalam SwiftUI.

1. Menampilkan UIKit View

Pertama, kita akan coba menampilkan View sederhana dari UIKit ke dalam SwiftUI. Perhatikan code berikut :

Menampilkan UIKit View
icon
1import SwiftUI
2
3struct ContentView: View {
4 var body: some View {
5 UIKitView()
6 .frame(width: 100, height: 100)
7 }
8}
9
10struct UIKitView: UIViewRepresentable {
11 func makeUIView(context: Context) -> some UIView {
12 let view = UIView()
13 view.backgroundColor = .systemOrange
14 return view
15 }
16
17 func updateUIView(_ uiView: UIViewType, context: Context) {}
18}

Ada 2 method yang harus dipenuhi jika kita ingin menggunakan protocol UIViewRepresentable, yaitu :

  • makeUIView : Digunakan untuk membuat View dari UIKit yang ingin kita tampilkan.

  • updateUIView : Digunakan untuk melakukan update ke UIKit View jika terjadi perubahan data. akan saya jelaskan lebih lanjut nanti di step nomor 2.

Jika code di atas kita jalankan, maka hasilnya akan seperti ini :

Menampilkan UIKit ViewMenampilkan UIKit View

2. Passing data dari SwiftUI ke UIKit

Selanjutnya, kita akan coba mengirim data dari SwiftUI ke UIKit View, sebagai contoh kita akan membuat sebuah 2 TextField, yang pertama adalah TextField dari SwiftUI dan yang satunya adalah TextField dari UIKit.

Jika TextField di SwiftUI berubah value-nya, maka akan mengupdate juga value dari TextField yang di UIKit.

Caranya seperti ini :

Passing data dari SwiftUI ke UIKit
icon
1import SwiftUI
2
3struct ContentView: View {
4 @State private var text: String = ""
5
6 var body: some View {
7 VStack {
8 Text("Text : \(text)")
9 .frame(maxWidth: .infinity)
10 .padding(20)
11 .background(.yellow)
12
13 Spacer()
14 .frame(height: 50)
15
16 Text("SwiftUI TextField :")
17 TextField("Type here...", text: $text)
18 .frame(height: 44)
19 .padding(.horizontal, 10)
20 .border(.gray, width: 2)
21
22 Text("UIKit TextField :")
23 UIKitTextField(text: $text)
24 .frame(height: 44)
25 .padding(.horizontal, 10)
26 .border(.gray, width: 2)
27 }
28 .padding(20)
29 }
30}
31
32
33struct UIKitTextField: UIViewRepresentable {
34 @Binding var text: String
35
36 func makeUIView(context: Context) -> UITextField {
37 let textField = UITextField()
38 textField.placeholder = "Type here..."
39 return textField
40 }
41
42 func updateUIView(_ uiView: UITextField, context: Context) {
43 uiView.text = text
44 }
45}

Penjelasan Kode :

  1. Pertama, kita buat properti text pada struct UIKitTextField

icon
1@Binding var text: String
  1. Kemudian kita berikan value untuk properti text tersebut dari state text yang ada pada SwiftUI.

icon
1@State private var text: String = ""
2...
3UIKitTextField(text: $text)
  1. Terakhir, kita gunakan method updateUIView untuk mengupdate value dari UITextField jika terjadi perubahan pada state text

icon
1func updateUIView(_ uiView: UITextField, context: Context) {
2 uiView.text = text
3}

Jika kita jalankan code-nya maka hasilnya akan seperti ini :

Passing data dari SwiftUI ke UIKitPassing data dari SwiftUI ke UIKit

3. Passing data dari UIKit ke SwiftUI

Sampai di sini kita telah berhasil untuk membuat agar value dari TextField di UIKit ikut berubah jika value dari TextField di SwiftUI berubah.

Namun jika value dari TextField di UIKit berubah, value dari TextField di SwiftUI tidak ikut berubah. 🤔

Tidak berubahTidak berubah

Lalu bagaimana cara untuk memecahkan masalah tersebut? kita bisa membuat sebuah Coordinator menggunakan method makeCoordinator, Coordinator ini bertugas untuk membuat delegasi pada UIKit View. Pada case di atas, kita akan membuat delegasi dari UITextFieldDelegate karena kita ingin menggunakan method textFieldDidChangeSelection, method ini akan dipanggil ketika value dari TextField di UIKit berubah, dan di dalam method tersebut juga kita akan meng-update value dari TextField di SwiftUI sesuai dengan value dari TextField di UIkit.

Passing data dari UIKit ke SwiftUI
icon
1struct UIKitTextField: UIViewRepresentable {
2 @Binding var text: String
3
4 func makeUIView(context: Context) -> UITextField {
5 let textField = UITextField()
6 textField.placeholder = "Type here..."
8 textField.delegate = context.coordinator
9 return textField
10 }
11
12 func updateUIView(_ uiView: UITextField, context: Context) {
13 uiView.text = text
14 }
15
17 func makeCoordinator() -> Coordinator {
18 return Coordinator(text: $text)
19 }
20
21 class Coordinator: NSObject, UITextFieldDelegate {
22 @Binding var text: String
23
24 init(text: Binding<String>) {
25 self._text = text
26 }
27
28 func textFieldDidChangeSelection(_ textField: UITextField) {
29 text = textField.text ?? ""
30 }
31 }
33}

Penjelasan kode :

  1. Kita buat dulu sebuah Coordinator menggunakan method makeCoordinator

  2. Kemudian kita delegasikan textField.delegate kepada context.coordinator, context.coordinator ini adalah hasil kembalian dari method makeCoordinator yang telah kita buat sebelumnya.

Sekarang, jika kita mengubah value dari TextField yang ada di UIKit, maka value dari TextField yang ada di SwiftUI pun akan ikut berubah :

Passing data dari UIKit ke SwiftUIPassing data dari UIKit ke SwiftUI

4. Custom Modifier

Terakhir, kita akan coba membuat Custom Modifier bernama textColorModifier pada struct UIKitTextField. modifier ini berfungsi untuk mengubah warna font pada TextField di UIKit.

Caranya seperti ini :

Custom Modifier
icon
1import SwiftUI
2
3struct ContentView: View {
4 @State private var text: String = ""
6 @State private var color: UIColor = .black
7
8 var body: some View {
9 VStack {
10 Text("Text : \(text)")
11 .frame(maxWidth: .infinity)
12 .padding(20)
13 .background(.yellow)
14
15 Spacer()
16 .frame(height: 50)
17
18 Text("SwiftUI TextField :")
19 TextField("Type here...", text: $text)
20 .frame(height: 44)
21 .padding(.horizontal, 10)
22 .border(.gray, width: 2)
23
24
25 Text("UIKit TextField :")
26 UIKitTextField(text: $text)
28 .textColorModifier(color)
29 .frame(height: 44)
30 .padding(.horizontal, 10)
31 .border(.gray, width: 2)
32
33 Spacer()
34 .frame(height: 50)
35
37 HStack {
38 Button("RED") {
39 color = .systemRed
40 }
41 .frame(maxWidth: .infinity, minHeight: 44)
42 .foregroundColor(.white)
43 .background(.red)
44
45 Button("GREEN") {
46 color = .systemGreen
47 }
48 .frame(maxWidth: .infinity, minHeight: 44)
49 .foregroundColor(.white)
50 .background(.green)
51
52 Button("BLUE") {
53 color = .systemBlue
54 }
55 .frame(maxWidth: .infinity, minHeight: 44)
56 .foregroundColor(.white)
57 .background(.blue)
58 }
60 }
61 .padding(20)
62 }
63}
64
65
66struct UIKitTextField: UIViewRepresentable {
67 @Binding var text: String
69 var textColor: UIColor = .black
70
71 func makeUIView(context: Context) -> UITextField {
72 let textField = UITextField()
73 textField.placeholder = "Type here..."
75 textField.textColor = textColor
76 textField.delegate = context.coordinator
77 return textField
78 }
79
80 func updateUIView(_ uiView: UITextField, context: Context) {
81 uiView.text = text
83 uiView.textColor = textColor
84 }
85
86 func makeCoordinator() -> Coordinator {
87 return Coordinator(text: $text)
88 }
89
90 class Coordinator: NSObject, UITextFieldDelegate {
91 @Binding var text: String
92
93 init(text: Binding<String>) {
94 self._text = text
95 }
96
97 func textFieldDidChangeSelection(_ textField: UITextField) {
98 text = textField.text ?? ""
99 }
100 }
101}
102
104extension UIKitTextField {
105 func textColorModifier(_ color: UIColor) -> UIKitTextField {
106 var textField = self
107 textField.textColor = color
108 return textField
109 }
110}

Yang perlu kita perhatikan adalah di bagian ini :

icon
1extension UIKitTextField {
2 func textColorModifier(_ color: UIColor) -> UIKitTextField {
3 var textField = self
4 textField.textColor = color
5 return textField
6 }
7}

Jadi, Kita membuat sebuah method textColorModifier yang mengembalikan tipe data UIKitTextField, method ini menerima 1 paramater yaitu color. kemudian di dalam method ini juga kita ubah properti textColor yang ada pada UIKitTextField. Sekarang, kita bisa pakai Custom Modifier yang telah kita buat barusan :

icon
1UIKitTextField(text: $text)
2 .textColorModifier(color)

Jika code di atas kita jalankan, maka hasilnya akan seperti ini :

Custom ModifierCustom Modifier


UIViewControllerRepresentable

UIViewControllerRepresentable adalah protocol yang dapat kita gunakan untuk me-wrap View Controller yang berasal dari UIKit agar bisa ditampilkan ke dalam SwiftUI.

1. Menampilkan UIKit View Controller

Pertama, kita akan coba menampilkan View Controller sederhana dari UIKit ke dalam SwiftUI. Perhatikan code berikut :

Menampilkan UIKit View Controller
icon
1import SwiftUI
2
3struct ContentView: View {
4 @State private var isPresented: Bool = false
5
6 var body: some View {
7 VStack {
8 Button("Show UIKit VC") {
9 isPresented = true
10 }
11 }
12 .sheet(isPresented: $isPresented, content: {
13 UIKitVC()
14 })
15 }
16}
17
18struct UIKitVC: UIViewControllerRepresentable {
19 func makeUIViewController(context: Context) -> some UIViewController {
20 let vc = UIViewController()
21 vc.view.backgroundColor = .systemYellow
22 return vc
23 }
24
25 func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
26}

Mirip dengan UIViewRepresentable, pada UIViewControllerRepresentable juga terdapat 2 method yang harus dipenuhi jika kita ingin menggunakan protocol tersebut, yaitu :

  • makeUIViewController : Digunakan untuk membuat View Controller yang ingin kita tampilkan.

  • updateUIViewController : Digunakan untuk melakukan update ke View Controller jika terjadi perubahan data.

Jika code di atas kita jalankan, maka hasilnya akan seperti ini :

Menampilkan UIKit View ControllerMenampilkan UIKit View Controller

2. Passing data dari SwiftUI ke UIKit

Selanjutnya, kita akan coba mengirim data dari SwiftUI ke UIKit View Controller. caranya sangat mudah :

Passing data dari SwiftUI ke UIKit
icon
1import SwiftUI
2
3struct ContentView: View {
4 @State private var isPresented: Bool = false
5
6 var body: some View {
7 VStack {
8 Button("Show UIKit VC") {
9 isPresented = true
10 }
11 }
12 .sheet(isPresented: $isPresented, content: {
14 UIKitVC(text: "From SwiftUI")
15 })
16 }
17}
18
19struct UIKitVC: UIViewControllerRepresentable {
21 let text: String
22
23 func makeUIViewController(context: Context) -> some UIViewController {
24 let vc = UIViewController()
25 vc.view.backgroundColor = .systemYellow
26
27 let label = UILabel()
28 label.frame = vc.view.bounds
29 label.textAlignment = .center
31 label.text = text
32 vc.view.addSubview(label)
33
34 return vc
35 }
36
37 func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
38}

Jika kita jalankan code-nya, dan kita klik button "Show UIKit VC" maka akan muncul text "From SwiftUI" seperti ini :

Passing data dari SwiftUI ke UIKitPassing data dari SwiftUI ke UIKit

3. Passing data dari UIKit ke SwiftUI

Pada contoh kali ini, kita akan membuat Image Picker menggunakan UIKit dan setelah user selesai memilih foto, maka foto tersebut akan dikirim lagi ke SwiftUI untuk ditampilkan kepada user.

Caranya seperti ini :

Passing data dari UIKit ke SwiftUI
icon
1import SwiftUI
2
3struct ContentView: View {
4 @State private var isPresented: Bool = false
5 @State private var image: UIImage?
6
7 var body: some View {
8 VStack {
10 if image != nil {
11 Image(uiImage: image!)
12 .resizable()
13 .scaledToFit()
14 .frame(width: 250, height: 250)
15 } else {
16 Text("No Image")
17 }
19
20 Spacer()
21 .frame(height: 50)
22
23 Button("Choose Image") {
24 isPresented = true
25 }
26 }
27 .sheet(isPresented: $isPresented, content: {
28 UIKitImagePicker(image: $image)
29 })
30 }
31}
32
33struct UIKitImagePicker: UIViewControllerRepresentable {
34 @Binding var image: UIImage?
35
37 func makeUIViewController(context: Context) -> some UIImagePickerController {
38 let vc = UIImagePickerController()
39 vc.allowsEditing = false
40 vc.delegate = context.coordinator
41 return vc
42 }
44
45 func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
46
47 func makeCoordinator() -> Coordinator {
48 return Coordinator(image: $image)
49 }
50
51 class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
52 @Binding var image: UIImage?
53
54 init(image: Binding<UIImage?>) {
55 self._image = image
56 }
57
59 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
60 guard let newImage = info[.originalImage] as? UIImage else { return }
61 image = newImage
62 }
64 }
65}

Penjelasan Kode :

  1. Kita buat dulu Image Picker dari UIKit menggunakan method makeUIViewController

icon
1func makeUIViewController(context: Context) -> some UIImagePickerController {
2 let vc = UIImagePickerController()
3 vc.allowsEditing = false
4 vc.delegate = context.coordinator
5 return vc
6}
  1. Lalu kita buat method didFinishPickingMediaWithInfo, method ini akan dipanggil ketika user selesai memilih foto. dan kemudian kita kirim data image-nya kepada SwiftUI.

icon
1func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
2 guard let newImage = info[.originalImage] as? UIImage else { return }
3 image = newImage
4}
  1. Terakhir, jika user selesai memilih foto (nilai dari image tidak nil), maka kita tampilkan foto tersebut kepada user :

icon
1if image != nil {
2 Image(uiImage: image!)
3 .resizable()
4 .scaledToFit()
5 .frame(width: 250, height: 250)
6} else {
7 Text("No Image")
8}

Jika kita jalankan code di atas, maka hasilnya akan seperti ini :

Passing data dari UIKit ke SwiftUIPassing data dari UIKit ke SwiftUI

Menggunakan Storyboard di SwiftUI

Yang terakhir, kita akan belajar bagaimana cara menggunakan Storyboard pada SwiftUI.

  1. Silakan buat file Storyboard baru dengan cara klik File -> New -> File -> lalu pilih Storyboard.

  2. Ubah Storyboard ID pada "Identity Inspector", menjadi "ExampleVC" misalnya.

    Storyboard IDStoryboard ID

  3. Lalu centang "Is Initial View Controller" pada "Attributes Inspector", dan silakan ubah UI dari View Controller menjadi apa aja yang kamu mau.

    StoryboardStoryboard

  4. Kemudian, ubah code pada file "ContentView.swift" menjadi seperti ini :

Menggunakan Storyboard di SwiftUI
icon
1import SwiftUI
2
3struct ContentView: View {
4 @State private var isPresented: Bool = false
5
6 var body: some View {
7 VStack {
8 Button("Show Storyboard") {
9 isPresented = true
10 }
11 }
12 .sheet(isPresented: $isPresented, content: {
13 ExampleVC()
14 })
15 }
16}
17
18struct ExampleVC: UIViewControllerRepresentable {
19 func makeUIViewController(context: Context) -> some UIViewController {
21 let storyboard = UIStoryboard(name: "Storyboard", bundle: Bundle.main)
22 let vc = storyboard.instantiateViewController(identifier: "ExampleVC")
24 return vc
25 }
26
27 func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
28}

Yang perlu diperhatikan dari code di atas adalah bagian ini :

icon
1let storyboard = UIStoryboard(name: "Storyboard", bundle: Bundle.main)
  1. Silakan ganti "Storyboard" dengan nama dari file Storyboard kamu.

icon
1let vc = storyboard.instantiateViewController(identifier: "ExampleVC")
  1. Ganti juga "ExampleVC" sesuai dengan Storyboard ID yang kamu berikan pada step 2 di atas.

Dan jika kita jalankan code-nya maka hasilnya akan seperti ini :

Menggunakan Storyboard di SwiftUIMenggunakan Storyboard di SwiftUI


Oke mungkin itu saja yang bisa saya bagikan kali ini, kalau kamu merasa artikel ini bermanfaat silakan Like & Share artikel ini ke teman-teman kamu atau jika kamu punya pertanyaan, tulis aja di kolom komentar, Thank you! 😁 🙏

iOS Development
Swift
SwiftUI
UIKit

Written by :
Alfin Syahruddin
Developer · Stock Trader · Libertarian · Freethinker

Always open to new ideas. 🕊️

Loading...

Related articles

Articles that you might want to read.

hero image
Integrasi SwiftUI ke dalam UIKit

Tutorial penggunaan UIHostingController.

18 April 2022 · 6 Minutes
iOS Development
Swift
SwiftUI
UIKit
hero image
CI/CD aplikasi iOS dengan Fastlane dan Github Actions

Otomatisasi build, testing, screenshot, dan deployment aplikasi iOS ke Testflight & AppStore.

24 September 2023 · 9 Minutes
iOS Development
CI/CD
Fastlane
Github Actions
hero image
DocC Tutorial

Dokumentasikan project-mu dengan DocC!

19 March 2023 · 8 Minutes
iOS Development
Swift
DocC
CI/CD
All rights reserved © Alfin Syahruddin · 2019
RSS Feed