Skip to content

Commit 2f69aac

Browse files
Feature/multiline text (#33)
* Working wip * Fix padding for topLine * Set version to 15.0 * Update swift-tools-version
1 parent bd0a3ce commit 2f69aac

File tree

5 files changed

+113
-46
lines changed

5 files changed

+113
-46
lines changed

Example/FloatingLabelTextFieldSwiftUI.xcodeproj/project.pbxproj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@
476476
buildSettings = {
477477
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
478478
INFOPLIST_FILE = FloatingLabelTextFieldSwiftUI/Info.plist;
479-
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
479+
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
480480
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
481481
MODULE_NAME = ExampleApp;
482482
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
@@ -492,7 +492,7 @@
492492
buildSettings = {
493493
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
494494
INFOPLIST_FILE = FloatingLabelTextFieldSwiftUI/Info.plist;
495-
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
495+
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
496496
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
497497
MODULE_NAME = ExampleApp;
498498
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
@@ -515,6 +515,7 @@
515515
"$(inherited)",
516516
);
517517
INFOPLIST_FILE = Tests/Info.plist;
518+
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
518519
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
519520
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
520521
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -533,6 +534,7 @@
533534
"$(inherited)",
534535
);
535536
INFOPLIST_FILE = Tests/Info.plist;
537+
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
536538
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
537539
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
538540
PRODUCT_NAME = "$(TARGET_NAME)";

Example/FloatingLabelTextFieldSwiftUI/ContentView.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ struct ContentView: View {
2626
@State private var showDatePicker: Bool = false
2727

2828
@State private var isPasswordShow: Bool = false
29+
30+
@State private var notes: String = ""
2931

3032
private var selectedDate: Binding<Date> {
3133
Binding<Date>(get: { self.date}, set : {
@@ -105,6 +107,12 @@ struct ContentView: View {
105107
.keyboardType(.emailAddress)
106108
.modifier(ThemeTextField())
107109

110+
111+
FloatingLabelTextField($notes, placeholder: "Notes", axis: .vertical)
112+
.spaceBetweenTitleText(8.0)
113+
.lineLimit(5)
114+
.frame(minHeight: 80)
115+
108116
FloatingLabelTextField($password, placeholder: "Password", editingChanged: { (isChanged) in
109117

110118
}) {
@@ -142,7 +150,7 @@ struct ContentView: View {
142150

143151
}) {
144152
Text("Create")
145-
}
153+
}
146154
.buttonStyle(CreateButtonStyle())
147155
Spacer()
148156
}

Package.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
// swift-tools-version:5.1
1+
// swift-tools-version:5.7
22
// The swift-tools-version declares the minimum version of Swift required to build this package.
33

44
import PackageDescription
55

66
let package = Package(
77
name: "FloatingLabelTextFieldSwiftUI",
88
platforms: [
9-
.iOS(.v13),
10-
.macOS(.v10_15),
11-
.tvOS(.v13)
9+
.iOS(.v15),
10+
.macOS(.v12),
11+
.tvOS(.v15)
1212
],
1313
products: [
1414
// Products define the executables and libraries produced by a package, and make them visible to other packages.

Sources/FloatingLabelTextFieldSwiftUI/FloatingLabelTextField.swift

Lines changed: 94 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99
import SwiftUI
1010

1111
//MARK: FloatingLabelTextField Style Protocol
12-
@available(iOS 13.0, *)
12+
@available(iOS 15.0, *)
1313
public protocol FloatingLabelTextFieldStyle {
1414
func body(content: FloatingLabelTextField) -> FloatingLabelTextField
1515
}
1616

1717
//MARK: FloatingLabelTextField View
18-
@available(iOS 13.0, *)
18+
@available(iOS 15.0, *)
1919
public struct FloatingLabelTextField: View {
2020

2121
//MARK: Binding Property
@@ -36,20 +36,24 @@ public struct FloatingLabelTextField: View {
3636

3737
@State var isShowError: Bool = false
3838

39-
@State fileprivate var isFocused: Bool = false
39+
@FocusState fileprivate var isFocused: Bool
40+
@State private var textFieldHeight: CGFloat = 0.0
4041

4142
//MARK: Observed Object
4243
@ObservedObject private var notifier = FloatingLabelTextFieldNotifier()
4344

4445
//MARK: Properties
46+
private let axis: Axis
4547
private var placeholderText: String = ""
4648
private var editingChanged: (Bool) -> () = { _ in }
4749
private var commit: () -> () = { }
4850

4951
//MARK: Init
50-
public init(_ text: Binding<String>, validtionChecker: Binding<Bool>? = nil, placeholder: String = "", editingChanged: @escaping (Bool)->() = { _ in }, commit: @escaping ()->() = { }) {
52+
public init(_ text: Binding<String>, validtionChecker: Binding<Bool>? = nil, placeholder: String = "",
53+
axis: Axis = .horizontal, editingChanged: @escaping (Bool)->() = { _ in }, commit: @escaping ()->() = { }) {
5154
self._textFieldValue = text
5255
self.placeholderText = placeholder
56+
self.axis = axis
5357
self.editingChanged = editingChanged
5458
self.commit = commit
5559
self._validtionChecker = validtionChecker ?? Binding.constant(false)
@@ -99,39 +103,81 @@ public struct FloatingLabelTextField: View {
99103
.foregroundColor((self.currentError.condition || !notifier.isShowError) ? (isSelected ? notifier.selectedTextColor : notifier.textColor) : notifier.errorColor)
100104

101105
} else {
102-
TextField("", text: $textFieldValue.animation(), onEditingChanged: { (isChanged) in
103-
withAnimation {
106+
if #available(iOS 16.0, *) {
107+
TextField("", text: $textFieldValue.animation(), axis: axis)
108+
.focused($isFocused)
109+
.onChange(of: isFocused, perform: { (isChanged) in
110+
withAnimation {
111+
DispatchQueue.main.async {
112+
self.isSelected = isChanged
113+
}
114+
}
115+
116+
DispatchQueue.main.async {
117+
self.isShowError = self.notifier.isRequiredField
118+
}
119+
120+
self.validtionChecker = self.currentError.condition
121+
self.editingChanged(isChanged)
122+
arrTextFieldEditActions = self.notifier.arrTextFieldEditActions
123+
})
124+
.onSubmit({
125+
self.isShowError = self.notifier.isRequiredField
126+
self.validtionChecker = self.currentError.condition
127+
self.commit()
128+
arrTextFieldEditActions = []
129+
})
130+
.lineLimit(notifier.lineLimit)
131+
.disabled(self.notifier.disabled)
132+
.allowsHitTesting(self.notifier.allowsHitTesting)
133+
.multilineTextAlignment(notifier.textAlignment)
134+
.font(notifier.font)
135+
.foregroundColor((self.currentError.condition || !notifier.isShowError) ? (isSelected ? notifier.selectedTextColor : notifier.textColor) : notifier.errorColor)
136+
.background(
137+
GeometryReader(content: set(geometry:))
138+
)
139+
} else {
140+
TextField("", text: $textFieldValue.animation(), onEditingChanged: { (isChanged) in
141+
withAnimation {
142+
DispatchQueue.main.async {
143+
self.isSelected = isChanged
144+
}
145+
}
146+
104147
DispatchQueue.main.async {
105-
self.isSelected = isChanged
148+
self.isShowError = self.notifier.isRequiredField
106149
}
107-
}
108150

109-
DispatchQueue.main.async {
151+
self.validtionChecker = self.currentError.condition
152+
self.editingChanged(isChanged)
153+
arrTextFieldEditActions = self.notifier.arrTextFieldEditActions
154+
}, onCommit: {
110155
self.isShowError = self.notifier.isRequiredField
111-
}
112-
113-
self.validtionChecker = self.currentError.condition
114-
self.editingChanged(isChanged)
115-
arrTextFieldEditActions = self.notifier.arrTextFieldEditActions
116-
}, onCommit: {
117-
self.isShowError = self.notifier.isRequiredField
118-
self.validtionChecker = self.currentError.condition
119-
self.commit()
120-
arrTextFieldEditActions = []
121-
})
122-
.disabled(self.notifier.disabled)
123-
.allowsHitTesting(self.notifier.allowsHitTesting)
124-
.multilineTextAlignment(notifier.textAlignment)
125-
.font(notifier.font)
126-
.foregroundColor((self.currentError.condition || !notifier.isShowError) ? (isSelected ? notifier.selectedTextColor : notifier.textColor) : notifier.errorColor)
156+
self.validtionChecker = self.currentError.condition
157+
self.commit()
158+
arrTextFieldEditActions = []
159+
})
160+
.disabled(self.notifier.disabled)
161+
.allowsHitTesting(self.notifier.allowsHitTesting)
162+
.multilineTextAlignment(notifier.textAlignment)
163+
.font(notifier.font)
164+
.foregroundColor((self.currentError.condition || !notifier.isShowError) ? (isSelected ? notifier.selectedTextColor : notifier.textColor) : notifier.errorColor)
165+
}
127166
}
128167
}
129168
}
130169

170+
private func set(geometry: GeometryProxy) -> some View {
171+
DispatchQueue.main.async {
172+
self.textFieldHeight = geometry.size.height
173+
}
174+
return Color.clear
175+
}
176+
131177
// MARK: Top error and title lable view
132178
var topTitleLable: some View {
133179
Text((self.currentError.condition || !notifier.isShowError) ? placeholderText : self.currentError.errorMessage)
134-
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: notifier.textAlignment.getAlignment())
180+
.frame(alignment: notifier.textAlignment.getAlignment())
135181
.animation(.default)
136182
.foregroundColor((self.currentError.condition || !notifier.isShowError) ? (self.isSelected ? notifier.selectedTitleColor : notifier.titleColor) : notifier.errorColor)
137183
.font(notifier.titleFont)
@@ -153,7 +199,9 @@ public struct FloatingLabelTextField: View {
153199
self.topTitleLable.padding(.bottom, CGFloat(notifier.spaceBetweenTitleText)).opacity(1)
154200

155201
} else {
156-
self.topTitleLable.padding(.bottom, CGFloat(!textFieldValue.isEmpty ? notifier.spaceBetweenTitleText : 0)).opacity((textFieldValue.isEmpty) ? 0 : 1)
202+
let padding = notifier.spaceBetweenTitleText + (axis == .vertical ? textFieldHeight : 0)
203+
self.topTitleLable.padding(.bottom, !textFieldValue.isEmpty ? padding : 0)
204+
.opacity((textFieldValue.isEmpty) ? 0 : 1)
157205
}
158206

159207
HStack {
@@ -179,20 +227,20 @@ public struct FloatingLabelTextField: View {
179227
}
180228

181229
}
182-
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .bottomLeading)
230+
.frame(alignment: .bottomLeading)
183231
}
184232
}
185233

186234
//MARK: FloatingLabelTextField Style Funcation
187-
@available(iOS 13.0, *)
235+
@available(iOS 15.0, *)
188236
extension FloatingLabelTextField {
189237
public func floatingStyle<S>(_ style: S) -> some View where S: FloatingLabelTextFieldStyle {
190238
return style.body(content: self)
191239
}
192240
}
193241

194242
//MARK: View Property Funcation
195-
@available(iOS 13.0, *)
243+
@available(iOS 15.0, *)
196244
extension FloatingLabelTextField {
197245
/// Sets the left view.
198246
public func leftView<LRView: View>(@ViewBuilder _ view: @escaping () -> LRView) -> Self {
@@ -208,7 +256,7 @@ extension FloatingLabelTextField {
208256
}
209257

210258
//MARK: Text Property Funcation
211-
@available(iOS 13.0, *)
259+
@available(iOS 15.0, *)
212260
extension FloatingLabelTextField {
213261
/// Sets the alignment for text.
214262
public func textAlignment(_ alignment: TextAlignment) -> Self {
@@ -235,8 +283,16 @@ extension FloatingLabelTextField {
235283
}
236284
}
237285

286+
@available(iOS 16.0, *)
287+
extension FloatingLabelTextField {
288+
public func lineLimit(_ limit: Int) -> Self {
289+
notifier.lineLimit = limit
290+
return self
291+
}
292+
}
293+
238294
//MARK: Line Property Funcation
239-
@available(iOS 13.0, *)
295+
@available(iOS 15.0, *)
240296
extension FloatingLabelTextField {
241297
/// Sets the line height.
242298
public func lineHeight(_ height: CGFloat) -> Self {
@@ -264,7 +320,7 @@ extension FloatingLabelTextField {
264320
}
265321

266322
//MARK: Title Property Funcation
267-
@available(iOS 13.0, *)
323+
@available(iOS 15.0, *)
268324
extension FloatingLabelTextField {
269325
/// Sets the title color.
270326
public func titleColor(_ color: Color) -> Self {
@@ -292,7 +348,7 @@ extension FloatingLabelTextField {
292348
}
293349

294350
//MARK: Text Property Funcation
295-
@available(iOS 13.0, *)
351+
@available(iOS 15.0, *)
296352
extension FloatingLabelTextField {
297353
/// Sets the text color.
298354
public func textColor(_ color: Color) -> Self {
@@ -314,7 +370,7 @@ extension FloatingLabelTextField {
314370
}
315371

316372
//MARK: Placeholder Property Funcation
317-
@available(iOS 13.0, *)
373+
@available(iOS 15.0, *)
318374
extension FloatingLabelTextField {
319375
/// Sets the placeholder color.
320376
public func placeholderColor(_ color: Color) -> Self {
@@ -330,7 +386,7 @@ extension FloatingLabelTextField {
330386
}
331387

332388
//MARK: Error Property Funcation
333-
@available(iOS 13.0, *)
389+
@available(iOS 15.0, *)
334390
extension FloatingLabelTextField {
335391
/// Sets the is show error message.
336392
public func isShowError(_ show: Bool) -> Self {
@@ -365,7 +421,7 @@ extension FloatingLabelTextField {
365421
}
366422

367423
//MARK: Text Field Editing Funcation
368-
@available(iOS 13.0, *)
424+
@available(iOS 15.0, *)
369425
extension FloatingLabelTextField {
370426
/// Disable text field editing action. Like cut, copy, past, all etc.
371427
public func addDisableEditingAction(_ actions: [TextFieldEditActions]) -> Self {
@@ -375,7 +431,7 @@ extension FloatingLabelTextField {
375431
}
376432

377433
//MARK: Animation Style Funcation
378-
@available(iOS 13.0, *)
434+
@available(iOS 15.0, *)
379435
extension FloatingLabelTextField {
380436
/// Enable the placeholder label when the textfield is focused.
381437
public func enablePlaceholderOnFocus(_ isEanble: Bool) -> Self {

Sources/FloatingLabelTextFieldSwiftUI/ObservableObject/ObservableObject.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,14 @@ class FloatingLabelTextFieldNotifier: ObservableObject {
3333
@Published var textColor: Color = .black
3434
@Published var selectedTextColor: Color = .blue
3535
@Published var font: Font = .system(size: 15)
36+
@Published var lineLimit: Int = 1
3637

3738
//MARK: Placeholder Properties
3839
@Published var placeholderColor: Color = .gray
3940
@Published var placeholderFont: Font = .system(size: 15)
4041

4142
//MARK: Other Properties
42-
@Published var spaceBetweenTitleText: Double = 15
43+
@Published var spaceBetweenTitleText: Double = 30
4344
@Published var isSecureTextEntry: Bool = false
4445
@Published var disabled: Bool = false
4546
@Published var allowsHitTesting: Bool = true

0 commit comments

Comments
 (0)