Przenieś widok za pomocą klawiatury za pomocą Swift

278

Mam aplikację, która ma pole tekstowe w dolnej połowie widoku. Oznacza to, że kiedy idę pisać w polu tekstowym, klawiatura pokrywa pole tekstowe.

Jak mógłbym poruszyć widok w górę podczas pisania, aby zobaczyć, co piszę, a następnie przesunąć go z powrotem do pierwotnego miejsca po zniknięciu klawiatury?

Szukałem wszędzie, ale wszystkie rozwiązania wydają się być w Obj-C, których nie mogę jeszcze całkiem przekonwertować.

Każda pomoc byłaby bardzo mile widziana.

Alex Catchpole
źródło
Najlepszym sposobem na to jest umieszczenie treści w UIScrollView , a następnie dostosowanie właściwości contentInset widoku przewijania o wysokość klawiatury, gdy jest wyświetlana. Absolutnie nie zakładaj wysokości klawiatury - użyj wartości z powiadomienia „klawiatura pokaże”.
nielsbot
1
W rzeczywistości dokumenty Apple mówią, jak to zrobić, w sekcji „Zarządzanie klawiaturą”: developer.apple.com/library/ios/documentation/StringsTextFonts/…
nielsbot
11
Myślę, że wszystkie poniższe odpowiedzi nie uwzględniają jednego przypadku: co zrobić, jeśli masz wiele pól tekstowych, a niektóre z nich znajdują się u góry ekranu? Za każdym razem, gdy użytkownik it is actually needed to scroll view up when keyboard appears
dotknie
Ta odpowiedź pozwala wykryć, czy rzeczywiście trzeba przewinąć widok w górę, gdy pojawia się klawiatura, sprawdzając, czy edytowane pole
tekstowe

Odpowiedzi:

710

Oto rozwiązanie bez obsługi przełączania z jednego pola tekstowego na inny:

 override func viewDidLoad() {
        super.viewDidLoad()
        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)            
    }   

func keyboardWillShow(notification: NSNotification) {            
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue() {
        self.view.frame.origin.y -= keyboardSize.height
    }            
}

func keyboardWillHide(notification: NSNotification) {
    self.view.frame.origin.y = 0
}

Aby rozwiązać ten problem, zamień dwie funkcje na keyboardWillShow/Hidenastępujące:

func keyboardWillShow(notification: NSNotification) {        
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue() {
        if view.frame.origin.y == 0 {
            self.view.frame.origin.y -= keyboardSize.height
        }
    }        
}

func keyboardWillHide(notification: NSNotification) {
    if view.frame.origin.y != 0 {
        self.view.frame.origin.y = 0
    }
}

EDYCJA DLA SWIFT 3.0:

override func viewDidLoad() {
    super.viewDidLoad()            
    NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)    
}

@objc func keyboardWillShow(notification: NSNotification) {        
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        if self.view.frame.origin.y == 0 {
            self.view.frame.origin.y -= keyboardSize.height
        }
    }        
}

@objc func keyboardWillHide(notification: NSNotification) {
    if self.view.frame.origin.y != 0 {
        self.view.frame.origin.y = 0
    }
}

EDYCJA DLA SWIFT 4.0:

override func viewDidLoad() {
    super.viewDidLoad()            
    NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)    
}

@objc func keyboardWillShow(notification: NSNotification) {        
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        if self.view.frame.origin.y == 0 {
            self.view.frame.origin.y -= keyboardSize.height
        }
    }        
}

@objc func keyboardWillHide(notification: NSNotification) {
    if self.view.frame.origin.y != 0 {
        self.view.frame.origin.y = 0
    }
}

EDYCJA SWIFT 4.2:

override func viewDidLoad() {
    super.viewDidLoad()            
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}

@objc func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        if self.view.frame.origin.y == 0 {
            self.view.frame.origin.y -= keyboardSize.height
        }
    }
}

@objc func keyboardWillHide(notification: NSNotification) {
    if self.view.frame.origin.y != 0 {
        self.view.frame.origin.y = 0
    }
}
Boris
źródło
56
Jeśli użytkownik dotknie innego pola tekstowego, gdy klawiatura jest obecna, widok zostanie przesunięty dalej w górę, co powoduje czarny obszar (rozmiar klawiatury) - musimy to naprawić, wprowadzając zmienną, która śledzi, czy klawiatura jest obecna, czy nie. . np. jeśli keyboardPresent == true, to nie przesuwaj początku widoku itp. etc
jonprasetyo
3
@Matthew Lin używa wartości logicznej, więc funkcje keyboardWillShow and hide działają tylko raz
iluvatar_GR
11
Tylko jedna sugestia, abyś nie musiał dużo debugować tak jak ja. Jeśli masz wiele pól uitextfield na tym samym ekranie, rozmiar klawiatury może się różnić (nie wyświetla sugestii dla niektórych danych wejściowych opartych na twoich ustawieniach), więc zaleca się ustawienie self.view.frame.origin.y = 0, za każdym razem usuwasz klawiaturę. Na przykład pokazywałby duże zmiany w polu tekstowym wiadomości e-mail, więc rozmiar klawiatury zwiększyłby się i nie pokazał sugestii dotyczących pola hasła, więc rozmiar klawiatury zmniejszyłby się.
Vandan Patel,
36
Musisz użyć UIKeyboardFrameEndUserInfoKeyraczej niż UIKeyboardFrameBeginUserInfoKeypodczas uzyskiwania rozmiaru klawiatury. Nie jestem pewien, dlaczego w tej chwili, ale te pierwsze przyniosą bardziej spójne wyniki.
jshapy8
3
Proszę wymienić UIKeyboardFrameBeginUserInfoKeyz UIKeyboardFrameEndUserInfoKey. Pierwsza daje ramkę początkową klawiatury, która czasami jest zerowa, a druga ramkę końcową klawiatury.
Arnab
90

Najłatwiejszy sposób, który nawet nie wymaga kodu:

  1. Pobierz KeyboardLayoutConstraint.swift i dodaj (przeciągnij i upuść) plik do swojego projektu, jeśli nie korzystasz już ze środowiska animacji Spring.
  2. W swoim scenorysie utwórz dolne ograniczenie dla pola Widok lub pola tekstowego, wybierz ograniczenie (kliknij je dwukrotnie), a następnie w Inspektorze tożsamości zmień jego klasę z NSLayoutConstraint na KeyboardLayoutConstraint.
  3. Gotowe!

Obiekt automatycznie przesunie się w górę za pomocą klawiatury, w synchronizacji.

