Skip to content

Commit c04cfa7

Browse files
Merge pull request #1 from swiftcomponentsio/input-field
Changed the appearance of InputField components
2 parents 954662c + 92dc106 commit c04cfa7

File tree

6 files changed

+119
-261
lines changed

6 files changed

+119
-261
lines changed

App/SwiftComponentsApp/SwiftComponentsApp/Previews/InputFieldPreview.swift

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import Observation
12
import SwiftComponents
23
import SwiftUI
34
import UIKit
@@ -10,11 +11,14 @@ struct InputFieldPreview: View {
1011
@State private var text: String = ""
1112
@FocusState private var isFocused: Bool
1213

14+
private let inputField = UKInputField()
15+
private let inputFieldDelegate = InputFieldDelegate()
16+
1317
var body: some View {
1418
VStack {
1519
PreviewWrapper(title: "UIKit") {
1620
UKComponentPreview(model: self.model) {
17-
UKInputField(model: self.model)
21+
self.inputField
1822
}
1923
}
2024
PreviewWrapper(title: "SwiftUI") {
@@ -59,11 +63,7 @@ struct InputFieldPreview: View {
5963
return self.model.placeholder.isNotNilAndEmpty
6064
},
6165
set: { newValue in
62-
if newValue {
63-
self.model.placeholder = "Placeholder"
64-
} else {
65-
self.model.placeholder = nil
66-
}
66+
self.model.placeholder = newValue ? "Placeholder" : nil
6767
}
6868
))
6969
Toggle("Required", isOn: self.$model.isRequired)
@@ -82,11 +82,44 @@ struct InputFieldPreview: View {
8282
title: "Tint Color",
8383
selection: self.$model.tintColor
8484
)
85+
Toggle("Title", isOn: .init(
86+
get: {
87+
return self.model.title.isNotNilAndEmpty
88+
},
89+
set: { newValue in
90+
self.model.title = newValue ? "Title" : nil
91+
}
92+
))
93+
}
94+
}
95+
.onAppear {
96+
self.inputField.textField.delegate = self.inputFieldDelegate
97+
}
98+
.toolbar {
99+
ToolbarItem(placement: .primaryAction) {
100+
if self.inputFieldDelegate.isEditing || self.isFocused {
101+
Button("Hide Keyboard") {
102+
self.isFocused = false
103+
self.inputField.resignFirstResponder()
104+
}
105+
}
85106
}
86107
}
87108
}
88109
}
89110

111+
@Observable
112+
private final class InputFieldDelegate: NSObject, UITextFieldDelegate {
113+
var isEditing: Bool = false
114+
115+
func textFieldDidBeginEditing(_ textField: UITextField) {
116+
self.isEditing = true
117+
}
118+
func textFieldDidEndEditing(_ textField: UITextField) {
119+
self.isEditing = false
120+
}
121+
}
122+
90123
#Preview {
91124
InputFieldPreview()
92125
}

Sources/SwiftComponents/InputField/Models/InputFieldTitlePosition.swift

Lines changed: 0 additions & 4 deletions
This file was deleted.

Sources/SwiftComponents/InputField/Models/InputFieldVM.swift

