@@ -10,17 +10,15 @@ import UIKit
1010
1111struct UIKitTextEditor : View {
1212 @Binding var text : String
13+ @Environment ( \. uiKitTextEditorFocusBinding) private var focusBinding
1314 @State private var minHeight = TextEditorMetrics . font. lineHeight
14- private let focusBinding : Binding < Bool >
1515 private let placeholder : String
1616
1717 init (
1818 text: Binding < String > ,
19- isFocused: Binding < Bool > ,
2019 placeholder: String = " "
2120 ) {
2221 self . _text = text
23- self . focusBinding = isFocused
2422 self . placeholder = placeholder
2523 }
2624
@@ -33,22 +31,66 @@ struct UIKitTextEditor: View {
3331 )
3432 . frame ( maxWidth: . infinity, minHeight: minHeight)
3533 }
34+
35+ // 각 메서드 내에 있는 `.focused()`의 정체
36+ // 해당 .focused()는 SwiftUI의 모디파이어
37+ // 이 뷰를 SwiftUI 포커스 시스템에 실제 포커스 타겟으로 등록해주는 역할을 함
38+
39+ func focused( _ condition: FocusState < Bool > . Binding ) -> some View {
40+ modifier ( TextEditorFocusModifier (
41+ focusBinding: Binding ( condition)
42+ ) )
43+ . focused ( condition)
44+ }
45+
46+ func focused< Value> (
47+ _ binding: FocusState < Value > . Binding ,
48+ equals value: Value
49+ ) -> some View where Value: Hashable & ExpressibleByNilLiteral {
50+ modifier ( TextEditorFocusModifier (
51+ focusBinding: Binding (
52+ binding,
53+ equals: value
54+ )
55+ ) )
56+ . focused ( binding, equals: value)
57+ }
3658}
3759
3860private enum TextEditorMetrics {
3961 static let font = UIFont . preferredFont ( forTextStyle: . body)
4062}
4163
64+ private struct TextEditorFocusModifier : ViewModifier {
65+ let focusBinding : Binding < Bool >
66+
67+ func body( content: Content ) -> some View {
68+ content
69+ . environment ( \. uiKitTextEditorFocusBinding, focusBinding)
70+ }
71+ }
72+
73+ private struct TextEditorFocusBindingKey : EnvironmentKey {
74+ static let defaultValue : Binding < Bool > ? = nil
75+ }
76+
77+ private extension EnvironmentValues {
78+ var uiKitTextEditorFocusBinding : Binding < Bool > ? {
79+ get { self [ TextEditorFocusBindingKey . self] }
80+ set { self [ TextEditorFocusBindingKey . self] = newValue }
81+ }
82+ }
83+
4284private struct UIKitTextEditorRepresentable : UIViewRepresentable {
4385 @Binding var text : String
4486 @Binding var minHeight : CGFloat
45- private let focusBinding : Binding < Bool >
87+ private let focusBinding : Binding < Bool > ?
4688 private let placeholder : String
4789
4890 init (
4991 text: Binding < String > ,
5092 minHeight: Binding < CGFloat > ,
51- focusBinding: Binding < Bool > ,
93+ focusBinding: Binding < Bool > ? ,
5294 placeholder: String
5395 ) {
5496 self . _text = text
@@ -90,13 +132,15 @@ private struct UIKitTextEditorRepresentable: UIViewRepresentable {
90132 context. coordinator. applyPlaceholderIfNeeded ( to: uiView)
91133
92134 DispatchQueue . main. async {
93- if focusBinding. wrappedValue {
94- if !uiView. isFirstResponder {
95- context. coordinator. preserveAncestorScrollOffset ( for: uiView)
96- uiView. becomeFirstResponder ( )
135+ if let focusBinding {
136+ if focusBinding. wrappedValue {
137+ if !uiView. isFirstResponder {
138+ context. coordinator. preserveAncestorScrollOffset ( for: uiView)
139+ uiView. becomeFirstResponder ( )
140+ }
141+ } else if uiView. isFirstResponder {
142+ uiView. resignFirstResponder ( )
97143 }
98- } else if uiView. isFirstResponder {
99- uiView. resignFirstResponder ( )
100144 }
101145 context. coordinator. updateHeight ( for: uiView)
102146 }
@@ -122,8 +166,8 @@ private struct UIKitTextEditorRepresentable: UIViewRepresentable {
122166 textView. textColor = . label
123167 }
124168
125- if ! parent. focusBinding. wrappedValue {
126- parent . focusBinding. wrappedValue = true
169+ if let focusBinding = parent. focusBinding , ! focusBinding. wrappedValue {
170+ focusBinding. wrappedValue = true
127171 }
128172
129173 restoreAncestorScrollOffsetIfNeeded ( )
@@ -145,8 +189,8 @@ private struct UIKitTextEditorRepresentable: UIViewRepresentable {
145189 }
146190
147191 func textViewDidEndEditing( _ textView: UITextView ) {
148- if parent. focusBinding. wrappedValue {
149- parent . focusBinding. wrappedValue = false
192+ if let focusBinding = parent. focusBinding , focusBinding. wrappedValue {
193+ focusBinding. wrappedValue = false
150194 }
151195
152196 applyPlaceholderIfNeeded ( to: textView)
@@ -199,6 +243,33 @@ private struct UIKitTextEditorRepresentable: UIViewRepresentable {
199243 }
200244}
201245
246+ private extension Binding where Value == Bool {
247+ init ( _ binding: FocusState < Bool > . Binding ) {
248+ self . init (
249+ get: { binding. wrappedValue } ,
250+ set: { binding. wrappedValue = $0 }
251+ )
252+ }
253+
254+ init < FocusedValue> (
255+ _ binding: FocusState < FocusedValue > . Binding ,
256+ equals value: FocusedValue
257+ ) where FocusedValue: Hashable & ExpressibleByNilLiteral {
258+ self . init (
259+ get: {
260+ binding. wrappedValue == value
261+ } ,
262+ set: { isFocused in
263+ if isFocused {
264+ binding. wrappedValue = value
265+ } else if binding. wrappedValue == value {
266+ binding. wrappedValue = nil
267+ }
268+ }
269+ )
270+ }
271+ }
272+
202273private extension UIView {
203274 var enclosingScrollView : UIScrollView ? {
204275 var currentSuperview = superview
0 commit comments