Skip to content

Commit

Permalink
Multiple adjustments and fix
Browse files Browse the repository at this point in the history
- Fixed styling modifiers to work only with attributes
- Added textColor modifier
- Improved documentation
- Added initializers with Foundation.Formatter support
  • Loading branch information
ricocrescenzio95 committed Apr 13, 2022
1 parent 85905bc commit b951ea4
Show file tree
Hide file tree
Showing 10 changed files with 364 additions and 91 deletions.
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
* A default `ResponderNavigatorView` usable as an `InputAccessoryView` to navigate through text fields
* Attributed placeholder
* `ParseableFormatStyle` when using `iOS 15`
* `Foundation.Formatter` when using `pre-iOS 15`
* DocC documented!

## Installation
Expand Down Expand Up @@ -125,10 +126,8 @@ Use Apple `DocC` generated documentation, from Xcode, `Product > Build Documenta
## Known Issues

- When an external keyboard is connected and the software keyboard is hidden,
on iOS 15 there is small layout jump when switching from a text field with custom input view and one
with normal software keyboard
- On iOS 14 this behavior is worse: sometimes the system tries to re-layout the component infinitely, leading to a
stack overflow and crash! Need to understand what actually happens under the hood...
on iOS 13/15 there are small layout jumps when switching from a text fields
- On iOS 14 this behavior is worse: the system tries to re-layout the component infinitely! Need to understand what actually happens under the hood...

## Found a bug or want new feature?

Expand Down
67 changes: 45 additions & 22 deletions SUITextFieldExample/SUITextFieldExample/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ struct ContentView: View {
case second
case third
case fourth
case fifth
}

@State private var toggleFont = false
Expand All @@ -27,13 +28,30 @@ struct ContentView: View {

let attributes: [NSAttributedString.Key: Any] = [
.kern: 5,
.foregroundColor: UIColor.systemOrange
.foregroundColor: UIColor.systemOrange,
.font: UIFont.systemFont(ofSize: 20, weight: .black),
.paragraphStyle: {
let p = NSMutableParagraphStyle()
p.alignment = .right
return p
}()
]

let moreKern: [NSAttributedString.Key: Any] = [
.kern: 15,
]

let placeholderAttributes: [NSAttributedString.Key: Any] = [
.kern: 5,
.foregroundColor: UIColor.systemGray
]

let formatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .full
formatter.timeStyle = .medium
return formatter
}()

var body: some View {
NavigationView {
Expand All @@ -59,6 +77,7 @@ struct ContentView: View {
}
.responder($focus, equals: .first)
.uiTextFieldTextLeftViewMode(.whileEditing)
.uiTextFieldDefaultTextAttributes(moreKern, mergePolicy: .keepNew)
SUITextField(text: $text)
.inputAccessoryView {
navigator
Expand All @@ -68,37 +87,41 @@ struct ContentView: View {
SUITextField(
value: $myDouble,
format: .number,
placeholder: AttributedString("Hey there").mergingAttributes(AttributeContainer(placeholderAttributes))
placeholder: AttributedString("Hey there")
.mergingAttributes(AttributeContainer(placeholderAttributes))
)
.inputAccessoryView {
navigator
}
.responder($focus, equals: .third)
SUITextField(value: $date, format: .dateTime)
.inputAccessoryView {
navigator
}
.inputView {
datePicker
}
.responder($focus, equals: .fourth)
} else {
SUITextField(text: .constant(date.description))
.inputAccessoryView {
navigator
}
.inputView {
datePicker
}
.responder($focus, equals: .third)
.uiTextFieldTextColor(.systemGreen)
}
SUITextField(value: $date, formatter: formatter)
.inputAccessoryView {
navigator
}
.inputView {
datePicker
}
.responder($focus, equals: .fourth)
.uiTextFieldFont(.systemFont(ofSize: 14, weight: .light))
}
.uiTextFieldDefaultTextAttributes(attributes)
.padding()
SUITextField(value: $date, formatter: formatter)
.inputAccessoryView {
navigator
}
.inputView {
datePicker
}
.responder($focus, equals: .fifth)
.uiTextFieldDefaultTextAttributes(attributes, mergePolicy: .keepOld)
.uiTextFieldTextColor(.systemRed)
.uiTextFieldFont(.italicSystemFont(ofSize: 12))
}
.uiTextFieldDefaultTextAttributes(attributes)
.uiTextFieldFont(toggleFont ? .monospacedSystemFont(ofSize: 50, weight: .medium) : nil)
.uiTextFieldFont(.systemFont(ofSize: 14, weight: .light))
.uiTextFieldBorderStyle(.roundedRect)
.uiTextFieldAdjustsFontSizeToFitWidth(.enabled(minSize: 8))
.navigationBarTitle("SUITextField")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ where Responder: Hashable, BackButton: View, NextButton: View, CenterView: View,
}
.padding(.horizontal, 16)
.padding(.vertical, 10)
.frame(minHeight: 44)
}

private var currentIndex: Int? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,56 @@ VStack {
.uiTextFieldFont(.systemFont(ofSize: 16, weight: .light))
```

