#swift #swiftui
#swift #swiftui
Вопрос:
Новичок здесь 😬
Я пытаюсь сделать «ввод» в SwiftUI с различными размерами шрифта.
Из того, что я могу сказать из других сообщений, нет способа сделать это с помощью собственного текстового поля, поэтому вместо этого я создал HStack с splicedText и отрисовал каждую часть моего текста отдельно для достижения желаемого результата. Этот бит работает нормально.
Однако теперь у меня есть как обычное текстовое поле, так и моя «метка», и я не могу понять, как скрыть это текстовое поле и при этом сохранить .decimalPad
его открытым.
Это тот вид, которого я пытаюсь достичь:
Это то, что у меня есть до сих пор:
Как вы можете видеть, мое значение отображается дважды. один почти правильно отформатирован, а другой неформатирован ниже (текстовое поле).
Я попытался добавить a .hidden()
в текстовое поле, и хотя это скрывает текстовое поле (), мой firstResponder() .decimalPad
больше не отображается, и у меня нет способа ввести свое значение: (
Я не хочу просто устанавливать для него значение opacity(0)
либо, поскольку я не хочу, чтобы оно занимало какое-либо место на мой взгляд.
В конечном итоге на этом экране будет 2 входа, и мне нужно иметь возможность щелкнуть «ярлык», чтобы .decimalPad
снова открыть. Однако я еще не зашел так далеко.
Вот код для отображения этого:
import SwiftUI
import SwiftUIX
struct CreateEntryView : View {
class ViewModel: ObservableObject {
@Published var value = "$17.00" {
didSet {
let validCharSet = CharacterSet(charactersIn: "$1234567890.,")
print("value: " value)
// add "$" prefix
if value.prefix(1) != "$" { value = "$" value }
//check if the new string contains any invalid characters
if (value).rangeOfCharacter(from: validCharSet.inverted) != nil {
// remove any invalid characters
value = String(value.unicodeScalars.filter {
validCharSet.contains($0)
})
}
// replace ',' with '.' (required to handle all locales w/ decimalPad)
let test = value.replacingOccurrences(of: ",", with: ".")
print("replace comma: " test)
// check if there's more than 2 decimals
// check if there's more than 1 dot
// check if there's more than 1 dollar-sign
}
}
}
@ObservedObject var viewModel = ViewModel()
// create splicedLabel for making label with varyign fontSize
var splicedLabel: [String] {
var spliceString = viewModel.value
// remove $ from string
spliceString.remove(at: spliceString.startIndex)
return spliceString.split(separator: ".").map(String.init)
}
var body: some View {
VStack {
HStack(alignment: .bottom, spacing: 0) {
Text("$")
.font(.body)
.fontWeight(.bold)
.padding(.bottom, 5)
if self.splicedLabel.indices.contains(0) {
Text(self.splicedLabel[0])
.font(.largeTitle)
.fontWeight(.bold)
}
if self.splicedLabel.indices.contains(1) {
HStack(alignment: .bottom, spacing: 0) {
Text(".")
.font(.body)
.fontWeight(.bold)
Text(self.splicedLabel[1])
.font(.body)
.fontWeight(.bold)
}.padding(.bottom, 5)
}
}
CocoaTextField("$123", text: $viewModel.value)
.isFirstResponder(true) // autoFocus
.keyboardType(.decimalPad)
}
}
}
Был бы очень признателен за информацию о том, как этого добиться. Возможно, я ошибаюсь, пытаясь использовать тексты в HStack для разных размеров шрифта.
Комментарии:
1. Возможно, используйте ZStack и alpha zero, чтобы исключить его из потока просмотра
Ответ №1:
Возможно, вы можете сделать это, используя UITextField и NSAttributedString, которые могут быть украшены шрифтами разного размера или цвета.
import UIKit
import SwiftUI
import Combine
struct MyTextField: UIViewRepresentable {
private var placeholder: String
@Binding private var text: String
private var textField = UITextField()
init(_ placeholder: String, text: Binding<String>) {
self.placeholder = placeholder
self._text = text
}
func makeCoordinator() -> Coordinator {
Coordinator(textField: self.textField, text: self._text)
}
func makeUIView(context: Context) -> UITextField {
textField.placeholder = self.placeholder
textField.font = UIFont.boldSystemFont(ofSize: 17)
textField.keyboardType = .decimalPad
textField.text = self.text
return textField
}
func updateUIView(_ uiView: UITextField, context: Context) {
}
class Coordinator: NSObject {
private var dispose = Set<AnyCancellable>()
@Binding var text: String
init(textField: UITextField, text: Binding<String>) {
self._text = text
super.init()
let publisher = NotificationCenter.default
.publisher(for: UITextField.textDidChangeNotification, object: textField)
.compactMap { $0.object as? UITextField }
.share()
publisher.compactMap { $0.text }
.receive(on: RunLoop.main)
.assign(to: .text, on: self)
.store(in: amp;dispose)
publisher.receive(on: RunLoop.main)
.sink(receiveValue: { (textField: UITextField) in
Coordinator.setAttributedString(textField: textField)
})
.store(in: amp;dispose)
}
static func setAttributedString(textField: UITextField) {
guard var text = textField.attributedText?.string else { return }
text = text.prefix(1) == "$" ? text : "$" text
let dollarAttributes: [NSAttributedString.Key: Any] = [
.foregroundColor: UIColor.black,
.font: UIFont.boldSystemFont(ofSize: 34)
]
let otherAttributes: [NSAttributedString.Key: Any] = [
.foregroundColor: UIColor.black,
.font: UIFont.boldSystemFont(ofSize: 17)
]
let attributedText = NSMutableAttributedString(string: text)
attributedText.addAttributes(dollarAttributes, range: NSRange(location: 1, length: text.count - 1))
let strings = text.split(separator: ".")
if let dollar = strings.first, strings.count > 1 {
let range = NSRange(location: dollar.count, length: text.count - dollar.count)
attributedText.addAttributes(otherAttributes, range: range)
}
textField.attributedText = attributedText
}
}
}
extension MyTextField.Coordinator: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
}
}
struct ContentView: View {
@State var text: String = "$"
var body: some View {
VStack {
MyTextField("placeholder", text: self.$text).padding()
Text(self.text).foregroundColor(.red).padding()
}
}
}