Na razie chciałbym zrezygnować z nowej opcji, którą daje iOS 11, czyli sugerowania haseł w aplikacji. Kiedy uruchamiam aplikację na iOS 11, dostaję opcję autouzupełniania na górze klawiatury, a moja nazwa użytkownika i pole tekstowe hasła nawet się nie pojawiają.
Moje pytanie brzmi: jak mogę wyłączyć nową funkcję autouzupełniania hasła, aby klawisz na klawiaturze nie był w ogóle wyświetlany, a ogólne zachowanie jest takie samo jak w przypadku wersji przed iOS 11?
textContentType
na.textContentType
- Powinno to powiedzieć iOS 11, że twoje pola nie są polami nazwy użytkownika / hasła (mimo że są) i uniemożliwić wyświetlenie widoku akcesoriów; Coś w rodzaju `self.passwordField.textContentType = .textContentType`Odpowiedzi:
iOS 11 i 12 i 13 - Swift 4.2 i 5 (zaktualizowane):
if #available(iOS 12, *) { // iOS 12 & 13: Not the best solution, but it works. passwordTextField.textContentType = .oneTimeCode } else { // iOS 11: Disables the autofill accessory view. // For more information see the explanation below. emailTextField.textContentType = .init(rawValue: "") passwordTextField.textContentType = .init(rawValue: "") }
Wyjaśnienie iOS 11 :
Upewnij się, że skonfigurowałeś wszystkie
UITextField
obiekty w ten sposób.Jeśli masz na przykład
UITextField
obiekt, w którym użytkownik musi wprowadzić swój adres e-mail i inny, w którym użytkownik musi wprowadzić swoje hasło, przypiszUITextContentType("")
do obu swoichtextContentType
właściwości. W przeciwnym razie nie będzie działać, a widok akcesoriów automatycznego wypełniania będzie nadal wyświetlany.źródło
UITextContentType
który nazywa sięnewPassword
(patrz: developer.apple.com/documentation/uikit/uitextcontenttype/ ... ). Spróbuj ustawić typ zawartości pól tekstowych hasła na ten typ. Wydaje mi się, że iOS nie pokaże wtedy widoku akcesoriów autouzupełniania, ponieważ użytkownik jest zmuszony ustawić nowe hasło i nie używać / uzyskiwać dostępu do hasła, które zostało ustawione wcześniej, za pomocą widoku akcesoriów autouzupełniania. W tej chwili to tylko teoria. Nie jestem pewien, czy to zadziała, ale daj mi znać, czy zadziała!UITextContentType
nanewPassword
nie działa. Wypróbowałem rozwiązanie @GalShahar, ale mi się nie podobało. Zamiast ja ustawienieUITextContentType
sięoneTimeCode
. Na razie to działa.isSecureTextEntry
oprócz ustawieniaUITextContentType
naoneTimeCode
. Możemy ustawićisSecureTextEntry
cokolwiek chcemy, gdy zaczną wpisywać pole tekstowe..isSecureTextEntry
abytrue
przed ustawieniemtextContentType
się.oneTimeCode
i działa zgodnie z oczekiwaniami. Nie wiem, jak wygląda Twój kod, ale podczas inicjalizacji pola tekstowego kolejność ustawiania jego właściwości nie jest ważna.Wygląda na to, że iOS 12 rozpoznaje hasło textFields również po
isSecureTextEntry
właściwości, a nie tylko potextContentType
właściwości, więc zniknięcie tego widoku akcesoriów nie jest naprawdę możliwe, chyba że oboje ustawicie textContentType na nic i usuniecie funkcję secureEntry (i spowoduje to lukę bezpieczeństwa w aplikacji) następnie uniemożliwia systemowi iOS 12 rozpoznanie textField jako hasło textField i wyświetlenie tego denerwującego widoku akcesoriów.W moim przypadku akcesorium spowodowało błąd, który powodował, że moja aplikacja nie odpowiadała po dotknięciu (co również spowodowało odrzucenie mojej aplikacji w procesie przeglądu aplikacji). Musiałem więc usunąć tę funkcję. Nie chciałem rezygnować z tej funkcji bezpieczeństwa, więc musiałem sam rozwiązywać problemy.
Chodzi o to, aby usunąć funkcję secureEntry, ale dodać ją ręcznie. Zadziałało:
Można to zrobić w ten sposób:
Swift 4-drożny:
Najpierw, zgodnie z odpowiedzią tutaj, ustaw
textContentType
na nic:if #available(iOS 10.0, *) { passwordText.textContentType = UITextContentType("") emailText.textContentType = UITextContentType("") }
Następnie zadeklaruj zmienną typu String, która później będzie zawierała prawdziwą zawartość textField:
var passwordValue = ""
Dodaj cel do passwordTextField, który będzie wywoływany za każdym razem, gdy zmieni się zawartość textField:
passwordText.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
Teraz właśnie to zrobi magię, zadeklaruj funkcję, która będzie obsługiwać zamiany tekstu:
@objc func textFieldDidChange(_ textField: UITextField) { if textField.text!.count > 1 { // User did copy & paste if passwordValue.count == 0 { // Pasted into an empty textField passwordValue = String(textField.text!) } else { // Pasted to a non empty textField passwordValue += textField.text!.substring(from: passwordValue.count) } } else { // User did input by keypad if textField.text!.count > passwordValue.count { // Added chars passwordValue += String(textField.text!.last!) } else if textField.text!.count < passwordValue.count { // Removed chars passwordValue = String(passwordValue.dropLast()) } } self.passwordText.text = String(repeating: "•", count: self.passwordText.text!.count) }
Wreszcie Set textField to
autocorrectionType
aby.no
usunąć tekst predykcyjną:To wszystko, użyj,
passwordValue
aby zalogować się.Mam nadzieję, że to komuś pomoże.
AKTUALIZACJA
Łapie również wklejone wartości, zapomniałem go wcześniej dodać.
źródło
Funkcję można wyłączyć, określając typ zawartości, który nie jest nazwą użytkownika ani hasłem. Na przykład, jeśli użytkownik powinien wprowadzić adres e-mail, możesz użyć
źródło
Możesz dodać rozszerzenie dla UITextContentType w ten sposób
extension UITextContentType { public static let unspecified = UITextContentType("unspecified") }
potem możesz go użyć
if #available(iOS 10.0, *) { passwordField.textContentType = .unspecified }
źródło
w odpowiedzi na @Gal Shahar Answer.
iOS 12 rozpoznaje hasła textFields według
isSecureTextEntry
właściwości, a nie tylko wedługtextContentType
właściwości.Droga dookoła, aby ominąć sugestię automatycznego wypełniania.
isSecureTextEntry
właściwość na false.self.passwordTextField.secureTextEntry = NO;
isSecureTextEntry
właściwość.- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { if (textField == self.passwordTextField && !self.passwordTextField.secureTextEntry) { self.passwordTextField.secureTextEntry = YES; } return YES; }
UWAGA: - NIE używaj
shouldBeginEditing
metody delegata UITextField, nadal będzie wyświetlać sugestię automatycznego wypełniania. Czy NIE ZastosowanietextFieldDidChange
metody delegata UITextField Będzie Autousuwanie pierwszy charachter jak będzie to się stało po wyświetleniu pierwszego charachter. „SecureTextEntry” spowoduje opróżnienie pola.źródło
Bardzo proste podejście w ios11 zadziałało. Załóżmy, że twoje iboutlets to usernametextfield i passwordtextfield. W funkcji viewDidLoad () kontrolera widoku, która przechowuje oba zewnętrzne, użyj następującego kodu
usernametextfield.textContentType = UITextContentType("") passwordtextfield.textContentType = UITextContentType("")
Po tym nie zobaczysz opcji automatycznego uzupełniania po dotknięciu pól tekstowych.
źródło
Cel C
if (@available(iOS 10, *)){ self.tfEmail.textContentType = @""; self.tfPassword.textContentType = @""; }
To zadziałało dla mnie.
źródło
Autouzupełnianie jest domyślnie włączone dla użytkowników. iOS zapisuje wszystkie hasła w pęku kluczy i udostępnia je na klawiaturze w Twoich aplikacjach.
UITextView
iUITextField
automatycznie uwzględniane jako hasło autouzupełniania. możesz wyłączyć, określając typ zawartości, który nie jest nazwą użytkownika ani hasłem, ale jeśli informacje o typie zawartości są już zapisane w pęku kluczy, zostaną wyświetlone na pasku szybkiego dostępu. więc lepiej przypisać pustyUITextContentType
typ i nie wyświetli się pasek szybkiego dostępu.Przykład:
if #available(iOS 10.0, *) { self.textField.textContentType = UITextContentType("") } else { // Fallback on earlier versions }
źródło
To zadziałało dla iOS 12 i 10:
if (@available(iOS 10, *)) { passwordTextField.textContentType = UITextContentTypeStreetAddressLine2; } if (@available(iOS 12, *)) { passwordTextField.textContentType = UITextContentTypeOneTimeCode; }
źródło
Możesz „wyłączyć” wykrywanie kombinacji nazwy użytkownika / hasła, przypisując atrapę
textContentType
do pola tekstowego hasła.passwordFormField.textContentType = UITextContentType("dummy")
Spowoduje to wyłączenie symbolu klucza zarówno dla pola hasła, jak i pola adresu e-mail, które je poprzedzało, dzięki czemu nie używasz jednej z predefiniowanych wartości i unikasz wyświetlania niepowiązanych sugestii w widoku akcesoriów klawiatury.
źródło
newPassword
oznaczam to pole hasła nowym UITextContentType iOS 12 . Nie wiem jednak, jak uniknąć tego nowego zachowania.Wersja celu C:
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"10.0")) { self.passwordTextField.textContentType = @""; self.confirmPasswordTextField.textContentType = @""; }
gdzie
#define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame) #define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending) #define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending) #define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending) #define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)
źródło
Możesz tutaj wypróbować różne odpowiedzi , z których wywnioskować, że prawdopodobnie można usunąć widok akcesoriów. Ale to pozostawia kilka błędów.
Możesz spróbować zaimplementować niestandardową klawiaturę, tylko dla pól haseł. Spróbuj także wyłączyć sugestie dla twojego pola tekstowego, myślę, że to również ukrywa
accessoryView
.EDYCJA: Nadal brak odpowiedzi na forach Apple na to samo pytanie. Nie mogłem też znaleźć nic na ten temat w oficjalnej
UITextField
dokumentacji .źródło
Szalony personel w tym temacie. Zrobiłem bez iOS, proponując mi hasło, ale dałem mi autouzupełnianie tylko dla e-maili. Tylko jeśli ktoś tego potrzebuje. Po różnych kombinacjach i różnych typach
textContentType
zrobiłem to tak, jak chciałem.I z tym kodem zadziałało. Nie ma znaczenia, czy masz,
email
czyusername
też zaproponuje Ci to, czego potrzebujesz. Więc wyłączyłem widok autouzupełniania akcesoriów i zostawiłem tylko autouzupełnianie na pasku narzędzi klawiatury.self.passwordField.isSecureTextEntry = true if #available(iOS 11.0, *) { self.emailField.textContentType = .username self.emailField.keyboardType = .emailAddress } if #available(iOS 12.0, *) { self.passwordField.textContentType = .password self.passwordField.keyboardType = .default }
źródło
Z tego, co wiem, odpowiedź Bema nie działa w iOS 12, a odpowiedź Gal Shahar nie uwzględnia niektórych skrajnych przypadków (na przykład, jeśli użytkownik usuwa wiele znaków jednocześnie). Obejrzałem to za pomocą IBAction, eliminując w ten sposób konieczność sprawdzania wersji iOS. Jestem tylko początkującym, więc to może nie być „najlepsza” odpowiedź lub najbardziej wydajna, ale miała dla mnie największy sens:
Najpierw usuń zaznaczenie opcji „Bezpieczne wprowadzanie tekstu” w serii ujęć lub ustaw ją na „fałsz” / „NIE” za pomocą kodu hasła UITextField. Uniemożliwi to systemowi iOS próbę autouzupełniania.
Następnie połącz swoje hasło UITextField z IBAction. Mój jest wezwany:
Napisana przeze mnie funkcja IBAction określa różnice między hasłem początkowym użytkownika a tym, co zostało wprowadzone do hasła UITextField i tworzy nowe hasło na podstawie tych informacji:
class Login: UIViewController { var password = "" override func viewDidLoad() { super.viewDidLoad() } @IBAction func editPasswordField(_ sender: UITextField) { var input = Array(sender.text ?? "") var oldPassword = Array(password) var newPassword = Array("") //if character(s) are simply deleted from "passwordField" (not replaced or added to), "cursorPosition" is used to determine which corresponding character(s) need to also be removed from "oldPassword" //this is indicated by "input" comprising of only "•" (bullets) and being shorter in length than "oldPassword" var onlyBullets = true for char in input { if char != "•" { onlyBullets = false } } if onlyBullets && input.count < oldPassword.count { if let selectedRange = sender.selectedTextRange { let cursorPosition = sender.offset(from: sender.beginningOfDocument, to: selectedRange.start) let prefix = String(oldPassword.prefix(cursorPosition)) let suffix = String(oldPassword.suffix(input.count - cursorPosition)) input = Array(prefix + suffix) } else { input = Array("") } } //if no changes were made via input, input would comprise solely of a number of bullets equal to the length of "oldPassword" //therefore, the number of changes made to "oldPassword" via "input" can be measured with "bulletDifference" by calculating the number of characters in "input" that are NOT bullets var bulletDifference = oldPassword.count for char in input { if char == "•" { bulletDifference -= 1 } } //the only way "bulletDifference" can be less than 0 is if a user copy-pasted a bullet into "input", which cannot be allowed because it breaks this function //if a user pastes bullet(s) into "input", "input" is deleted //an edge case not accounted for is pasting a mix of characters and bullets (i.e. "ex•mple") when "oldPassword.count" exceeds the number of bullets in the mixed input, but this does not cause crashes and therefore is not worth preventing if bulletDifference < 0 { bulletDifference = oldPassword.count input = Array("") } //"bulletDifference" is used to remove every character from "oldPassword" that corresponds with a character in "input" that has been changed //a changed character in "input" is indicated by the fact that it is not a bullet //once "bulletDifference" equals the number of bullets deleted, this loop ends var bulletsDeleted = 0 for i in 0..<input.count { if bulletsDeleted == bulletDifference { break } if input[i] != "•" { oldPassword.remove(at: i - bulletsDeleted) bulletsDeleted += 1 } } //what remains of "oldPassword" is used to substitute bullets in "input" for appropriate characters to create "newPassword" //for example, if "oldPassword" is "AcbDE" and "input" is "•bc••", then "oldPassword" will get truncated to "ADE" and "newPassword" will equal "A" + "bc" + "DE", or "AbcDE" var i = 0 for char in input { if char == "•" { newPassword.append(oldPassword[i]) i += 1 } else { newPassword.append(char) } } password = String(newPassword) //"passwordField.text" is then converted into a string of bullets equal to the length of the new password to ensure password security in the UI sender.text = String(repeating: "•", count: password.count) } }
Konstruktywna krytyka jest mile widziana!
źródło
Ok, mogę potwierdzić, że na iOS 14 działają bez problemów:
// once this property is declared, textField.keyboardType does // not seem to have any effect in showing the autofill textField.textContentType = UITextContentType.oneTimeCode textField.keyboardType = UIKeyboardType.emailAddress
źródło
self.passwordTextField.autocorrectionType = NO;
Wygląda na to, że nie działa, znak pęku kluczy wciąż tam jest,
self.passwordTextField.textContentType = UITextContentTypeName;
Powyższy kod działa, ale jeśli użytkownicy skonfigurują swoje konto Apple ID, wtedy nazwa identyfikatora Apple zostanie wyświetlona na klawiaturze, której nie można wyłączyć, ustawiając autocorrectionType na Nie, nie jestem pewien, czy Apple nadal udoskonala tę funkcję autouzupełniania , teraz jest dość wadliwy.
źródło
To zadziałało dla mnie:
UWAGA: spróbuj umieścić ten kod w haśle, potwierdzeniu hasła (jeśli dotyczy) ORAZ w polach tekstowych wiadomości e-mail. Nie umieszczałem go w polu tekstowym e-maila i nadal pojawiał się w dwóch polach hasła.
if #available(iOS 12, *) { // iOS 12: Not the best solution, but it works. cell.textField.textContentType = .oneTimeCode } else { // iOS 11: Disables the autofill accessory view. cell.textField.textContentType = .init(rawValue: "") }
źródło
Myślę, że ustawienie całego tekstu UITextField textContentType w formularzu na
UITextContentType("")
lub.oneTimeCode
nie jest czystym rozwiązaniem. Włącz / wyłączisSecureTextEntry
nadal powoduje ten sam problem.Odpowiedź @Gal Shahar jest ładna, ale nadal nie jest doskonała. Zamaskowany znak nie jest tym samym, co znak zamaskowany, który jest używany w tekście bezpiecznego wprowadzania z Apple. Powinien używać znaku Unicode „BLACK CIRCLE” (U + 25CF) https://www.fileformat.info/info/unicode/char/25cf/index.htm
Nie obsługuje również ruchu kursora. Zmieni pozycję kursora na koniec tekstu podczas wstawiania tekstu w środku. Poda złą wartość podczas wybierania i zastępowania tekstu.
Jeśli zdecydujesz się użyć niestandardowego isSecureEntryText, aby uniknąć autouzupełniania hasła, oto kod:
Swift 5 (wersja prosta)
@IBOutlet weak var passwordTextField: UITextField! var maskedPasswordChar: String = "●" var passwordText: String = "" var isSecureTextEntry: Bool = true { didSet { let selectedTextRange = passwordTextField.selectedTextRange passwordTextField.text = isSecureTextEntry ? String(repeating: maskedPasswordChar, count: passwordText.count) : passwordText passwordTextField.selectedTextRange = selectedTextRange } } //this is UITextFieldDelegate func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { if textField == passwordTextField { //update password string if let swiftRange = Range(range, in: passwordText) { passwordText = passwordText.replacingCharacters(in: swiftRange, with: string) } else { passwordText = string } //replace textField text with masked password char textField.text = isSecureTextEntry ? String(repeating: maskedPasswordChar, count: passwordText.count) : passwordText //handle cursor movement if let newPosition = textField.position(from: textField.beginningOfDocument, offset: range.location + string.utf16.count) { textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition) } return false } return true }
Swift 5 (KOMPLETNA wersja z zabezpieczeniem animacji ostatniego znaku)
private struct Constants { static let SecuringLastCharPasswordDelay = 1.5 } @IBOutlet weak var passwordTextField: UITextField! private var secureTextAnimationQueue: [String] = [] var maskedPasswordChar: String = "●" var passwordText: String = "" var isSecureTextEntry: Bool = true { didSet { secureTextAnimationQueue.removeAll() let selectedTextRange = passwordTextField.selectedTextRange passwordTextField.text = isSecureTextEntry ? String(repeating: maskedPasswordChar, count: passwordText.count) : passwordText passwordTextField.selectedTextRange = selectedTextRange } } //this is UITextFieldDelegate func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { if textField == passwordTextField { //update password string if let swiftRange = Range(range, in: passwordText) { passwordText = passwordText.replacingCharacters(in: swiftRange, with: string) } else { passwordText = string } //replace textField text with masked password char updateTextFieldString(textField, shouldChangeCharactersIn: range, replacementString: string) //handle cursor movement if let newPosition = textField.position(from: textField.beginningOfDocument, offset: range.location + string.utf16.count) { textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition) } return false } return true } private func updateTextFieldString(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) { if isSecureTextEntry { if string.count == .one, let text = textField.text { let maskedText = String(repeating: maskedPasswordChar, count: text.count) var newMaskedText = String() if let swiftRange = Range(range, in: maskedText) { newMaskedText = maskedText.replacingCharacters(in: swiftRange, with: string) } else { newMaskedText = text + maskedText } textField.text = newMaskedText secureTextAnimationQueue.append(string) asyncWorker.asyncAfter(deadline: .now() + Constants.SecuringLastCharPasswordDelay) { [weak self] in self?.securingLastPasswordChar() } } else { secureTextAnimationQueue.removeAll() textField.text = String(repeating: maskedPasswordChar, count: passwordText.count) } } else { textField.text = passwordText } } private func securingLastPasswordChar() { guard secureTextAnimationQueue.count > .zero, isSecureTextEntry else { return } secureTextAnimationQueue.removeFirst() if secureTextAnimationQueue.count == .zero { let selectedTextRange = passwordTextField.selectedTextRange passwordTextField.text = String(repeating: maskedPasswordChar, count: passwordText.count) passwordTextField.selectedTextRange = selectedTextRange } }
źródło
Załączam zrzut ekranu. możesz zmienić typ zawartości jako nazwę użytkownika w serii ujęć lub możesz to zrobić programowo. możesz stworzyć rozszerzenie dla UITextfield.
func diableAutofill() { self.autocorrectionType = .no if #available(iOS 11.0, *) { self.textContentType = .username } else { self.textContentType = .init("") } }
źródło
Dzięki Apple nie mogłem znaleźć sposobu na natywne metody, gdy isSecureTextEntry jest ustawione na YES. Sposób Gal Shahara to jedyne rozwiązanie umożliwiające wyłączenie opcji automatycznego uzupełniania hasła w widoku akcesoriów. Ale łatwiej to zrobić
cel C
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
szybki
textField(_:shouldChangeCharactersIn:replacementString:)
delegat. I użyj prostego kodu, takiego jak ten. Zadeklaruj zmienną typu String, która później będzie zawierała prawdziwą zawartość textField, moja to pswd.
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { //Thanks iOS13!!! if(!_pswd) { _pswd = @""; } _pswd = [_pswd stringByReplacingCharactersInRange:range withString:string]; if (!buttonShowPassword.selected) { textField.text = [@"" stringByPaddingToLength:_pswd.length withString: @"•" startingAtIndex:0]; } else { textField.text = _pswd; } return NO; }
źródło
dla SwiftUI wykonaj następujące czynności:
SecureField("Password", text: "some text").disableAutocorrection(true)
źródło