gammachill
źródło
4
Aby wybrać dolne ograniczenie, możesz także przejść do Inspektora rozmiarów, a następnie kliknąć dwukrotnie ograniczenie na liście - raywenderlich.com/wp-content/uploads/2015/09/…
gammachill
3
To działało idealnie dla mnie. jest to dosłownie dwuetapowy proces. 1. Dodaj KeyboardLayoutConstraint.swift, 2. W scenorysie utwórz dolne ograniczenie dla widoku lub pola tekstowego. UWAGA: Usunąłem moje ograniczenia i dodałem tylko 1 ograniczenie na dole widoku lub pola tekstowego i zmieniłem jego klasę z NSLayoutConstraint na KeyboardLayoutConstraint. Następnie wszelkie widoki / pola tekstowe itp. Powyżej po prostu połączyłem ograniczenia z tego elementu do elementu za pomocą jednego KeyboardLayoutConstraint, a wynikiem były wszystkie elementy w widoku przesunięte w GÓRĘ / W DÓŁ, ​​gdy Klawiatura pojawia się / Znika
Brian
4
Jest to najlepsze rozwiązanie, podany kod nie koduje na stałe żadnych wartości, takich jak długość lub krzywa animacji lub rozmiar klawiatury. Jest również łatwy do zrozumienia.
miguelSantirso
3
Działa to dla mnie, ale dostaję dodatkowe 50 pikseli przestrzeni między górą klawiatury a dolną częścią scrollView. Zastanawiam się, czy jest to spowodowane dolnym ograniczeniem Bezpiecznego obszaru, którego używam. Ktoś na to wpadł?
Clifton Labrum,
1
To była niesamowita odpowiedź. Bardzo fajny design. Jedna sugestia: jeśli twoje widoki / pola tekstowe znajdują się w komórkach widoku tabeli, możesz zauważyć, że widoki posiadające to ograniczenie będą skakać niezręcznie za każdym razem, gdy użytkownik kliknie enter i przejdzie do następnego pola tekstowego. Możesz owinąć animacje, DispatchQueue.main.async {}aby to naprawić. Dobra robota! Kciuki w górę!
ScottyBlades
68

Jedna z popularnych odpowiedzi w tym wątku używa następującego kodu:

func keyboardWillShow(sender: NSNotification) {
    self.view.frame.origin.y -= 150
}
func keyboardWillHide(sender: NSNotification) {
    self.view.frame.origin.y += 150
}

Istnieje oczywisty problem z przesunięciem widoku o wartość statyczną. Będzie ładnie wyglądać na jednym urządzeniu, ale będzie źle wyglądać przy dowolnej konfiguracji rozmiaru. Musisz uzyskać wysokość klawiatury i użyć jej jako wartości przesunięcia.

Oto rozwiązanie, które działa na wszystkich urządzeniach i obsługuje wielkość liter, w której użytkownik ukrywa predykcyjne pole tekstowe podczas pisania.

Rozwiązanie

Ważne, aby pamiętać, że jako parametr obiektu przekazujemy self.view.window. To zapewni nam dane z naszej klawiatury, takie jak jej wysokość!

@IBOutlet weak var messageField: UITextField!

override func viewDidLoad() {
    super.viewDidLoad()

    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: self.view.window)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: self.view.window)
}

func keyboardWillHide(sender: NSNotification) {
    let userInfo: [NSObject : AnyObject] = sender.userInfo!
    let keyboardSize: CGSize = userInfo[UIKeyboardFrameBeginUserInfoKey]!.CGRectValue.size
    self.view.frame.origin.y += keyboardSize.height
}

Sprawimy, że będzie ładnie wyglądał na wszystkich urządzeniach i obsłuży przypadek, w którym użytkownik dodaje lub usuwa przewidywane pole tekstowe.

func keyboardWillShow(sender: NSNotification) {
    let userInfo: [NSObject : AnyObject] = sender.userInfo!
    let keyboardSize: CGSize = userInfo[UIKeyboardFrameBeginUserInfoKey]!.CGRectValue.size
    let offset: CGSize = userInfo[UIKeyboardFrameEndUserInfoKey]!.CGRectValue.size

    if keyboardSize.height == offset.height {
        UIView.animateWithDuration(0.1, animations: { () -> Void in
            self.view.frame.origin.y -= keyboardSize.height
        })
    } else {
        UIView.animateWithDuration(0.1, animations: { () -> Void in
            self.view.frame.origin.y += keyboardSize.height - offset.height
        })
    }
}

Usuń obserwatorów

Nie zapomnij usunąć obserwatorów przed opuszczeniem widoku, aby zapobiec przesyłaniu niepotrzebnych wiadomości.

override func viewWillDisappear(animated: Bool) {
    NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: self.view.window)
    NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: self.view.window)
}

Aktualizacja na podstawie pytania z komentarzy:

Jeśli masz dwa lub więcej pól tekstowych, możesz sprawdzić, czy wartość view.frame.origin.y wynosi zero.

func keyboardWillShow(sender: NSNotification) {
    let userInfo: [NSObject : AnyObject] = sender.userInfo!

    let keyboardSize: CGSize = userInfo[UIKeyboardFrameBeginUserInfoKey]!.CGRectValue.size
    let offset: CGSize = userInfo[UIKeyboardFrameEndUserInfoKey]!.CGRectValue.size

    if keyboardSize.height == offset.height {
        if self.view.frame.origin.y == 0 {
            UIView.animateWithDuration(0.1, animations: { () -> Void in
                self.view.frame.origin.y -= keyboardSize.height
            })
        }
    } else {
        UIView.animateWithDuration(0.1, animations: { () -> Void in
            self.view.frame.origin.y += keyboardSize.height - offset.height
        })
    }
     print(self.view.frame.origin.y)
}
Dan Beaulieu
źródło
1
w przypadku wielu pól tekstowych widok przesuwa się w górę i nie wraca w dół
Mugunthan Balakrishnan,
Musisz zmienić swoje warunki, aby uwzględnić pola tekstowe
Dan Beaulieu,
dzięki za odpowiedź, znalazłem odpowiedź, której szukałem w tym wątku na przepełnieniu stosu stackoverflow.com/questions/1126726/…
Mugunthan Balakrishnan
@MugunthanBalakrishnan dzięki za poruszenie tego problemu, dodałem rozwiązanie.
Dan Beaulieu
Cześć chłopaki, jest błąd. Obserwatorzy nie są usuwani z widoku po wywołaniu w viewWillDisappear. Zamień ten wiersz „NSNotificationCenter.defaultCenter (). RemoveObserver (self, name: UIKeyboardWillHideNotification, object: self.view.window)” na „NSNotificationCenter.defaultCenter (). RemoveObserver (self, nazwa: UIKeyboardWillHideNotification, obiekt: brak) następnie” został usunięty
Rudald
29

Dodaj to do kontrolera widoku. Działa jak marzenie. Po prostu dostosuj wartości.

override func viewDidLoad() {
    super.viewDidLoad()        
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name:NSNotification.Name.UIKeyboardWillShow, object: nil);
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name:NSNotification.Name.UIKeyboardWillHide, object: nil);
}