## Dive deep into Default Text Attributes

Using ``SUITextField/uiTextFieldDefaultTextAttributes(_:mergePolicy:)`` you can specify how to apply default attributes
to every text field.
Since this modifier works using environment, another modifier at higher view hierarchy might
have been applied.

Following example apply `kern`, `foregroundColor` and `font` to text fields which bind `password` and `repeatPassoword`;
the text field which bind `username` apply **only** the new kern and get rid of previously set attributes:

```swift
let attributes: [NSAttributedString.Key: Any] = [
.kern: 5,
.foregroundColor: UIColor.systemOrange,
.font: UIFont.systemFont(ofSize: 20, weight: .black),
]

let moreKern: [NSAttributedString.Key: Any] = [
.kern: 15,
]

var body: some View {
VStack {
SUITextField(text: $username)
.uiTextFieldDefaultTextAttributes(kern)
SUITextField(text: $password)
SUITextField(text: $repeatPassword)
}
.uiTextFieldDefaultTextAttributes(attributes)
}
```

If you want to override `kern` and keep other old attributes, you will use ``DefaultAttributesMergePolicy`` like:

```swift
SUITextField(text: $username)
.uiTextFieldDefaultTextAttributes(kern, mergePolicy: .keepNew)
```

Finally, there are cases where you might merge new attributes but keep old ones when same key is found,
you can use `.keepOld`. Following example apply a `font`, a `textColor` and then the default attributes
applies **only** the `kern` becuase policy is `.keepOld`

```swift
SUITextField(text: $text)
.uiTextFieldDefaultTextAttributes(attributes, mergePolicy: .keepOld)
.uiTextFieldTextColor(.systemRed)
.uiTextFieldFont(.italicSystemFont(ofSize: 12))
```

## Get the environment value

Each modifier apply an environment, so you can get this value using `@Environment` property wrapper.
Expand All @@ -79,9 +129,16 @@ struct MyView: View {

## Topics

### Modifiers
### Modify appearence

- ``SUITextField/uiTextFieldFont(_:)``
- ``SUITextField/uiTextFieldTextColor(_:)``
- ``SUITextField/uiTextFieldTextAlignment(_:)``
- ``SUITextField/uiTextFieldDefaultTextAttributes(_:mergePolicy:)``
- ``DefaultAttributesMergePolicy``

### Modify behavior

- ``SUITextField/uiTextFieldReturnKeyType(_:)``
- ``SUITextField/uiTextFieldSecureTextEntry(_:)``
- ``SUITextField/uiTextFieldClearButtonMode(_:)``
Expand All @@ -90,10 +147,8 @@ struct MyView: View {
- ``SUITextField/uiTextFieldAutocorrectionType(_:)``
- ``SUITextField/uiTextFieldKeyboardType(_:)``
- ``SUITextField/uiTextFieldTextContentType(_:)``
- ``SUITextField/uiTextFieldTextAlignment(_:)``
- ``SUITextField/uiTextFieldTextLeftViewMode(_:)``
- ``SUITextField/uiTextFieldTextRightViewMode(_:)``
- ``SUITextField/uiTextFieldDefaultTextAttributes(_:)``
- ``SUITextField/uiTextFieldSpellCheckingType(_:)``
- ``SUITextField/uiTextFieldPasswordRules(_:)``
- ``SUITextField/uiTextFieldAdjustsFontSizeToFitWidth(_:)``
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ A `SwiftUI` wrapper of `UITextField` that allows more customization and programm
``SUITextField`` is a wrapper of `UITextField` that allows to navigate programmatically through responders,
allows to set a custom `inputView`, `inputAccessoryView` while editing, as well as `leftView` and `rightView`.

You can also use all the `UITextFieldDelegate` methods, all exposed as `SwiftUI` modifiers.
You can also use all the `UITextFieldDelegate` methods, all exposed as `SwiftUI` modifiers. 😎

On `iOS 15`, you can use the new `ParseableFormatStyle` to bind the text field to a custom value; on `pre-iOS 15`
a similar API is exposed using `Foundation.Formatter` 🥳

All these additional customization are passed as `SwiftUI` views/modifiers, allowing to use its lovely declarative API! 🎉

Expand Down Expand Up @@ -65,6 +68,9 @@ struct ContentView: View {
// more code...
```

You can create a text field with a combination of text, plain placeholder, attributed placeholder, custom value and
formatters. Check all the provided initializers in ``SUITextField``!

## Topics

### Essential views
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// DefaultAttributesMergePolicy.swift
//
//
// Created by Rico Crescenzio on 13/04/22.
//

/// Used in ``SUITextField/uiTextFieldDefaultTextAttributes(_:mergePolicy:)``
/// to tell the modifier how to apply new attributes.
public enum DefaultAttributesMergePolicy {

/// When two dict of attributes contains same key, old value is kept.
case keepOld

/// When two dict of attributes contains same key, new value is kept.
case keepNew

/// The old dict of attributes is completely removed in place of the new one.
case rewriteAll
}
Loading

0 comments on commit b951ea4

Please sign in to comment.