Lines changed: 28 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public struct InputFieldVM: ComponentVM {
1313

1414
/// The corner radius of the input field.
1515
///
16-
/// /// Defaults to `.medium`.
16+
/// Defaults to `.medium`.
1717
public var cornerRadius: ComponentRadius = .medium
1818

1919
/// The font used for the input field's text.
@@ -65,7 +65,7 @@ public struct InputFieldVM: ComponentVM {
6565
public var tintColor: UniversalColor = .accent
6666

6767
/// The title displayed on the input field.
68-
public var title: String = ""
68+
public var title: String?
6969

7070
/// Initializes a new instance of `InputFieldVM` with default values.
7171
public init() {}
@@ -88,6 +88,13 @@ extension InputFieldVM {
8888
return UniversalFont.Component.large
8989
}
9090
}
91+
var height: CGFloat {
92+
return switch self.size {
93+
case .small: 40
94+
case .medium: 60
95+
case .large: 80
96+
}
97+
}
9198
var horizontalPadding: CGFloat {
9299
switch self.cornerRadius {
93100
case .none, .small, .medium, .large, .custom:
@@ -96,6 +103,9 @@ extension InputFieldVM {
96103
return 16
97104
}
98105
}
106+
var spacing: CGFloat {
107+
return self.title.isNotNilAndEmpty ? 12 : 0
108+
}
99109
var backgroundColor: UniversalColor {
100110
if let color {
101111
return color.main.withOpacity(0.25)
@@ -118,56 +128,6 @@ extension InputFieldVM {
118128
var placeholderColor: UniversalColor {
119129
return self.foregroundColor.withOpacity(self.isEnabled ? 0.7 : 0.3)
120130
}
121-
func titleColor(for position: InputFieldTitlePosition) -> UniversalColor {
122-
switch position {
123-
case .top:
124-
return self.foregroundColor
125-
case .center:
126-
return self.foregroundColor.withOpacity(self.isEnabled ? 0.8 : 0.45)
127-
}
128-
}
129-
func titleFont(for position: InputFieldTitlePosition) -> UniversalFont {
130-
switch position {
131-
case .top:
132-
return self.preferredFont.withRelativeSize(-1)
133-
case .center:
134-
let relativePadding: CGFloat = switch self.size {
135-
case .small: 1.5
136-
case .medium: 2
137-
case .large: 3
138-
}
139-
return self.preferredFont.withRelativeSize(relativePadding)
140-
}
141-
}
142-
}
143-
144-
// MARK: - Layout Helpers
145-
146-
extension InputFieldVM {
147-
var inputFieldTopPadding: CGFloat {
148-
switch self.size {
149-
case .small: 30
150-
case .medium: 34
151-
case .large: 38
152-
}
153-
}
154-
var inputFieldHeight: CGFloat {
155-
switch self.size {
156-
case .small: 26
157-
case .medium: 28
158-
case .large: 30
159-
}
160-
}
161-
var verticalPadding: CGFloat {
162-
switch self.size {
163-
case .small: 12
164-
case .medium: 14
165-
case .large: 16
166-
}
167-
}
168-
var height: CGFloat {
169-
return self.inputFieldHeight + self.inputFieldTopPadding + self.verticalPadding
170-
}
171131
}
172132

173133
// MARK: - UIKit Helpers
@@ -182,13 +142,17 @@ extension InputFieldVM {
182142
.foregroundColor: self.placeholderColor.uiColor
183143
])
184144
}
185-
func nsAttributedTitle(for position: InputFieldTitlePosition) -> NSAttributedString {
145+
var nsAttributedTitle: NSAttributedString? {
146+
guard let title else {
147+
return nil
148+
}
149+
186150
let attributedString = NSMutableAttributedString()
187151
attributedString.append(NSAttributedString(
188-
string: self.title,
152+
string: title,
189153
attributes: [
190-
.font: self.titleFont(for: position).uiFont,
191-
.foregroundColor: self.titleColor(for: position).uiColor
154+
.font: self.preferredFont.uiFont,
155+
.foregroundColor: self.foregroundColor.uiColor
192156
]
193157
))
194158
if self.isRequired {
@@ -201,7 +165,7 @@ extension InputFieldVM {
201165
attributedString.append(NSAttributedString(
202166
string: "*",
203167
attributes: [
204-
.font: self.titleFont(for: position).uiFont,
168+
.font: self.preferredFont.uiFont,
205169
.foregroundColor: UniversalColor.danger.uiColor
206170
]
207171
))
@@ -211,36 +175,22 @@ extension InputFieldVM {
211175
func shouldUpdateLayout(_ oldModel: Self) -> Bool {
212176
return self.size != oldModel.size
213177
|| self.horizontalPadding != oldModel.horizontalPadding
178+
|| self.spacing != oldModel.spacing
179+
|| self.cornerRadius != oldModel.cornerRadius
214180
}
215181
}
216182

217-
// MARK: - UIKit Helpers
183+
// MARK: - SwiftUI Helpers
218184

219185
extension InputFieldVM {
220186
var autocorrectionType: UITextAutocorrectionType {
221187
return self.isAutocorrectionEnabled ? .yes : .no
222188
}
223-
func attributedTitle(
224-
for position: InputFieldTitlePosition
225-
) -> AttributedString {
226-
var attributedString = AttributedString()
227-
228-
var attributedTitle = AttributedString(self.title)
229-
attributedTitle.font = self.titleFont(for: position).font
230-
attributedTitle.foregroundColor = self.titleColor(for: position).uiColor
231-
attributedString.append(attributedTitle)
232-
233-
if self.isRequired {
234-
var space = AttributedString(" ")
235-
space.font = .systemFont(ofSize: 5)
236-
attributedString.append(space)
237-
238-
var requiredSign = AttributedString("*")
239-
requiredSign.font = self.titleFont(for: position).font
240-
requiredSign.foregroundColor = UniversalColor.danger.uiColor
241-
attributedString.append(requiredSign)
189+
var attributedTitle: AttributedString? {
190+
guard let nsAttributedTitle else {
191+
return nil
242192
}
243193

244-
return attributedString
194+
return AttributedString(nsAttributedTitle)
245195
}
246196
}

Sources/SwiftComponents/InputField/SUInputField.swift

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,6 @@ public struct SUInputField<FocusValue: Hashable>: View {
2727

2828
@Environment(\.colorScheme) private var colorScheme
2929

30-
private var titlePosition: InputFieldTitlePosition {
31-
if self.model.placeholder.isNilOrEmpty,
32-
self.text.isEmpty,
33-
self.globalFocus != self.localFocus {
34-
return .center
35-
} else {
36-
return .top
37-
}
38-
}
39-
4030
// MARK: Initialization
4131

4232
/// Initializer.
@@ -60,19 +50,14 @@ public struct SUInputField<FocusValue: Hashable>: View {
6050
// MARK: Body
6151

6252
public var body: some View {
63-
ZStack(alignment: Alignment(
64-
horizontal: .leading,
65-
vertical: self.titlePosition == .top ? .top : .center
66-
)) {
67-
Text(self.model.attributedTitle(for: self.titlePosition))
68-
.font(self.model.titleFont(for: self.titlePosition).font)
69-
.foregroundStyle(
70-
self.model
71-
.titleColor(for: self.titlePosition)
72-
.color(for: self.colorScheme)
73-
)
74-
.padding(.top, self.titlePosition == .top ? self.model.verticalPadding : 0)
75-
.animation(.linear(duration: 0.1), value: self.titlePosition)
53+
HStack(spacing: self.model.spacing) {
54+
if let title = self.model.attributedTitle {
55+
Text(title)
56+
.font(self.model.preferredFont.font)
57+
.foregroundStyle(
58+
self.model.foregroundColor.color(for: self.colorScheme)
59+
)
60+
}
7661

7762
Group {
7863
if self.model.isSecureInput {
@@ -96,11 +81,9 @@ public struct SUInputField<FocusValue: Hashable>: View {
9681
.submitLabel(self.model.submitType.submitLabel)
9782
.autocorrectionDisabled(!self.model.isAutocorrectionEnabled)
9883
.textInputAutocapitalization(self.model.autocapitalization.textInputAutocapitalization)
99-
.frame(height: self.model.inputFieldHeight)
100-
.padding(.bottom, self.model.verticalPadding)
101-
.padding(.top, self.model.inputFieldTopPadding)
10284
}
10385
.padding(.horizontal, self.model.horizontalPadding)
86+
.frame(height: self.model.height)
10487
.background(self.model.backgroundColor.color(for: self.colorScheme))
10588
.onTapGesture {
10689
self.globalFocus = self.localFocus
@@ -110,11 +93,6 @@ public struct SUInputField<FocusValue: Hashable>: View {
11093
cornerRadius: self.model.cornerRadius.value()
11194
)
11295
)
113-
.onChange(of: self.globalFocus) { _ in
114-
// NOTE: Workaround to force `globalFocus` value update properly
115-
// Without this workaround the title position changes to `center`
116-
// when the text is cleared
117-
}
11896
}
11997
}
12098

0 commit comments

Comments
 (0)