@objc func keyboardWillShow(sender: NSNotification) {
    self.view.frame.origin.y -= 150
}
@objc func keyboardWillHide(sender: NSNotification) {
    self.view.frame.origin.y += 150
}
użytkownik3677173
źródło
To działa dla mnie. Jest to jednak trochę gwałtowne. Jak mogę to zrobić, aby przejść gładko w górę? istnieje również sposób, aby zastosować go tylko do jednego z pól tekstowych, ponieważ obecnie robi to dla wszystkich. :(
DannieCoderBoi
2
Może nie działać z „automatycznym układem”, więc rozważ wyłączenie go, jeśli tak jest.
Teodor Ciuraru,
1
To powoduje pewne funky zachowanie z autolayout @Josh, mylisz się
Mike
5
Nie rób tego! Nie można założyć, że klawiatura ma określony rozmiar.
nielsbot
2
Powinieneś użyć keyboardSize. Co się stanie, gdy będziesz mieć widoki akcesoriów i różne wysokości klawiatury na urządzeniach? Odłączona klawiatura?
xtrinch
25

Ulepszyłem nieco jedną z odpowiedzi, aby działała z różnymi klawiaturami i różnymi widokami / polami tekstowymi na jednej stronie:

Dodaj obserwatorów:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange(notification:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)

    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}

func keyboardWillHide() {
    self.view.frame.origin.y = 0
}

func keyboardWillChange(notification: NSNotification) {

    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        if YOURTEXTVIEW.isFirstResponder {
            self.view.frame.origin.y = -keyboardSize.height
        }
    }
}

Usuń obserwatorów:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
}
scipianne
źródło
3
to rozwiązanie działa lepiej niż zaakceptowana odpowiedź. Zaakceptowana odpowiedź pokazuje klawiaturę tylko raz, co dla mnie jest błędem :)
Masih
Działa to dla Xcode 10.1 i iOS 12. Akceptowana odpowiedź nie jest już ważna.
zeeshan
To doskonała odpowiedź, jedyne, co chciałbym dodać, to śledzenie dolnej bezpiecznej strefy w nowszych urządzeniach (X, XS itp.), Aby to uwzględnić.
Munib
@Munib Patrz stackoverflow.com/a/54993623/1485230 Inne problemy to brak animacji widoku i nieprzestrzeganie zmiany wysokości klawiatury.
Antzi
co jeśli mój textField jest w górnej części widoku? mam na myśli, jeśli jest pole tekstowe o początku Y = 0 .. ?? potem textField idzie w górę i nie mogę tego zobaczyć
Saifan Nadaf
18

Nie reklama, promocja ani spam , tylko dobre rozwiązanie. Wiem, że to pytanie ma prawie 30 odpowiedzi i jestem tak zszokowany, że nikt nawet nie wspomniał o tym pięknym projekcie GitHub, który robi to wszystko dla Ciebie, a nawet lepiej. Wszystkie odpowiedzi przesuwają widok w górę. Właśnie rozwiązałem wszystkie moje problemy z tym IQKeyboardManager. Ma ponad 13000 gwiazd.
Po prostu dodaj to do pliku podfile, jeśli używasz szybkiego

pod 'IQKeyboardManagerSwift'

a następnie wewnątrz AppDelegate.swift import IQKeyboardManagerSwift

import IQKeyboardManagerSwift

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

      IQKeyboardManager.shared.enable = true // just add this line

      return true
    }
}

Dodaj linię, IQKeyboardManager.shared.enable = trueaby ją włączyć
To rozwiązanie jest koniecznością, jeśli wybierasz się na produkcję.

weegee
źródło
To jest naprawdę dobre, ale najnowsza wersja nie działa dla mnie, użyłem 6.2.1 i importuję jako import IQKeyboardManageri użyłem IQKeyboardManager.shared().isEnabled = truew AppDelegate
Dhanu K
2
Działa to niesamowicie, gdy używa się wielu tekstów do edycji. To oszczędza mój czas
Dhanu K
1
Nie mogę podziękować za wskazanie tej wspaniałej biblioteki. Ta biblioteka jest w końcu OSTATECZNĄ ODPOWIEDZIĄ na wszystkie bzdury związane z klawiaturą, dla których Apple nigdy nie zapewnił rozwiązania. Teraz będę go używać do wszystkich moich projektów, nowych i starych, i zaoszczędzę czas i ból głowy, które pojawia się, znika lub nie znika, lub jak to ukryć i dlaczego się nakłada, problemy powodują mnie od czasu dzień programuję dla iPhone'ów.
zeeshan
1
Napisałem własny kod, aby przesunąć pole tekstowe, podobnie jak inne rozwiązania tutaj. Przekonałem się, że IQKeyboardManager jest bardzo dobry i odtąd go włączę. Zachowam mój kod pod ręką na wypadek, gdyby framework nie był aktualizowany.
TM Lynch
@DhanuK, właśnie znalazłem tę bibliotekę i działa idealnie i nie wymaga wysiłku. Kod delegowania aplikacji został zaktualizowany do IQKeyboardManager.shared.enable = true
adougies
15

Błąd czarnego ekranu (Swift 4 i 4.2) .

Naprawiłem problem z czarnym ekranem. W sprawdzonym rozwiązaniu Wysokość klawiatury zmienia się po stuknięciu, co powoduje czarny ekran.

Muszę użyć UIKeyboardFrameEndUserInfoKey zamiast UIKeyboardFrameBeginUserInfoKey

var isKeyboardAppear = false

override func viewDidLoad() {
    super.viewDidLoad() 
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

@objc func keyboardWillShow(notification: NSNotification) {
    if !isKeyboardAppear {
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
            if self.view.frame.origin.y == 0{
                self.view.frame.origin.y -= keyboardSize.height
            }
        }
        isKeyboardAppear = true
    }
}

@objc func keyboardWillHide(notification: NSNotification) {
    if isKeyboardAppear {
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
            if self.view.frame.origin.y != 0{
                self.view.frame.origin.y += keyboardSize.height
            }
        }
         isKeyboardAppear = false
    }
}
Ali Ihsan URAL
źródło
Nie będzie działać, jeśli istnieje pasek kart. Musisz obliczyć wysokość paska kart, w przeciwnym razie między klawiaturą a widokiem pojawi się czarny ekran.
Ankur Lahiry
Nie naprawia to czarnego obszaru, w którym klawiatura znajdowała się na iPhonie X i nowszych. I za każdym razem, gdy pojawia się i znika klawiatura, główny widok przesuwa się w dół.
Edison,
14

Widzę, że wszystkie odpowiedzi przesuwają sam widok o wartość wysokości klawiatury. Cóż, mam szczegółową odpowiedź, która może być użyteczna, jeśli używasz ograniczeń, tj. Które autolayoutporuszają widok, zmieniając jego wartość ograniczenia (na przykład dolne lub górne ograniczenie) o określoną wartość lub możesz użyć wartości rozmiaru klawiatury.

