Skip to content

Commit

Permalink
Feature/multiline text (#33)
Browse files Browse the repository at this point in the history
* Working wip

* Fix padding for topLine

* Set version to 15.0

* Update swift-tools-version
  • Loading branch information
michallaskowski authored Dec 31, 2022
1 parent bd0a3ce commit 2f69aac
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = FloatingLabelTextFieldSwiftUI/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MODULE_NAME = ExampleApp;
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
Expand All @@ -492,7 +492,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = FloatingLabelTextFieldSwiftUI/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MODULE_NAME = ExampleApp;
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
Expand All @@ -515,6 +515,7 @@
"$(inherited)",
);
INFOPLIST_FILE = Tests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
Expand All @@ -533,6 +534,7 @@
"$(inherited)",
);
INFOPLIST_FILE = Tests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down
10 changes: 9 additions & 1 deletion Example/FloatingLabelTextFieldSwiftUI/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ struct ContentView: View {
@State private var showDatePicker: Bool = false

@State private var isPasswordShow: Bool = false

@State private var notes: String = ""

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


FloatingLabelTextField($notes, placeholder: "Notes", axis: .vertical)
.spaceBetweenTitleText(8.0)
.lineLimit(5)
.frame(minHeight: 80)

FloatingLabelTextField($password, placeholder: "Password", editingChanged: { (isChanged) in

}) {
Expand Down Expand Up @@ -142,7 +150,7 @@ struct ContentView: View {

}) {
Text("Create")
}
}
.buttonStyle(CreateButtonStyle())
Spacer()
}
Expand Down
8 changes: 4 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// swift-tools-version:5.1
// swift-tools-version:5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "FloatingLabelTextFieldSwiftUI",
platforms: [
.iOS(.v13),
.macOS(.v10_15),
.tvOS(.v13)
.iOS(.v15),
.macOS(.v12),
.tvOS(.v15)
],
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.
Expand Down
132 changes: 94 additions & 38 deletions Sources/FloatingLabelTextFieldSwiftUI/FloatingLabelTextField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
import SwiftUI

//MARK: FloatingLabelTextField Style Protocol
@available(iOS 13.0, *)
@available(iOS 15.0, *)
public protocol FloatingLabelTextFieldStyle {
func body(content: FloatingLabelTextField) -> FloatingLabelTextField
}

