'OTC View autolayout in SwiftUI
I'm new to swiftui and swift basically, i made One-time-code screen, and here i have a problem. When i run project on my old phone (iphone 6) when keyboard appears, my textfield size changes (it gets very thin vertically). So i was wondering is there any way to add autolayout for older devices?
Here is my code
struct OneTimeCodeBoxes: View {
@Binding var codeDict: [Int: String]
@State var firstResponderIndex = 0
var onCommit: (()->Void)?
var body: some View {
HStack {
ForEach(0..<codeDict.count) { i in
let isEmpty = codeDict[i]?.isEmpty == true
OneTimeCodeInput(
index: i,
codeDict: $codeDict,
firstResponderIndex: $firstResponderIndex,
onCommit: onCommit
)
.aspectRatio(1, contentMode: .fit)
.overlay(RoundedRectangle(cornerRadius: 10)
.stroke(lineWidth: isEmpty ? 1 : 2)
.foregroundColor(isEmpty ? .secondary : .green))
}
}
}
}
struct OneTimeCodeBoxes_Previews: PreviewProvider {
static var previews: some View {
OneTimeCodeBoxes(codeDict: .constant([0: "", 1: "", 2: "", 3: ""]))
.padding()
.previewLayout(.sizeThatFits)
}
}
Here is my OneTimeCodeInput part of code
struct OneTimeCodeInput: UIViewRepresentable {
typealias UIViewType = UITextField
let index: Int
@Binding var codeDict: [Int: String]
@Binding var firstResponderIndex: Int
var onCommit: (()->Void)?
class Coordinator: NSObject, UITextFieldDelegate {
let index: Int
@Binding var codeDict: [Int: String]
@Binding var firstResponderIndex: Int
private lazy var codeDigits: Int = codeDict.count
init(index: Int, codeDict: Binding<[Int: String]>, firstResponderIndex: Binding<Int>) {
self.index = index
self._codeDict = codeDict
self._firstResponderIndex = firstResponderIndex
}
@objc func textFieldEditingChanged(_ textField: UITextField) {
print("textField.text!", textField.text!)
guard textField.text!.count == codeDigits else { return }
codeDict = textField.text!.enumerated().reduce(into: [Int: String](), { dict, tuple in
let (index, char) = tuple
dict.updateValue(String(char), forKey: index)
})
firstResponderIndex = codeDigits - 1
}
func textField(_ textField: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool
{
print("replacementString", string)
if string.isBackspace {
codeDict.updateValue("", forKey: index)
firstResponderIndex = max(0, textField.text == "" ? index - 1 : index)
return false
}
for i in index..<min(codeDict.count, index + string.count) {
codeDict.updateValue(string.stringAt(index: i - index), forKey: i)
// print(codeDict)
firstResponderIndex = min(codeDict.count - 1, index + string.count)
}
return true
}
}
func makeCoordinator() -> Coordinator {
.init(index: index, codeDict: $codeDict, firstResponderIndex: $firstResponderIndex)
}
func makeUIView(context: Context) -> UITextField {
let tf = BackspaceTextField(onDelete: {
firstResponderIndex = max(0, index - 1)
})
tf.addTarget(context.coordinator, action: #selector(Coordinator.textFieldEditingChanged), for: .editingChanged)
tf.delegate = context.coordinator
tf.keyboardType = .numberPad
tf.textContentType = .oneTimeCode
tf.font = .preferredFont(forTextStyle: .largeTitle)
tf.textAlignment = .center
return tf
}
func updateUIView(_ uiView: UITextField, context: Context) {
uiView.text = codeDict[index]
if index == firstResponderIndex {
uiView.becomeFirstResponder()
}
if index == firstResponderIndex,
codeDict.values.filter({ !$0.isEmpty }).count == codeDict.count
{
onCommit?()
}
}
}
extension OneTimeCodeInput {
class BackspaceTextField: UITextField {
var onDelete: (()->Void)?
init(onDelete: (()->Void)?) {
self.onDelete = onDelete
super.init(frame: .zero)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func deleteBackward() {
super.deleteBackward()
onDelete?()
}
}
}
This is how it shows on Ipnone13 simulator, and it's correct. I'm trying to do same on older devices
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|