W tym przykładzie używam ograniczenia dolnego z pola tekstowego do Widoku układu dolnego o wartości początkowej 175.

@IBOutlet weak var bottomConstraint: NSLayoutConstraint!

override func viewDidLoad() {
    super.viewDidLoad()        
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil);
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil);
}

func keyboardWillShow(notification: NSNotification) {
    //To retrieve keyboard size, uncomment following line
    //let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue()
    bottomConstraint.constant = 260
    UIView.animateWithDuration(0.3) {
        self.view.layoutIfNeeded()
    }
}

func keyboardWillHide(notification: NSNotification) {
    //To retrieve keyboard size, uncomment following line
    //let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue()
    bottomConstraint.constant = 175
    UIView.animateWithDuration(0.3) {
        self.view.layoutIfNeeded()
    }
}
Amro Shafie
źródło
Cześć, proszę pana, czy mógłbyś mi powiedzieć, dlaczego to nie działa, gdy zostanie umieszczony w widoku, który zawiera również TableView? Działa dobrze w tym samym scenariuszu, gdy zawiera CollectionView.
elarcoiris
9

Nastąpiły pewne zmiany w sposobie definiowania KeyboardWillHideNotification.

To rozwiązanie działa z Swift 4.2 :

NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)


@objc func keyboardWillShow(_ notification:Notification) {
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        self.view.frame.origin.y -= keyboardSize.height
    }
}

@objc func keyboardWillHide(_ notification:Notification) {
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        self.view.frame.origin.y += keyboardSize.height
    }
}
Ivan Le Hjelmeland
źródło
2
co jeśli mój textField jest w górnej części widoku? mam na myśli, jeśli jest pole tekstowe o początku Y = 0 .. ?? potem textField idzie w górę i nie mogę tego zobaczyć
Saifan Nadaf
7

Swift 5.0:

Po 4-5 godzinach walki przyszedłem z prostym rozszerzeniem UIViewController z prostym kodem, który działa jak urok

* Widok nie powinien się poruszać, gdy TextField znajduje się nad klawiaturą

* Nie ma potrzeby ustawiania stałej wartości na NSLayoutConstraint

* Nie wymaga biblioteki innej firmy

* Nie wymaga kodu animacji

* Działa również w widoku tabeli

* To działa na Auto układ / auto zmiana rozmiaru

extension UIViewController {
    func addKeyboardObserver() {
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardNotifications(notification:)),
                                               name: UIResponder.keyboardWillChangeFrameNotification,
                                               object: nil)
    }

    func removeKeyboardObserver(){
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
    }

    // This method will notify when keyboard appears/ dissapears
    @objc func keyboardNotifications(notification: NSNotification) {

        var txtFieldY : CGFloat = 0.0  //Using this we will calculate the selected textFields Y Position
        let spaceBetweenTxtFieldAndKeyboard : CGFloat = 5.0 //Specify the space between textfield and keyboard


        var frame = CGRect(x: 0, y: 0, width: 0, height: 0)
        if let activeTextField = UIResponder.currentFirst() as? UITextField ?? UIResponder.currentFirst() as? UITextView {
            // Here we will get accurate frame of textField which is selected if there are multiple textfields
            frame = self.view.convert(activeTextField.frame, from:activeTextField.superview)
            txtFieldY = frame.origin.y + frame.size.height
        }

        if let userInfo = notification.userInfo {
            // here we will get frame of keyBoard (i.e. x, y, width, height)
            let keyBoardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
            let keyBoardFrameY = keyBoardFrame!.origin.y
            let keyBoardFrameHeight = keyBoardFrame!.size.height

            var viewOriginY: CGFloat = 0.0
            //Check keyboards Y position and according to that move view up and down
            if keyBoardFrameY >= UIScreen.main.bounds.size.height {
                viewOriginY = 0.0
            } else {
                // if textfields y is greater than keyboards y then only move View to up
                if txtFieldY >= keyBoardFrameY {

                    viewOriginY = (txtFieldY - keyBoardFrameY) + spaceBetweenTxtFieldAndKeyboard

                    //This condition is just to check viewOriginY should not be greator than keyboard height
                    // if its more than keyboard height then there will be black space on the top of keyboard.
                    if viewOriginY > keyBoardFrameHeight { viewOriginY = keyBoardFrameHeight }
                }
            }

            //set the Y position of view
            self.view.frame.origin.y = -viewOriginY
        }
    }
}

Dodaj to rozszerzenie UIResponder, aby dowiedzieć się, który TextField jest wybrany

extension UIResponder {

    static weak var responder: UIResponder?

    static func currentFirst() -> UIResponder? {
        responder = nil
        UIApplication.shared.sendAction(#selector(trap), to: nil, from: nil, for: nil)
        return responder
    }

    @objc private func trap() {
        UIResponder.responder = self
    }
}

Następnie użyj tego w dowolnym ViewController

   override func viewWillAppear(_ animated: Bool) {
        self.addKeyboardObserver()
    }

    override func viewWillDisappear(_ animated: Bool) {
        self.removeKeyboardObserver()
    }

  • Zarejestruj to powiadomienie w func viewWillAppear(_ animated: Bool)

  • Wyrejestruj to powiadomienie w func viewWillDisappear(_ animated:Bool)

    Pobierz wersję demo tutaj

Saifan Nadaf
źródło
To wyglądało na najlepsze rozwiązanie, jednak jest kilka błędów. 1, pole tekstowe przesuwa się w górę, ale kiedy zaczynam pisać, podskakuje jeszcze trochę. 2, W orientacji poziomej podczas pisania pola tekstowego czasami przeskakuje w lewo.
Darren
@Darren próbuję dowiedzieć się o tych błędach, ale nie znalazłem, czy możesz powiedzieć, skąd je masz, mam na myśli, dla której wersji / urządzenia ... ??
Saifan Nadaf
6

Dla Swift 3 stworzyłem podklasę UIViewController, ponieważ potrzebowałem stałego zachowania we wszystkich kontrolerach widoku.

class SomeClassVC: UIViewController {

  //MARK: - Lifecycle
  override func viewDidLoad() {
    super.viewDidLoad()

    addKeyboardObservers()
  }

  override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    removeKeyboardObservers()
  }

