Code continued...
Focusable Text Field
/**
Supports first responder and pageable state, so the keyboard only
shows after the page view has fully transitioned.
*/
struct FocusablePageableTextField: UIViewRepresentable {
@Binding var text: String
var isFirstResponder: Bool = false
var placeholder: String = ""
var textAlignment: NSTextAlignment = .left
var autocapitalizationType: UITextAutocapitalizationType = .sentences
var keybaordType: UIKeyboardType = .default
var textContentType: UITextContentType? = nil
var autocorrectionType: UITextAutocorrectionType = .yes
var activePage: Int
@Binding var isPageChanging: Bool
@Binding var currPage: Int
@Binding var showSpinner: Bool
var onReturnCallback: () -> Void
var onButtonPressCallback: () -> Void
// Optional callback for when text field is updated
var onUpdateCallback: (() -> Void)? = nil
func makeUIView(context: UIViewRepresentableContext<FocusablePageableTextField>) -> UITextField {
let textField = UITextField(frame: .zero)
textField.placeholder = placeholder
textField.textAlignment = textAlignment
textField.autocapitalizationType = autocapitalizationType
textField.keyboardType = keybaordType
textField.textContentType = textContentType == nil ? .none : textContentType!
textField.autocorrectionType = autocorrectionType
// Prevents the textfield from growing when text exceeds its bounds
textField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
// Style
textField.layer.cornerRadius = 20
textField.layer.borderWidth = 2
textField.layer.borderColor = ColorPalette.primaryTint.cgColor
textField.font = UIFont(name: Fonts.brandBold.uiktFont, size: UIScreen.main.bounds.height > 812 ? 18 : 14)
textField.textColor = ColorPalette.secondaryTint
textField.rightView = UIView()
textField.rightViewMode = .always
textField.rightView?.translatesAutoresizingMaskIntoConstraints = false
textField.rightView?.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.height > 568 ? 35 : 30).isActive = true
textField.addPadding(.left(UIScreen.main.bounds.height > 568 ? 35 : 30))
textField.rightView?.addSubview(context.coordinator.continueButton)
textField.rightView?.addConstraintsWithFormat(format: "H:|[v0]|", views: context.coordinator.continueButton)
textField.rightView?.addConstraintsWithFormat(format: "V:|[v0]|", views: context.coordinator.continueButton)
textField.delegate = context.coordinator
context.coordinator.continueButton.addTarget(context.coordinator, action: #selector(context.coordinator.buttonCallback), for: .touchUpInside)
return textField
}
func makeCoordinator() -> FocusablePageableTextField.Coordinator {
return Coordinator(self)
}
func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<FocusablePageableTextField>) {
uiView.text = text
DispatchQueue.main.async { self.onUpdateCallback?() }
if !isPageChanging && currPage == activePage && isFirstResponder && !context.coordinator.didBecomeFirstResponder {
DispatchQueue.main.async {
uiView.becomeFirstResponder()
}
context.coordinator.didBecomeFirstResponder = true
}
if isPageChanging {
uiView.endEditing(true)
context.coordinator.didBecomeFirstResponder = false
}
}
class Coordinator: NSObject, UITextFieldDelegate {
private let parent: FocusablePageableTextField
var didBecomeFirstResponder = false
init(_ parent: FocusablePageableTextField) {
self.parent = parent
}
func textFieldDidChangeSelection(_ textField: UITextField) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.parent.text = textField.text ?? ""
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
parent.onReturnCallback()
return true
}
@objc func buttonCallback(sender: UIButton) {
parent.onButtonPressCallback()
}
// View components
let continueButton: UIButton = {
let button = UIButton(type: .custom)
button.setImage(UIImage(named: "Icon-Chevron-Blue"), for: .normal)
button.imageView?.contentMode = .scaleAspectFill
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
}
}
View containing Paging View (Presented by the Sheet)
struct PagesView: View {
@State private var currPage = 0
@State private var pageDirection: UIPageViewController.NavigationDirection = .forward
@State private var isPageChanging = false
var body: some View {
VStack {
PageControlView(numberOfPages: 3, currentPage: $currPage) // UIPageControl
PageViewController(controllers: [
AnyView(SamplePageInputView(isPageChanging: $isPageChanging,
currPage: $currPage,
submitCallback: nextPage)),
AnyView(SamplePageInputView(isPageChanging: $isPageChanging,
currPage: $currPage,
submitCallback: nextPage)),
AnyView(SamplePageInputView(isPageChanging: $isPageChanging,
currPage: $currPage,
submitCallback: nextPage))]
.map { UIHostingController(rootView: $0)},
currPage: $currPage, pageDirection: $pageDirection, isPageChanging: $isPageChanging)
}
}
private func nextPage() {
if (!isPageChanging) {
pageDirection = .forward
currPage += 1
isPageChanging = true
}
}
private func previousPage() {
if (!isPageChanging) {
pageDirection = .reverse
currPage -= 1
isPageChanging = true
}
}
}