//MARK: FloatingLabelTextField View
@available(iOS 13.0, *)
@available(iOS 15.0, *)
public struct FloatingLabelTextField: View {

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

@State var isShowError: Bool = false

@State fileprivate var isFocused: Bool = false
@FocusState fileprivate var isFocused: Bool
@State private var textFieldHeight: CGFloat = 0.0

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

//MARK: Properties
private let axis: Axis
private var placeholderText: String = ""
private var editingChanged: (Bool) -> () = { _ in }
private var commit: () -> () = { }

//MARK: Init
public init(_ text: Binding<String>, validtionChecker: Binding<Bool>? = nil, placeholder: String = "", editingChanged: @escaping (Bool)->() = { _ in }, commit: @escaping ()->() = { }) {
public init(_ text: Binding<String>, validtionChecker: Binding<Bool>? = nil, placeholder: String = "",
axis: Axis = .horizontal, editingChanged: @escaping (Bool)->() = { _ in }, commit: @escaping ()->() = { }) {
self._textFieldValue = text
self.placeholderText = placeholder
self.axis = axis
self.editingChanged = editingChanged
self.commit = commit
self._validtionChecker = validtionChecker ?? Binding.constant(false)
Expand Down Expand Up @@ -99,39 +103,81 @@ public struct FloatingLabelTextField: View {
.foregroundColor((self.currentError.condition || !notifier.isShowError) ? (isSelected ? notifier.selectedTextColor : notifier.textColor) : notifier.errorColor)

} else {
TextField("", text: $textFieldValue.animation(), onEditingChanged: { (isChanged) in
withAnimation {
if #available(iOS 16.0, *) {
TextField("", text: $textFieldValue.animation(), axis: axis)
.focused($isFocused)
.onChange(of: isFocused, perform: { (isChanged) in
withAnimation {
DispatchQueue.main.async {
self.isSelected = isChanged
}
}

DispatchQueue.main.async {
self.isShowError = self.notifier.isRequiredField
}

self.validtionChecker = self.currentError.condition
self.editingChanged(isChanged)
arrTextFieldEditActions = self.notifier.arrTextFieldEditActions
})
.onSubmit({
self.isShowError = self.notifier.isRequiredField
self.validtionChecker = self.currentError.condition
self.commit()
arrTextFieldEditActions = []
})
.lineLimit(notifier.lineLimit)
.disabled(self.notifier.disabled)
.allowsHitTesting(self.notifier.allowsHitTesting)
.multilineTextAlignment(notifier.textAlignment)
.font(notifier.font)
.foregroundColor((self.currentError.condition || !notifier.isShowError) ? (isSelected ? notifier.selectedTextColor : notifier.textColor) : notifier.errorColor)
.background(
GeometryReader(content: set(geometry:))
)
} else {
TextField("", text: $textFieldValue.animation(), onEditingChanged: { (isChanged) in
withAnimation {
DispatchQueue.main.async {
self.isSelected = isChanged
}
}

DispatchQueue.main.async {
self.isSelected = isChanged
self.isShowError = self.notifier.isRequiredField
}
}

DispatchQueue.main.async {
self.validtionChecker = self.currentError.condition
self.editingChanged(isChanged)
arrTextFieldEditActions = self.notifier.arrTextFieldEditActions
}, onCommit: {
self.isShowError = self.notifier.isRequiredField
}

self.validtionChecker = self.currentError.condition
self.editingChanged(isChanged)
arrTextFieldEditActions = self.notifier.arrTextFieldEditActions
}, onCommit: {
self.isShowError = self.notifier.isRequiredField
self.validtionChecker = self.currentError.condition
self.commit()
arrTextFieldEditActions = []
})
.disabled(self.notifier.disabled)
.allowsHitTesting(self.notifier.allowsHitTesting)
.multilineTextAlignment(notifier.textAlignment)
.font(notifier.font)
.foregroundColor((self.currentError.condition || !notifier.isShowError) ? (isSelected ? notifier.selectedTextColor : notifier.textColor) : notifier.errorColor)
self.validtionChecker = self.currentError.condition
self.commit()
arrTextFieldEditActions = []
})
.disabled(self.notifier.disabled)
.allowsHitTesting(self.notifier.allowsHitTesting)
.multilineTextAlignment(notifier.textAlignment)
.font(notifier.font)
.foregroundColor((self.currentError.condition || !notifier.isShowError) ? (isSelected ? notifier.selectedTextColor : notifier.textColor) : notifier.errorColor)
}
}
}
}

private func set(geometry: GeometryProxy) -> some View {
DispatchQueue.main.async {
self.textFieldHeight = geometry.size.height
}
return Color.clear
}

// MARK: Top error and title lable view
var topTitleLable: some View {
Text((self.currentError.condition || !notifier.isShowError) ? placeholderText : self.currentError.errorMessage)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: notifier.textAlignment.getAlignment())
.frame(alignment: notifier.textAlignment.getAlignment())
.animation(.default)
.foregroundColor((self.currentError.condition || !notifier.isShowError) ? (self.isSelected ? notifier.selectedTitleColor : notifier.titleColor) : notifier.errorColor)
.font(notifier.titleFont)
Expand All @@ -153,7 +199,9 @@ public struct FloatingLabelTextField: View {
self.topTitleLable.padding(.bottom, CGFloat(notifier.spaceBetweenTitleText)).opacity(1)

} else {
self.topTitleLable.padding(.bottom, CGFloat(!textFieldValue.isEmpty ? notifier.spaceBetweenTitleText : 0)).opacity((textFieldValue.isEmpty) ? 0 : 1)
let padding = notifier.spaceBetweenTitleText + (axis == .vertical ? textFieldHeight : 0)
self.topTitleLable.padding(.bottom, !textFieldValue.isEmpty ? padding : 0)
.opacity((textFieldValue.isEmpty) ? 0 : 1)
}