  //MARK: - Overrides
  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    view.endEditing(true)
  }

  //MARK: - Help
  func addKeyboardObservers() {
    NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
  }

  func removeKeyboardObservers() {
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: self.view.window)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: self.view.window)
  }

  func keyboardWillShow(notification: NSNotification) {
    let keyboardHeight = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.height
    UIView.animate(withDuration: 0.1, animations: { () -> Void in
      self.view.window?.frame.origin.y = -1 * keyboardHeight!
      self.view.layoutIfNeeded()
    })
  }

  func keyboardWillHide(notification: NSNotification) {
    UIView.animate(withDuration: 0.1, animations: { () -> Void in
      self.view.window?.frame.origin.y = 0
      self.view.layoutIfNeeded()
    })
  }

  func resignTextFieldFirstResponders() {
    for textField in self.view.subviews where textField is UITextField {
      textField.resignFirstResponder()
    }
  }

  func resignAllFirstResponders() {
      view.endEditing(true)
  }
}
Pavle Mijatovic
źródło
Zainspirowany rozwiązaniem Pavle'a uaktualniłem go, aby automatycznie podnieść klawiaturę o pewien procent pozostałej dostępnej przestrzeni, a także znaleźć rekurencyjne pole rekurencyjnie dla właściwego układu. Złap go tutaj: gist.github.com/noordawod/24d32b2ce8363627ea73d7e5991009a0
Noor Dawod
Mój pasek kart również przesuwa się w górę wraz z oknem! :(
Alfi
6

Zweryfikowana odpowiedź nie uwzględnia pozycji pola tekstowego i zawiera pewne błędy (podwójne przesunięcie, nigdy nie wraca do pierwotnej pozycji, przesunięcie, nawet jeśli pole tekstowe znajduje się nad widokiem ...)

Chodzi o to:

  • aby uzyskać punkt skupienia bezwzględnej pozycji Y TextField
  • aby uzyskać wysokość klawiatury
  • aby uzyskać ScreenHeight
  • Następnie oblicz odległość między pozycją klawiatury a polem tekstowym (jeśli <0 -> przesuń widok w górę)
  • użyć UIView.transform zamiast UIView.frame.origin.y - = .., ponieważ łatwiej jest wrócić do pierwotnej pozycji dzięki UIView.transform = .identity

wtedy będziemy mogli przesuwać widok tylko w razie potrzeby i określonego przesunięcia w odrze, aby skupić pole tekstowe tuż nad klawiaturą

Oto kod:

Szybki 4

class ViewController: UIViewController, UITextFieldDelegate {

var textFieldRealYPosition: CGFloat = 0.0

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(self, selector: #selector(VehiculeViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(VehiculeViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)

  // Delegate all textfields

}


@objc func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        let distanceBetweenTextfielAndKeyboard = self.view.frame.height - textFieldRealYPosition - keyboardSize.height
        if distanceBetweenTextfielAndKeyboard < 0 {
            UIView.animate(withDuration: 0.4) {
                self.view.transform = CGAffineTransform(translationX: 0.0, y: distanceBetweenTextfielAndKeyboard)
            }
        }
    }
}


@objc func keyboardWillHide(notification: NSNotification) {
    UIView.animate(withDuration: 0.4) {
        self.view.transform = .identity
    }
}


func textFieldDidBeginEditing(_ textField: UITextField) {
  textFieldRealYPosition = textField.frame.origin.y + textField.frame.height
  //take in account all superviews from textfield and potential contentOffset if you are using tableview to calculate the real position
}

}

Quentin N.
źródło
Bardzo dobrze! (W viewDidLoad masz „VehicleiculeViewController” zamiast tylko „ViewController”).
przedłużenie
O wiele bardziej kompletna i przydatna odpowiedź. Dziękuję Ci! Sugeruję, aby sprawdzenie klawiatury było wywoływane w następujący sposób, ponieważ zapewni spójny rozmiar klawiatury ......... jeśli pozwala keyboardSize = (powiadomienie.userInfo? [UIResponder.keyboardFrameEndUserInfoKey] jako? NSValue) ?. cgRectValue .size
Ben
4

Zauważyłem, że inne odpowiedzi obejmowały wycięcie części góry z widoku. Jeśli chcesz po prostu zmienić rozmiar widoku bez wycinania zawartości, po prostu spróbuj tej metody :)

func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        self.view.setTranslatesAutoresizingMaskIntoConstraints(true)
        self.view.frame = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.height - keyboardSize.height)
    }
}

func keyboardWillHide(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        self.collectionView.setTranslatesAutoresizingMaskIntoConstraints(false)
        self.view.frame = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.height + keyboardSize.height)
    }
}
Grant Park
źródło
4

Moje dwa centy dla początkujących: w powyższych próbkach ktoś zmienia współrzędne, inne używa „maski autoresize” i inne ograniczenia:

Jak mówi Apple, nie mieszaj tych 3 rodzajów logiki. Jeśli masz ograniczenia w Storyboard, nie próbuj zmieniać x / y. To zdecydowanie nie działa.

ingconti
źródło
4

Więc żadna z pozostałych odpowiedzi nie wydaje się poprawna.

Klawiatura Good Behaviored na iOS powinna:

  • Zmień rozmiar automatycznie, gdy klawiatura zmieni rozmiary (TAK, MOŻE)
  • Animuj z taką samą prędkością jak klawiatura
  • Animuj przy użyciu tej samej krzywej co klawiatura
  • Przestrzegaj bezpiecznych obszarów, jeśli to konieczne.
  • Działa również w trybie iPad / Undocked

Mój kod używa NSLayoutConstraintdeklarowanego jako@IBOutlet

@IBOutlet private var bottomLayoutConstraint: NSLayoutConstraint!

Możesz także użyć transformacji, zobaczyć przesunięcia, .... Myślę, że łatwiej jest z ograniczeniem tho. Działa poprzez ustawienie ograniczenia na dole, może być konieczna zmiana kodu, jeśli twoja stała nie jest równa 0 / Not na dole.

Oto kod:

