// // ComposeContentWarningTextField.swift // Tusker // // Created by Shadowfacts on 10/12/20. // Copyright © 2020 Shadowfacts. All rights reserved. // import SwiftUI struct ComposeContentWarningTextField: UIViewRepresentable { typealias UIViewType = UITextField @Binding var text: String @EnvironmentObject private var uiState: ComposeUIState func makeUIView(context: Context) -> UITextField { let view = UITextField() view.placeholder = "Write your warning here" view.borderStyle = .roundedRect view.delegate = context.coordinator view.addTarget(context.coordinator, action: #selector(Coordinator.didChange(_:)), for: .editingChanged) context.coordinator.textField = view context.coordinator.uiState = uiState context.coordinator.text = $text return view } func updateUIView(_ uiView: UITextField, context: Context) { uiView.text = text } func makeCoordinator() -> Coordinator { return Coordinator() } class Coordinator: NSObject, UITextFieldDelegate, ComposeAutocompleteHandler { weak var textField: UITextField? var text: Binding! var uiState: ComposeUIState! @objc func didChange(_ textField: UITextField) { text.wrappedValue = textField.text ?? "" } func textFieldDidBeginEditing(_ textField: UITextField) { uiState.autocompleteHandler = self updateAutocompleteState(textField: textField) } func textFieldDidEndEditing(_ textField: UITextField) { updateAutocompleteState(textField: textField) } func textFieldDidChangeSelection(_ textField: UITextField) { // Update text binding before potentially triggering SwiftUI view update. // See comment in MainComposeTextView.Coordinator.textViewDidChangeSelection text.wrappedValue = textField.text ?? "" updateAutocompleteState(textField: textField) } func autocomplete(with string: String) { guard let textField = textField, let text = textField.text, let selectedRange = textField.selectedTextRange, let lastWordStartIndex = findAutocompleteLastWord(textField: textField) else { return } let distanceToEnd = textField.offset(from: selectedRange.start, to: textField.endOfDocument) let selectedRangeStartUTF16 = textField.offset(from: textField.beginningOfDocument, to: selectedRange.start) let characterBeforeCursorIndex = text.utf16.index(text.startIndex, offsetBy: selectedRangeStartUTF16) let insertSpace: Bool if distanceToEnd > 0 { let charAfterCursor = text[characterBeforeCursorIndex] insertSpace = charAfterCursor != " " && charAfterCursor != "\n" } else { insertSpace = true } let string = insertSpace ? string + " " : string textField.text!.replaceSubrange(lastWordStartIndex.. text.startIndex { let c = text[text.index(before: lastWordStartIndex)] if isPermittedForAutocomplete(c) || c == ":" { uiState.autocompleteState = nil return } } let selectedRangeStartUTF16 = textField.offset(from: textField.beginningOfDocument, to: selectedRange.start) let cursorIndex = text.utf16.index(text.startIndex, offsetBy: selectedRangeStartUTF16) if lastWordStartIndex >= text.startIndex { let lastWord = text[lastWordStartIndex.. Bool { return (c >= "a" && c <= "z") || (c >= "A" && c <= "Z") || (c >= "0" && c <= "9") || c == "_" } private func findAutocompleteLastWord(textField: UITextField) -> String.Index? { guard textField.isFirstResponder, let selectedRange = textField.selectedTextRange, selectedRange.isEmpty, let text = textField.text, !text.isEmpty else { return nil } let selectedRangeStartUTF16 = textField.offset(from: textField.beginningOfDocument, to: selectedRange.start) let cursorIndex = text.utf16.index(text.startIndex, offsetBy: selectedRangeStartUTF16) var lastWordStartIndex = text.index(before: cursorIndex) while true { let c = text[lastWordStartIndex] if !isPermittedForAutocomplete(c) { break } if lastWordStartIndex > text.startIndex { lastWordStartIndex = text.index(before: lastWordStartIndex) } else { break } } return lastWordStartIndex } } }