HStack {
Expand All @@ -179,20 +227,20 @@ public struct FloatingLabelTextField: View {
}

}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .bottomLeading)
.frame(alignment: .bottomLeading)
}
}

//MARK: FloatingLabelTextField Style Funcation
@available(iOS 13.0, *)
@available(iOS 15.0, *)
extension FloatingLabelTextField {
public func floatingStyle<S>(_ style: S) -> some View where S: FloatingLabelTextFieldStyle {
return style.body(content: self)
}
}

//MARK: View Property Funcation
@available(iOS 13.0, *)
@available(iOS 15.0, *)
extension FloatingLabelTextField {
/// Sets the left view.
public func leftView<LRView: View>(@ViewBuilder _ view: @escaping () -> LRView) -> Self {
Expand All @@ -208,7 +256,7 @@ extension FloatingLabelTextField {
}

//MARK: Text Property Funcation
@available(iOS 13.0, *)
@available(iOS 15.0, *)
extension FloatingLabelTextField {
/// Sets the alignment for text.
public func textAlignment(_ alignment: TextAlignment) -> Self {
Expand All @@ -235,8 +283,16 @@ extension FloatingLabelTextField {
}
}

@available(iOS 16.0, *)
extension FloatingLabelTextField {
public func lineLimit(_ limit: Int) -> Self {
notifier.lineLimit = limit
return self
}
}

//MARK: Line Property Funcation
@available(iOS 13.0, *)
@available(iOS 15.0, *)
extension FloatingLabelTextField {
/// Sets the line height.
public func lineHeight(_ height: CGFloat) -> Self {
Expand Down Expand Up @@ -264,7 +320,7 @@ extension FloatingLabelTextField {
}

//MARK: Title Property Funcation
@available(iOS 13.0, *)
@available(iOS 15.0, *)
extension FloatingLabelTextField {
/// Sets the title color.
public func titleColor(_ color: Color) -> Self {
Expand Down Expand Up @@ -292,7 +348,7 @@ extension FloatingLabelTextField {
}

//MARK: Text Property Funcation
@available(iOS 13.0, *)
@available(iOS 15.0, *)
extension FloatingLabelTextField {
/// Sets the text color.
public func textColor(_ color: Color) -> Self {
Expand All @@ -314,7 +370,7 @@ extension FloatingLabelTextField {
}

//MARK: Placeholder Property Funcation
@available(iOS 13.0, *)
@available(iOS 15.0, *)
extension FloatingLabelTextField {
/// Sets the placeholder color.
public func placeholderColor(_ color: Color) -> Self {
Expand All @@ -330,7 +386,7 @@ extension FloatingLabelTextField {
}

//MARK: Error Property Funcation
@available(iOS 13.0, *)
@available(iOS 15.0, *)
extension FloatingLabelTextField {
/// Sets the is show error message.
public func isShowError(_ show: Bool) -> Self {
Expand Down Expand Up @@ -365,7 +421,7 @@ extension FloatingLabelTextField {
}

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

//MARK: Animation Style Funcation
@available(iOS 13.0, *)
@available(iOS 15.0, *)
extension FloatingLabelTextField {
/// Enable the placeholder label when the textfield is focused.
public func enablePlaceholderOnFocus(_ isEanble: Bool) -> Self {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,14 @@ class FloatingLabelTextFieldNotifier: ObservableObject {
@Published var textColor: Color = .black
@Published var selectedTextColor: Color = .blue
@Published var font: Font = .system(size: 15)
@Published var lineLimit: Int = 1

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

//MARK: Other Properties
@Published var spaceBetweenTitleText: Double = 15
@Published var spaceBetweenTitleText: Double = 30
@Published var isSecureTextEntry: Bool = false
@Published var disabled: Bool = false
@Published var allowsHitTesting: Bool = true
Expand Down

0 comments on commit 2f69aac

Please sign in to comment.