// In ViewDidLoad
    NotificationCenter.default.addObserver(self, selector: #selector(?MyViewController.keyboardDidChange), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)


@objc func keyboardDidChange(notification: Notification) {
    let userInfo = notification.userInfo! as [AnyHashable: Any]
    let endFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
    let animationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber
    let animationCurve = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as! NSNumber
    bottomLayoutConstraint.constant = view.frame.height - endFrame.origin.y - view.safeAreaInsets.bottom // If your constraint is not defined as a safeArea constraint you might want to skip the last part.
    // Prevents iPad undocked keyboard.
    guard endFrame.height != 0, view.frame.height == endFrame.height + endFrame.origin.y else {
        bottomLayoutConstraint.constant = 0
        return
    }
    UIView.setAnimationCurve(UIView.AnimationCurve(rawValue: animationCurve.intValue)!)
    UIView.animate(withDuration: animationDuration.doubleValue) {
        self.view.layoutIfNeeded()
        // Do additional tasks such as scrolling in a UICollectionView
    }
}
Antzi
źródło
3

jego 100% idealna odpowiedź na wszystkie aktualizacje Guy's Tableview Height po otwarciu klawiatury

Dla Swift 4.2

   override func viewDidLoad() {
      super.viewDidLoad()
      NotificationCenter.default.addObserver(self, selector: #selector(RecipeVC.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)

      NotificationCenter.default.addObserver(self, selector: #selector(RecipeVC.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
   }

   @objc func keyboardWillShow(notification: NSNotification) {
    if ((notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue) != nil {

        var userInfo = notification.userInfo!
        var keyboardFrame:CGRect = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
        keyboardFrame = self.view.convert(keyboardFrame, from: nil)

        var contentInset:UIEdgeInsets = self.tbl.contentInset
          contentInset.bottom = keyboardFrame.size.height
          self.tbl.contentInset = contentInset
    }
}

   @objc func keyboardWillHide(notification: NSNotification) {
    if ((notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue) != nil {
        let contentInset:UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
        self.tbl.contentInset = contentInset
    }
}

Swift3.2

    override func viewDidLoad() {
          super.viewDidLoad()

           NotificationCenter.default.addObserver(self, selector: #selector(RecipeVC.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)

           NotificationCenter.default.addObserver(self, selector: #selector(RecipeVC.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    }
    func keyboardWillShow(notification: NSNotification) {
         if ((notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue) != nil {
         //self.view.frame.origin.y -= keyboardSize.height
         var userInfo = notification.userInfo!
         var keyboardFrame:CGRect = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
          keyboardFrame = self.view.convert(keyboardFrame, from: nil)

          var contentInset:UIEdgeInsets = self.tbl.contentInset
          contentInset.bottom = keyboardFrame.size.height
          self.tbl.contentInset = contentInset

       }
    }

    func keyboardWillHide(notification: NSNotification) {
         if ((notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue) != nil {
         let contentInset:UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
         self.tbl.contentInset = contentInset
         }
    }
Maulik Patel
źródło
To najlepsza odpowiedź. tbl powinien być tableView i dodałem trochę dopełnienia: contentInset.bottom = keyboardFrame.size.height + 10
iphaaw
2

Dla Swift 3

func textFieldDidBeginEditing(_ textField: UITextField) { // became first responder

    //move textfields up
    let myScreenRect: CGRect = UIScreen.main.bounds
    let keyboardHeight : CGFloat = 216

    UIView.beginAnimations( "animateView", context: nil)
    var movementDuration:TimeInterval = 0.35
    var needToMove: CGFloat = 0

    var frame : CGRect = self.view.frame
    if (textField.frame.origin.y + textField.frame.size.height + UIApplication.shared.statusBarFrame.size.height > (myScreenRect.size.height - keyboardHeight - 30)) {
        needToMove = (textField.frame.origin.y + textField.frame.size.height + UIApplication.shared.statusBarFrame.size.height) - (myScreenRect.size.height - keyboardHeight - 30);
    }

    frame.origin.y = -needToMove
    self.view.frame = frame
    UIView.commitAnimations()
}

func textFieldDidEndEditing(_ textField: UITextField) {
    //move textfields back down
    UIView.beginAnimations( "animateView", context: nil)
    var movementDuration:TimeInterval = 0.35
    var frame : CGRect = self.view.frame
    frame.origin.y = 0
    self.view.frame = frame
    UIView.commitAnimations()
}
Celil Bozkurt
źródło
2

Swift 4:

Miałem problem z najczęściej akceptowaną odpowiedzią, w której ukrywanie klawiatury nie przywracało widoku do końca strony (tylko częściowo). To zadziałało dla mnie (+ zaktualizowane dla Swift 4).

override func viewDidLoad() {
    super.viewDidLoad()
    self.hideKeyboardWhenTappedAround()
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

@objc func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        if self.view.frame.origin.y == 0{
            self.view.frame.origin.y -= keyboardSize.height
        }
    }
}

@objc func keyboardWillHide(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        if self.view.frame.origin.y != 0{
            self.view.frame.origin.y = 0
        }
    }
}
Rbar
źródło
co jeśli mój textField jest w górnej części widoku? mam na myśli, jeśli jest pole tekstowe o początku Y = 0 .. ?? potem textField idzie w górę i nie mogę tego zobaczyć
Saifan Nadaf
2

Podobne do odpowiedzi @ Boris, ale w Swift 5 :

override func viewDidLoad() {
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}

@IBAction func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        if self.view.frame.origin.y == 0 {
            self.view.frame.origin.y -= keyboardSize.height
        }
    }
}

@IBAction func keyboardWillHide(notification: NSNotification) {
    if self.view.frame.origin.y != 0 {
        self.view.frame.origin.y = 0
    }
}
Jerry Chong
źródło
1

Oto moje rozwiązanie (w rzeczywistości ten kod dotyczy przypadku, gdy masz niewiele pól tekstowych w widoku, działa to również w przypadku, gdy masz jedno pole tekstowe)

class MyViewController: UIViewController, UITextFieldDelegate {

@IBOutlet weak var firstTextField: UITextField!
@IBOutlet weak var secondTextField: UITextField!

var activeTextField: UITextField!
var viewWasMoved: Bool = false


override func viewDidLoad() {
    super.viewDidLoad()
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(PrintViewController.keyboardWillShow(_:)), name: UIKeyboardWillShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(PrintViewController.keyboardWillHide(_:)), name: UIKeyboardWillHideNotification, object: nil)
}

override func viewDidDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

func textFieldDidBeginEditing(textField: UITextField) {
    self.activeTextField = textField
}

func textFieldDidEndEditing(textField: UITextField) {
    self.activeTextField = nil
}

func textFieldShouldReturn(textField: UITextField) -> Bool {
    textField.resignFirstResponder()
    return true
}


func keyboardWillShow(notification: NSNotification) {

    let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue()

    var aRect: CGRect = self.view.frame
    aRect.size.height -= keyboardSize!.height

    let activeTextFieldRect: CGRect? = activeTextField?.frame
    let activeTextFieldOrigin: CGPoint? = activeTextFieldRect?.origin

    if (!CGRectContainsPoint(aRect, activeTextFieldOrigin!)) {
        self.viewWasMoved = true
        self.view.frame.origin.y -= keyboardSize!.height
    } else {
        self.viewWasMoved = false
    }
}

func keyboardWillHide(notification: NSNotification) {
    if (self.viewWasMoved) {
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
            self.view.frame.origin.y += keyboardSize.height
        }
    }
}
Vah.Sah
źródło
Nie zapomnij ustawić Delegata na textFields
SwingerDinger 14.04.16
Zmień warunek w klawiaturze pokaże jako, jeśli (! CGRectContainsPoint (aRect, newOrgin!) &&! Self.viewWasMoved)
KingofBliss
dodaj self.viewWasMoved = false podczas resetowania ramki
KingofBliss
1

Zaktualizowano dla Swift 3 ...

Jak powiedzieli inni, musisz dodać obserwatorów powiadomień do metody viewDidLoad () kontrolera, tak jak to:

NotificationCenter.default.addObserver(forName: .UIKeyboardWillShow, object: nil, queue: nil)
    { notification in
    self.keyboardWillShow(notification)
    }

NotificationCenter.default.addObserver(forName: .UIKeyboardWillHide, object: nil, queue: nil)
    { notification in
    self.keyboardWillHide(notification)
    }

NotificationCenter.default.addObserver(forName: .UIKeyboardDidShow, object: nil, queue: nil)
    { _ in
    self.enableUserInteraction()
    }

NotificationCenter.default.addObserver(forName: .UIKeyboardDidHide, object: nil, queue: nil)
    { _ in
    self.enableUserInteraction()
    }

Pamiętaj, aby w razie potrzeby usunąć obserwatorów (robię to w metodzie viewWillDisappear ())

NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardDidShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardDidHide, object: nil)

Następnie zaimplementuj metody pokazywania i ukrywania - zwróć uwagę na wiersz, który mówi aplikacji, aby ignorowała zdarzenia interakcji (beginIgnoringInteractionEvents). Jest to ważne, ponieważ bez niego użytkownik może dotknąć pola lub nawet widoku przewijania i spowodować, że zmiana nastąpi po raz drugi, co spowoduje straszną usterkę interfejsu użytkownika. Ignorowanie zdarzeń interakcji przed pokazywaniem i ukrywaniem klawiatury zapobiegnie temu:

func keyboardWillShow(notification: Notification)
    {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
        {
        UIApplication.shared.beginIgnoringInteractionEvents()
        self.view.frame.origin.y -= keyboardSize.height
        // add this line if you are shifting a scrollView, as in a chat application
        self.timelineCollectionView.contentInset.top += keyboardSize.height
        }
    }

func keyboardWillHide(notification: Notification)
    {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
        {
        UIApplication.shared.beginIgnoringInteractionEvents()
        self.view.frame.origin.y += keyboardSize.height
        // add this line if you are shifting a scrollView, as in a chat application
        self.timelineCollectionView.contentInset.top -= keyboardSize.height
        }
    }

Na koniec włącz ponownie interakcje użytkownika (pamiętaj, że ta metoda jest uruchamiana po tym, jak klawiatura didShow lub didHide):

func enableUserInteraction()
    {
    UIApplication.shared.endIgnoringInteractionEvents()
    }
Gene Loparco
źródło
1

Kod Swift 3

var activeField: UITextField?

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(self, selector: #selector(ProfileViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(ProfileViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

func textFieldDidBeginEditing(_ textField: UITextField){
    activeField = textField
}

func textFieldDidEndEditing(_ textField: UITextField){
    activeField = nil
}

func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        if (self.activeField?.frame.origin.y)! >= keyboardSize.height {
            self.view.frame.origin.y = keyboardSize.height - (self.activeField?.frame.origin.y)!
        } else {
            self.view.frame.origin.y = 0
        }
    }
}

func keyboardWillHide(notification: NSNotification) {
    self.view.frame.origin.y = 0
}
Rohit Sharma
źródło
1

Jeśli masz 2 lub więcej pól tekstowych na tym samym VC, a użytkownik stuka w jedno z nich, a następnie w drugie, bez wywoływania funkcji keyboardWillHide, widok idzie jeszcze raz w górę, co nie jest konieczne, ponieważ będziesz mieć klawiaturę, puste miejsce, które ma wysokość klawiatury, a następnie widok, używając kodu w odpowiedzi, którą edytowałem:

override func viewDidLoad() {       
    super.viewDidLoad()
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)
}

func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        self.view.frame.origin.y -= keyboardSize.height
    }
}

