diff --git a/core/Sources/Components/TextEditor/UseCase/TexEditBorderUseCaseTests.swift b/core/Sources/Components/TextEditor/UseCase/TexEditBorderUseCaseTests.swift index 575f5ba03..bcd9145b2 100644 --- a/core/Sources/Components/TextEditor/UseCase/TexEditBorderUseCaseTests.swift +++ b/core/Sources/Components/TextEditor/UseCase/TexEditBorderUseCaseTests.swift @@ -18,18 +18,9 @@ final class TextEditorBorderUseCaseTests: XCTestCase { let texteditorBorders = TextEditorBordersUseCase().execute(theme: theme, intent: intent, isFocused: false) - let borderWidth: CGFloat - - if intent == .neutral { - borderWidth = theme.border.width.small - } else { - borderWidth = theme.border.width.medium - } - - let expectedBorders = TextEditorBorders ( radius: theme.border.radius.large, - width: borderWidth + width: theme.border.width.small ) XCTAssertEqual(texteditorBorders, expectedBorders, "Wrong border width") } diff --git a/core/Sources/Components/TextEditor/UseCase/TextEditorBordersUseCase.swift b/core/Sources/Components/TextEditor/UseCase/TextEditorBordersUseCase.swift index 1071cabc3..55f0d9508 100644 --- a/core/Sources/Components/TextEditor/UseCase/TextEditorBordersUseCase.swift +++ b/core/Sources/Components/TextEditor/UseCase/TextEditorBordersUseCase.swift @@ -20,13 +20,7 @@ final class TextEditorBordersUseCase: TextEditorBordersUseCasable { isFocused: Bool) -> TextEditorBorders { let radious = theme.border.radius.large - let width: CGFloat - - if intent == .neutral, !isFocused { - width = theme.border.width.small - } else { - width = theme.border.width.medium - } + let width = isFocused ? theme.border.width.medium : theme.border.width.small return .init( radius: radious, diff --git a/core/Sources/Components/TextEditor/UseCase/TextEditorColorsUseCase.swift b/core/Sources/Components/TextEditor/UseCase/TextEditorColorsUseCase.swift index e014f060e..640c9d66b 100644 --- a/core/Sources/Components/TextEditor/UseCase/TextEditorColorsUseCase.swift +++ b/core/Sources/Components/TextEditor/UseCase/TextEditorColorsUseCase.swift @@ -31,7 +31,7 @@ struct TextEditorColorsUseCase: TextEditorColorsUseCasable { let background: any ColorToken if !isEnabled || isReadonly { - let dim = isReadonly ? theme.dims.none : theme.dims.dim3 + let dim = !isEnabled ? theme.dims.dim3 : theme.dims.none text = theme.colors.base.onSurface.opacity(dim) border = theme.colors.base.onSurface.opacity(theme.dims.dim3) background = theme.colors.base.onSurface.opacity(theme.dims.dim5) diff --git a/core/Sources/Components/TextEditor/View/SwiftUI/TextEditorView.swift b/core/Sources/Components/TextEditor/View/SwiftUI/TextEditorView.swift index aa7e5f7c4..0fc291a35 100644 --- a/core/Sources/Components/TextEditor/View/SwiftUI/TextEditorView.swift +++ b/core/Sources/Components/TextEditor/View/SwiftUI/TextEditorView.swift @@ -8,21 +8,37 @@ import SwiftUI +enum Field: Hashable { + case text + case none +} + public struct TextEditorView: View { @ScaledMetric private var minHeight: CGFloat = 44 - @ScaledMetric private var defaultTexEditorVerticalPadding: CGFloat = 9 - @ScaledMetric private var defaultTexEditorHorizontalPadding: CGFloat = 5 + private var defaultTexEditorTopPadding: CGFloat = 8 + private var defaultTexEditorBottomPadding: CGFloat = 9 + private var defaultTexEditorHorizontalPadding: CGFloat = 5 @ScaledMetric private var scaleFactor: CGFloat = 1.0 @ObservedObject private var viewModel: TextEditorViewModel @Binding private var text: String private var titleKey: String - @FocusState private var isFocused: Bool + @FocusState private var focusedField: Field? + @Environment(\.isEnabled) private var isEnabled + @State private var textEditorEnabled: Bool = true + + private var isPlaceholderTextHidden: Bool { + return !self.titleKey.isEmpty && self.text.isEmpty + } private var isPlaceholderHidden: Bool { - return !self.titleKey.isEmpty && self.text.isEmpty && !self.viewModel.isFocused + if #available(iOS 16.0, *) { + return self.isPlaceholderTextHidden || self.viewModel.isReadOnly + } else { + return self.isPlaceholderTextHidden || self.viewModel.isReadOnly || !self.isEnabled + } } public init( @@ -52,30 +68,34 @@ public struct TextEditorView: View { .scrollIndicators(.never) } else { self.placeHolderView() - .onAppear { - UIScrollView.appearance().showsVerticalScrollIndicator = false - } self.textEditorView() - .onAppear { - UITextView.appearance().backgroundColor = .clear - UITextView.appearance().showsVerticalScrollIndicator = false - } } } .frame(minHeight: self.minHeight) - .border(width: self.viewModel.borderWidth * self.scaleFactor, radius: self.viewModel.borderRadius, colorToken: self.viewModel.borderColor) + .border(width: self.viewModel.borderWidth * self.scaleFactor, radius: self.viewModel.borderRadius * self.scaleFactor, colorToken: self.viewModel.borderColor) .tint(self.viewModel.textColor.color) .allowsHitTesting(self.viewModel.isEnabled) - .focused(self.$isFocused) - .onChange(of: self.isFocused) { value in - self.viewModel.isFocused = value + .focused(self.$focusedField, equals: .text) + .onChange(of: self.focusedField) { focusedField in + self.viewModel.isFocused = focusedField == .text } - .isEnabledChanged { isEnabled in + .isEnabled(self.isEnabled) { isEnabled in self.viewModel.isEnabled = isEnabled } + .onChange(of: self.viewModel.isEnabled) { isEnabled in + if !isEnabled { + self.focusedField = nil + } + self.textEditorEnabled = isEnabled + } + .onChange(of: self.viewModel.isReadOnly) { isReadOnly in + if isReadOnly { + self.focusedField = nil + } + } .onTapGesture { if !self.viewModel.isReadOnly { - self.isFocused = true + self.focusedField = .text } } .accessibilityElement() @@ -91,15 +111,15 @@ public struct TextEditorView: View { .foregroundStyle(self.viewModel.textColor.color) .padding( EdgeInsets( - top: .zero, - leading: self.viewModel.horizontalSpacing - self.defaultTexEditorHorizontalPadding, - bottom: .zero, - trailing: self.viewModel.horizontalSpacing - self.defaultTexEditorHorizontalPadding + top: .zero + self.scaleFactor, + leading: (self.viewModel.horizontalSpacing * self.scaleFactor - self.defaultTexEditorHorizontalPadding), + bottom: .zero + self.scaleFactor, + trailing: (self.viewModel.horizontalSpacing * self.scaleFactor - self.defaultTexEditorHorizontalPadding) ) ) - .opacity(self.isPlaceholderHidden || self.viewModel.isReadOnly ? 0 : 1) + .opacity(!self.isPlaceholderHidden || self.viewModel.isFocused ? 1 : 0) .accessibilityHidden(true) - + .environment(\.isEnabled, self.textEditorEnabled) } @ViewBuilder @@ -107,24 +127,24 @@ public struct TextEditorView: View { ScrollView { HStack(spacing: 0) { VStack(spacing: 0) { - Text(self.isPlaceholderHidden && !self.viewModel.isReadOnly ? self.titleKey : self.$text.wrappedValue) + Text(self.isPlaceholderTextHidden ? self.titleKey : self.$text.wrappedValue) .font(self.viewModel.font.font) - .foregroundStyle(self.viewModel.isReadOnly ? self.viewModel.textColor.color : self.viewModel.placeholderColor.color) - .frame(maxWidth: .infinity, alignment: .leading) - .opacity(self.isPlaceholderHidden || self.viewModel.isReadOnly ? 1 : 0) + .foregroundStyle(self.isPlaceholderTextHidden ? self.viewModel.placeholderColor.color : self.viewModel.textColor.color) + .padding( + EdgeInsets( + top: self.defaultTexEditorTopPadding + self.scaleFactor, + leading: self.viewModel.horizontalSpacing * self.scaleFactor, + bottom: self.defaultTexEditorBottomPadding + self.scaleFactor, + trailing: self.viewModel.horizontalSpacing * self.scaleFactor + ) + ) + .opacity(self.isPlaceholderHidden ? 1 : 0) .accessibilityHidden(true) + Spacer(minLength: 0) } Spacer(minLength: 0) } - .padding( - EdgeInsets( - top: self.defaultTexEditorVerticalPadding, - leading: self.viewModel.horizontalSpacing, - bottom: self.defaultTexEditorVerticalPadding, - trailing: self.viewModel.horizontalSpacing - ) - ) } } @@ -133,3 +153,12 @@ public struct TextEditorView: View { return self } } + +private extension View { + func isEnabled(_ value: Bool, complition: @escaping (Bool) -> Void) -> some View { + DispatchQueue.main.async { + complition(value) + } + return self.disabled(!value) + } +} diff --git a/core/Sources/Components/TextEditor/View/UIKit/TextEditorUIView.swift b/core/Sources/Components/TextEditor/View/UIKit/TextEditorUIView.swift index 10b7495b1..8535347a6 100644 --- a/core/Sources/Components/TextEditor/View/UIKit/TextEditorUIView.swift +++ b/core/Sources/Components/TextEditor/View/UIKit/TextEditorUIView.swift @@ -17,7 +17,7 @@ public final class TextEditorUIView: UITextView { @ScaledUIMetric private var defaultSystemVerticalPadding: CGFloat = 8 @ScaledUIMetric private var scaleFactor: CGFloat = 1.0 - private let viewModel: TextEditorViewModel + private var viewModel: TextEditorViewModel! private var cancellables = Set() private var placeHolderConstarints: [NSLayoutConstraint]? private var placeHolderLabelYAnchor: NSLayoutConstraint? @@ -45,7 +45,7 @@ public final class TextEditorUIView: UITextView { self._delegate = newValue } get { - return self._delegate + return super.delegate } } @@ -104,6 +104,9 @@ public final class TextEditorUIView: UITextView { set { self.viewModel.isEnabled = newValue self.isUserInteractionEnabled = newValue + if !isEnabled { + _ = self.resignFirstResponder() + } } } @@ -168,21 +171,21 @@ public final class TextEditorUIView: UITextView { self.textContainer.lineFragmentPadding = 0 self.textContainerInset = UIEdgeInsets( top: self.defaultSystemVerticalPadding, - left: self.viewModel.horizontalSpacing, + left: self.viewModel.horizontalSpacing * self.scaleFactor, bottom: self.defaultSystemVerticalPadding, - right: self.viewModel.horizontalSpacing + right: self.viewModel.horizontalSpacing * self.scaleFactor ) self.addSubview(self.placeHolderLabel) self.placeHolderConstarints = [ self.placeHolderLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: self.defaultSystemVerticalPadding), - self.placeHolderLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: self.viewModel.horizontalSpacing), - self.placeHolderLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -self.viewModel.horizontalSpacing), + self.placeHolderLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: self.viewModel.horizontalSpacing * self.scaleFactor), + self.placeHolderLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -self.viewModel.horizontalSpacing * self.scaleFactor), self.placeHolderLabel.bottomAnchor.constraint(greaterThanOrEqualTo: self.bottomAnchor, constant: -self.defaultSystemVerticalPadding) ] self.placeHolderLabelYAnchor = self.placeHolderLabel.centerYAnchor.constraint(lessThanOrEqualTo: self.centerYAnchor) self.placeHolderLabelXAnchor = self.placeHolderLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor) - self.placeholderLabelWidthAnchor = self.placeHolderLabel.widthAnchor.constraint(lessThanOrEqualTo: self.textInputView.widthAnchor, constant: -2 * self.viewModel.horizontalSpacing) + self.placeholderLabelWidthAnchor = self.placeHolderLabel.widthAnchor.constraint(lessThanOrEqualTo: self.textInputView.widthAnchor, constant: -2 * self.viewModel.horizontalSpacing * self.scaleFactor) self.heightAnchor.constraint(greaterThanOrEqualToConstant: self.minHeight).isActive = true } @@ -293,7 +296,7 @@ extension TextEditorUIView: UITextViewDelegate { } public func textViewDidBeginEditing(_ textView: UITextView) { - self.hidePlaceHolder(true) + self.hidePlaceHolder(!textView.text.isEmpty) self._delegate?.textViewDidBeginEditing?(textView) }