Skip to content

Commit 0946fff

Browse files
committed
Add swiftui.md
1 parent e7c91eb commit 0946fff

File tree

1 file changed

+218
-0
lines changed

1 file changed

+218
-0
lines changed

libs/notes/swiftui.md

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
This is from the Apple UIKit docs
2+
(https://developer.apple.com/documentation/uikit):
3+
4+
"UIKit works seamlessly with the SwiftUI framework, so you can implement parts of
5+
your UIKit app in SwiftUI or mix interface elements between the two frameworks.
6+
For example, you can place UIKit views and view controllers inside SwiftUI
7+
views, and vice versa."
8+
9+
Could you show me how to do this?
10+
11+
---
12+
13+
Sure, I can help with that. You can use UIKit views in SwiftUI by conforming to
14+
the `UIViewRepresentable` protocol, and you can use SwiftUI views in UIKit by
15+
wrapping them in a `UIHostingController`. Here's an example of each.
16+
17+
## UIKit views in SwiftUI
18+
19+
First, let's show how to use a UIKit view in a SwiftUI view. We'll use a
20+
`UISlider` as an example:
21+
22+
```swift
23+
import SwiftUI
24+
import UIKit
25+
26+
struct UIKitSlider: UIViewRepresentable {
27+
@Binding var value: Float
28+
29+
func makeUIView(context: Context) -> UISlider {
30+
let slider = UISlider(frame: .zero)
31+
slider.value = value
32+
slider.addTarget(context.coordinator, action: #selector(Coordinator.valueChanged(_:)), for: .valueChanged)
33+
return slider
34+
}
35+
36+
func updateUIView(_ uiView: UISlider, context: Context) {
37+
uiView.value = value
38+
}
39+
40+
func makeCoordinator() -> Coordinator {
41+
Coordinator(value: $value)
42+
}
43+
44+
class Coordinator: NSObject {
45+
var value: Binding<Float>
46+
47+
init(value: Binding<Float>) {
48+
self.value = value
49+
}
50+
51+
@objc func valueChanged(_ sender: UISlider) {
52+
self.value.wrappedValue = sender.value
53+
}
54+
}
55+
}
56+
```
57+
58+
You can then use `UIKitSlider` in your SwiftUI views like this:
59+
60+
```swift
61+
struct ContentView: View {
62+
@State private var sliderValue: Float = 0.5
63+
64+
var body: some View {
65+
VStack {
66+
Text("Slider Value: \(sliderValue)")
67+
UIKitSlider(value: $sliderValue)
68+
}
69+
}
70+
}
71+
```
72+
73+
## SwiftUI views in UIKit
74+
75+
You can also embed SwiftUI views in your UIKit code by using a
76+
UIHostingController. Here's an example of how to do that:
77+
78+
```swift
79+
import SwiftUI
80+
import UIKit
81+
82+
struct SwiftUIView: View {
83+
var body: some View {
84+
Text("Hello, SwiftUI!")
85+
.padding()
86+
.background(Color.green)
87+
.foregroundColor(.white)
88+
.cornerRadius(10)
89+
}
90+
}
91+
92+
class ViewController: UIViewController {
93+
override func viewDidLoad() {
94+
super.viewDidLoad()
95+
96+
let hostingController = UIHostingController(rootView: SwiftUIView())
97+
addChild(hostingController)
98+
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
99+
view.addSubview(hostingController.view)
100+
101+
NSLayoutConstraint.activate([
102+
hostingController.view.centerXAnchor.constraint(equalTo: view.centerXAnchor),
103+
hostingController.view.centerYAnchor.constraint(equalTo: view.centerYAnchor),
104+
])
105+
}
106+
}
107+
```
108+
109+
In this example, `SwiftUIView` is embedded in a `ViewController`. It will
110+
display a text "Hello, SwiftUI!" with a green background in the center of the
111+
screen.
112+
113+
---
114+
115+
Could you show me how to place a UIViewController (instead of just a UIView)
116+
inside SwiftUI views?
117+
118+
---
119+
120+
## UIKit UIViewControllers in SwiftUI
121+
122+
Yes, absolutely! In order to use a `UIViewController` in a SwiftUI view, you
123+
need to use the `UIViewControllerRepresentable` protocol. Here's an example of
124+
how to use a `UIImagePickerController` (which is a subclass of
125+
`UIViewController`) inside a SwiftUI view:
126+
127+
```swift
128+
import SwiftUI
129+
import UIKit
130+
131+
struct ImagePicker: UIViewControllerRepresentable {
132+
@Binding var selectedImage: UIImage?
133+
@Environment(\.presentationMode) private var presentationMode
134+
135+
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
136+
var parent: ImagePicker
137+
138+
init(_ parent: ImagePicker) {
139+
self.parent = parent
140+
}
141+
142+
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
143+
if let uiImage = info[.originalImage] as? UIImage {
144+
parent.selectedImage = uiImage
145+
}
146+
147+
parent.presentationMode.wrappedValue.dismiss()
148+
}
149+
}
150+
151+
func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
152+
let picker = UIImagePickerController()
153+
picker.delegate = context.coordinator
154+
return picker
155+
}
156+
157+
func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {
158+
// update function implementation
159+
}
160+
161+
func makeCoordinator() -> Coordinator {
162+
Coordinator(self)
163+
}
164+
}
165+
```
166+
167+
You can then use `ImagePicker` in your SwiftUI views like this:
168+
169+
```swift
170+
struct ContentView: View {
171+
@State private var isShowingImagePicker = false
172+
@State private var selectedImage: UIImage?
173+
174+
var body: some View {
175+
VStack {
176+
Button(action: {
177+
isShowingImagePicker = true
178+
}) {
179+
Text("Open Image Picker")
180+
}
181+
182+
selectedImage.map { Image(uiImage: $0).resizable().scaledToFit() }
183+
}
184+
.sheet(isPresented: $isShowingImagePicker) {
185+
ImagePicker(selectedImage: $selectedImage)
186+
}
187+
}
188+
}
189+
```
190+
191+
In this example, when you press the "Open Image Picker" button, it opens up an
192+
image picker. Once an image is selected, it is displayed on the screen.
193+
194+
---
195+
196+
Does SwiftUI render to UIKit on mobile and AppKit on desktop?
197+
198+
---
199+
200+
In essence, yes. SwiftUI operates at a higher level of abstraction than UIKit or
201+
AppKit. When you define interfaces in SwiftUI, you describe them in a
202+
platform-agnostic way, but when those interfaces are rendered on a particular
203+
platform, SwiftUI uses the native rendering engine of that platform.
204+
205+
On iOS and iPadOS, SwiftUI uses UIKit (more specifically, underlying
206+
technologies like Core Animation and Core Graphics) for rendering. On macOS,
207+
SwiftUI uses AppKit. Similarly, on watchOS, it uses WatchKit, and on tvOS, it
208+
uses TVUIKit.
209+
210+
However, it's important to note that SwiftUI doesn't directly convert your
211+
SwiftUI views into UIKit or AppKit components. Instead, SwiftUI works directly
212+
with the underlying rendering and layout engines to create UIs that match the
213+
capabilities and conventions of each platform.
214+
215+
This means, for instance, that a SwiftUI `Button` does not become a `UIButton`
216+
on iOS or a `NSButton` on macOS. It remains a SwiftUI `Button`, but it is drawn
217+
and behaves appropriately for the platform it's running on, thanks to the
218+
rendering done by SwiftUI using the native technologies.

0 commit comments

Comments
 (0)