func keyboardWillHide(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        self.view.frame.origin.y += keyboardSize.height
    }
}

Aby rozwiązać ten problem, zamień dwie funkcje „KeyboardWillShow / Hide” na następujące:

func keyboardWillShow(notification: NSNotification) {
 if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
    if view.frame.origin.y == 0{
        self.view.frame.origin.y -= keyboardSize.height
        }
    }
}

func keyboardWillHide(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        if view.frame.origin.y != 0 {
            self.view.frame.origin.y += keyboardSize.height
        }
    }
}
Pan Xcoder
źródło
co jeśli mój textField jest w górnej części widoku? mam na myśli, jeśli jest pole tekstowe o początku Y = 0 .. ?? potem textField idzie w górę i nie mogę tego zobaczyć
Saifan Nadaf
1

@ Rozwiązanie Borisa jest BARDZO dobre, ale widok może czasem być uszkodzony.

Aby uzyskać idealne wyrównanie, użyj poniższego kodu

override func viewDidLoad() {
super.viewDidLoad()            
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)}

Funkcje:

@objc func keyboardWillShow(notification: NSNotification) {        
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
    if self.view.frame.origin.y == 0{
        self.view.frame.origin.y -= keyboardSize.height
    }
}}    

I,

@objc func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
    if self.view.frame.origin.y != 0{
        self.view.frame.origin.y = 0 
    }
} }
Eray Alparslan
źródło
0

ten samouczek wideo jest najlepszy. 7 minut i będzie to miało sens. Takie proste rozwiązanie, gdy masz wiele pól tekstowych i chcesz, aby widok przewijania przesuwał liczbę „x” pikseli po dotknięciu tego konkretnego pola tekstowego.

https://youtu.be/VuiPGJOEBH4

Tylko te kroki:

-Umieść wszystkie pola tekstowe w widoku przewijania ograniczonym do krawędzi widoku.

-Połącz wszystkie pola tekstowe i przewiń widok jako delegaci do kontrolera widoku.

-Połącz wszystkie pola tekstowe i przewiń widok za pomocą IBOutlet.

class ViewController: UIViewController, UITextFieldDelegate {

-Dodaj protokół UITextFieldDelegate do swojej klasy

@IBOutlet var stateAddress: UITextField!
@IBOutlet var zipAddress: UITextField!
@IBOutlet var phoneNumber: UITextField!
@IBOutlet var vetEmailAddress: UITextField!    
@IBOutlet weak var scrollView: UIScrollView!

-Dodaj metody UITextFieldDelegate do pliku szybkiego:

func textFieldShouldReturn(textField: UITextField) -> Bool {

    textField.resignFirstResponder()
    return true
}


func textFieldDidBeginEditing(textField: UITextField) {

    if (textField == self.stateAddress) {
        scrollView.setContentOffset(CGPointMake(0, 25), animated: true)
    }
    else if (textField == self.zipAddress) {
        scrollView.setContentOffset(CGPointMake(0, 57), animated: true)
    }
    else if (textField == self.phoneNumber) {
        scrollView.setContentOffset(CGPointMake(0, 112), animated: true)
    }
    else if (textField == self.vetEmailAddress) {
        scrollView.setContentOffset(CGPointMake(0, 142), animated: true)
    }
}

func textFieldDidEndEditing(textField: UITextField) {

    scrollView.setContentOffset(CGPointMake(0, 0), animated: true)
}

Pierwsza metoda po prostu aktywuje przycisk powrotu na klawiaturze, aby zwolnić klawiaturę. Drugim jest, gdy klikniesz w dowolne określone pole tekstowe, a następnie ustawisz przesunięcie y przesunięcia o ile przewijany jest twój scrollview (mój jest oparty na położeniu y na moich kontrolerach widoku 25,57,112,142). Ostatni mówi, że po stuknięciu z klawiatury widok przewijania wraca do pierwotnej lokalizacji.

W ten sposób mój piksel widoku jest idealny!

bme003
źródło
0

Ta funkcja Shuda została wbudowana w Ios, jednak musimy to zrobić na zewnątrz.
Wstaw poniższy kod
* Aby przenieść widok, gdy textField znajduje się pod klawiaturą,
* Nie przenosić widoku, gdy textField znajduje się nad klawiaturą
* Aby w razie potrzeby przenieść widok w oparciu o wysokość klawiatury.
To działa i przetestowano we wszystkich przypadkach.

import UIKit

class NamVcc: UIViewController, UITextFieldDelegate
{
    @IBOutlet weak var NamTxtBoxVid: UITextField!

