Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,55 @@ struct AccountCreationFormFieldViewModel {
struct AccountCreationFormFieldView: View {
private let viewModel: AccountCreationFormFieldViewModel

/// Whether the text field is *shown* as secure.
/// When the field is secure, there is a button to show/hide the text field input.
@State private var showsSecureInput: Bool = true

// Tracks the scale of the view due to accessibility changes.
@ScaledMetric private var scale: CGFloat = 1.0

init(viewModel: AccountCreationFormFieldViewModel) {
self.viewModel = viewModel
self.showsSecureInput = viewModel.isSecure
}

var body: some View {
VStack(alignment: .leading, spacing: Layout.verticalSpacing) {
Text(viewModel.header)
.subheadlineStyle()
if viewModel.isSecure {
SecureField(viewModel.placeholder, text: viewModel.text)
.textFieldStyle(RoundedBorderTextFieldStyle(focused: viewModel.isFocused))
ZStack(alignment: .trailing) {
// Text field based on the `isTextFieldSecure` state.
Group {
if showsSecureInput {
SecureField(viewModel.placeholder, text: viewModel.text)
} else {
TextField(viewModel.placeholder, text: viewModel.text)
}
}
.font(.body)
.textFieldStyle(RoundedBorderTextFieldStyle(
focused: viewModel.isFocused,
// Custom insets to leave trailing space for the reveal button.
insets: .init(top: RoundedBorderTextFieldStyle.Defaults.insets.top,
leading: RoundedBorderTextFieldStyle.Defaults.insets.leading,
bottom: RoundedBorderTextFieldStyle.Defaults.insets.bottom,
trailing: Layout.secureFieldRevealButtonHorizontalPadding * 2 + Layout.secureFieldRevealButtonDimension * scale))
)
.keyboardType(viewModel.keyboardType)

// Button to show/hide the text field content.
Button(action: {
showsSecureInput.toggle()
}) {
Image(systemName: showsSecureInput ? "eye.slash" : "eye")
.accentColor(Color(.textSubtle))
.frame(width: Layout.secureFieldRevealButtonDimension * scale,
height: Layout.secureFieldRevealButtonDimension * scale)
.padding(.leading, Layout.secureFieldRevealButtonHorizontalPadding)
.padding(.trailing, Layout.secureFieldRevealButtonHorizontalPadding)
}
}
} else {
TextField(viewModel.placeholder, text: viewModel.text)
.textFieldStyle(RoundedBorderTextFieldStyle(focused: viewModel.isFocused))
Expand All @@ -50,6 +87,8 @@ struct AccountCreationFormFieldView: View {
private extension AccountCreationFormFieldView {
enum Layout {
static let verticalSpacing: CGFloat = 8
static let secureFieldRevealButtonHorizontalPadding: CGFloat = 16
static let secureFieldRevealButtonDimension: CGFloat = 18
}
}

Expand All @@ -65,9 +104,10 @@ struct AccountCreationFormField_Previews: PreviewProvider {
AccountCreationFormFieldView(viewModel: .init(header: "Choose a password",
placeholder: "Password",
keyboardType: .default,
text: .constant("w"),
text: .constant("wwwwwwwwwwwwwwwwwwwwwwww"),
isSecure: true,
errorMessage: "Too simple",
isFocused: false))
.environment(\.sizeCategory, .extraExtraExtraLarge)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,27 @@ import SwiftUI

/// Text field has a rounded border that has a thicker border and brighter border color when the field is focused.
struct RoundedBorderTextFieldStyle: TextFieldStyle {
let focused: Bool
private let focused: Bool
private let insets: EdgeInsets

init(focused: Bool, insets: EdgeInsets = Defaults.insets) {
self.focused = focused
self.insets = insets
}

func _body(configuration: TextField<Self._Label>) -> some View {
configuration
.padding(10)
.padding(insets)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the change of height when switching between field types is due to vertical padding and the content height difference of SecureField and TextField.

A quick fix would be to add a fixed height, we may need to take scale into consideration:

Suggested change
.padding(insets)
.padding(insets)
.frame(height: 44 * scale)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After some testing, the insets aren't the cause of the mismatched height, the height is just slightly different between the TextField and SecureField. In a09e7e3, I passed an optional height parameter for AccountCreationFormFieldView to set 44 * scale.

.background(
RoundedRectangle(cornerRadius: 8, style: .continuous)
.stroke(focused ? Color(.brand): Color.gray,
lineWidth: focused ? 2: 1)
)
}
}

extension RoundedBorderTextFieldStyle {
enum Defaults {
static let insets = EdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
}
}