    var VydTxtBoxVar: UITextField!
    var ChkKeyPadDspVar: Bool = false
    var KeyPadHytVal: CGFloat!

    override func viewDidLoad()
    {
        super.viewDidLoad()

        NamTxtBoxVid.delegate = self
    }

    override func viewWillAppear(animated: Bool)
    {
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: #selector(TdoWenKeyPadVyd(_:)),
            name:UIKeyboardWillShowNotification,
            object: nil);
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: #selector(TdoWenKeyPadHyd(_:)),
            name:UIKeyboardWillHideNotification,
            object: nil);
    }

    func textFieldDidBeginEditing(TxtBoxPsgVar: UITextField)
    {
        self.VydTxtBoxVar = TxtBoxPsgVar
    }

    func textFieldDidEndEditing(TxtBoxPsgVar: UITextField)
    {
        self.VydTxtBoxVar = nil
    }

    func textFieldShouldReturn(TxtBoxPsgVar: UITextField) -> Bool
    {
        self.VydTxtBoxVar.resignFirstResponder()
        return true
    }

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
    {
        view.endEditing(true)
        super.touchesBegan(touches, withEvent: event)
    }

    func TdoWenKeyPadVyd(NfnPsgVar: NSNotification)
    {
        if(!self.ChkKeyPadDspVar)
        {
            self.KeyPadHytVal = (NfnPsgVar.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue().height

            var NonKeyPadAraVar: CGRect = self.view.frame
            NonKeyPadAraVar.size.height -= self.KeyPadHytVal

            let VydTxtBoxCenVal: CGPoint? = VydTxtBoxVar?.frame.origin

            if (!CGRectContainsPoint(NonKeyPadAraVar, VydTxtBoxCenVal!))
            {
                self.ChkKeyPadDspVar = true
                UIView.animateWithDuration(1.0,
                    animations:
                    { self.view.frame.origin.y -= (self.KeyPadHytVal)},
                    completion: nil)
            }
            else
            {
                self.ChkKeyPadDspVar = false
            }
        }

    }

    func TdoWenKeyPadHyd(NfnPsgVar: NSNotification)
    {
        if (self.ChkKeyPadDspVar)
        {
            self.ChkKeyPadDspVar = false
            UIView.animateWithDuration(1.0,
                animations:
                { self.view.frame.origin.y += (self.KeyPadHytVal)},
                completion: nil)
        }
    }

    override func viewDidDisappear(animated: Bool)
    {
        super.viewWillDisappear(animated)
        NSNotificationCenter.defaultCenter().removeObserver(self)
        view.endEditing(true)
        ChkKeyPadDspVar = false
    }
}

| :: | Czasami widok będzie w dół, w takim przypadku użyj wysokości +/- 150:

    NonKeyPadAraVar.size.height -= self.KeyPadHytVal + 150

    { self.view.frame.origin.y -= self.KeyPadHytVal  - 150},
                    completion: nil)

    { self.view.frame.origin.y += self.KeyPadHytVal  - 150},
                completion: nil)
Sujay ONZ
źródło
0
func keyboardWillShow(notification: NSNotification) {

    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        self.view.frame.origin.y = self.view.frame.height - (self.view.frame.height + keyboardSize.height)
    }

}

func keyboardWillHide(notification: NSNotification) {
        self.view.frame.origin.y = 0
}

musi być bardziej stabilny

Hadevs Play
źródło
0
 override func viewWillAppear(animated: Bool)
 {
 super.viewWillAppear(animated)

 NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
 NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)

 }

 // MARK: - keyboard
 func keyboardWillShow(notification: NSNotification) 
{

if let userInfo = notification.userInfo {
if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
let contentInsets = self.tblView.contentInset as UIEdgeInsets
self.tblView.contentInset = UIEdgeInsets(top: contentInsets.top, left: contentInsets.left, bottom: keyboardSize.height, right:contentInsets.right)
                    // ...
                } else {
                    // no UIKeyboardFrameBeginUserInfoKey entry in userInfo
                }
            } else {
                // no userInfo dictionary in notification
            }
        }

func keyboardWillHide(notification: NSNotification) 
{
let contentInsets = self.tblView.contentInset as UIEdgeInsets
self.tblView.contentInset = UIEdgeInsets(top: contentInsets.top, left: contentInsets.left, bottom: 0, right:contentInsets.right)
 }
Himali Shah
źródło
0

Użyj następującego kodu, aby wyświetlić Up on UITextField Clicked

func textFieldDidBeginEditing(textField: UITextField) {
    ViewUpanimateMoving(true, upValue: 100)
}
func textFieldDidEndEditing(textField: UITextField) {
    ViewUpanimateMoving(false, upValue: 100)
}
func ViewUpanimateMoving (up:Bool, upValue :CGFloat){
    var durationMovement:NSTimeInterval = 0.3
    var movement:CGFloat = ( up ? -upValue : upValue)
    UIView.beginAnimations( "animateView", context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)
    UIView.setAnimationDuration(durationMovement)
    self.view.frame = CGRectOffset(self.view.frame, 0,  movement)
    UIView.commitAnimations()
}
Jugal K Balara
źródło
0

Zrobiłem cocoapod, aby uprościć sprawę:

https://github.com/xtrinch/KeyboardLayoutHelper

Jak tego użyć:

Stwórz dolne ograniczenie automatycznego układu, nadaj mu klasę KeyboardLayoutConstraint w module KeyboardLayoutHelper, a kapsuła wykona pracę niezbędną do jej zwiększenia, aby dostosować się do pojawiania się i znikania klawiatury. Zobacz przykładowy projekt z przykładami, jak go używać (zrobiłem dwa: pola tekstowe w scrollView i pola tekstowe pionowo wyśrodkowane z dwoma podstawowymi widokami - logowanie i rejestracja).

Dolne ograniczenie układu może dotyczyć widoku kontenera, samego pola tekstowego, cokolwiek, jak go nazwiesz.

xtrinch
źródło