Düzenlemeye başlarken klavye mevcut olduğunda bir UITextField öğesinin yukarı hareket etmesini nasıl sağlayabilirim?


1691

İOS SDK ile:

Bir var UIViewolan UITextFieldbir klavyeyi olduğunu s. Bunu yapabilmek için ihtiyacım var:

  1. UIScrollViewKlavye açıldığında diğer metin alanlarını görmek için öğesinin içeriğinin kaydırılmasına izin ver

  2. Otomatik olarak "zıpla" (yukarı kaydırarak) veya kısaltır

Bir ihtiyacım olduğunu biliyorum UIScrollView. Ben Sesimin sınıfını değiştirme denedim UIViewa UIScrollViewama hala yukarı veya aşağı textboxes kaydırmak veremiyoruz.

Ben a hem gerekiyor mu UIViewve UIScrollView? Biri diğerinin içine giriyor mu?

Etkin metin alanına otomatik olarak ilerlemek için neler yapılması gerekir?

İdeal olarak, bileşenlerin kurulumunun mümkün olduğunca fazlası, Interface Builder'da yapılacaktır. Sadece neye ihtiyaç duyduğuna dair kod yazmak istiyorum.

Not: UIView(veya UIScrollView) Ben TabBar (gündeme getirdiği ile çalışıyorum o UITabBarnormal işlevine gerekiyor).


Düzenleme: Sadece klavye geldiğinde kaydırma çubuğunu ekliyorum. Gerekli olmasa da, daha iyi bir arayüz sağladığını hissediyorum, çünkü kullanıcı örneğin metin kutularını kaydırabilir ve değiştirebilir.

UIScrollViewKlavye yukarı ve aşağı gittiğinde çerçeve boyutunu değiştirdiğim yerde çalıştım . Ben sadece kullanıyorum:

-(void)textFieldDidBeginEditing:(UITextField *)textField { 
    //Keyboard becomes visible
    scrollView.frame = CGRectMake(scrollView.frame.origin.x, 
                     scrollView.frame.origin.y, 
scrollView.frame.size.width,
scrollView.frame.size.height - 215 + 50);   //resize
}

-(void)textFieldDidEndEditing:(UITextField *)textField {
   //keyboard will hide
    scrollView.frame = CGRectMake(scrollView.frame.origin.x, 
       scrollView.frame.origin.y, 
     scrollView.frame.size.width,
      scrollView.frame.size.height + 215 - 50); //resize
}

Ancak, bu otomatik olarak "yukarı taşımak" veya görünür alan alt metin alanları ortalamıyor, bu gerçekten istiyorum.


6
Şuna bir bak. Senin için güçlük yok. Savunma
Aruna

21
Apple tarafından belgelenmiştir, bunun en iyi yol olduğunu düşünüyorum: developer.apple.com/library/ios/#documentation/StringsTextFonts/…
Maik639 31:13

58
Bu kodu kullanın. Appdelegate.m dosyasında sadece 1 satıra ihtiyacınız var ve çalışıyor. github.com/hackiftekhar/IQKeyboardManager
Pradeep Mittal

9
Şimdiye kadar bulduğum en iyi yol bu açık kaynak TPKeyboardAvoiding
Mongi Zaidi

2
Başka bir yol, bu tür içerik metin alanlarını ve tümünü TableViewController'a eklemek ve tableview'in bunu işlemesine izin vermektir.
Vicky Dhas

Yanıtlar:


1036
  1. Yalnızca ScrollViewşu anda sahip olduğunuz içerikler iPhone ekranına sığmıyorsa ihtiyacınız olacaktır . ( ScrollViewSadece TextFieldklavye geldiğinde yukarı kaydırma yapmak için bileşenlerin denetimi olarak ekliyorsanız, gerekli değildir.)

  2. TextFieldKlavye ile kapsanmayı önlemenin standart yolu , klavye her görüntülendiğinde görünümü yukarı / aşağı hareket ettirmektir.

İşte bazı örnek kod:

#define kOFFSET_FOR_KEYBOARD 80.0

-(void)keyboardWillShow {
    // Animate the current view out of the way
    if (self.view.frame.origin.y >= 0)
    {
        [self setViewMovedUp:YES];
    }
    else if (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}

-(void)keyboardWillHide {
    if (self.view.frame.origin.y >= 0)
    {
        [self setViewMovedUp:YES];
    }
    else if (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}

-(void)textFieldDidBeginEditing:(UITextField *)sender
{
    if ([sender isEqual:mailTf])
    {
        //move the main view, so that the keyboard does not hide it.
        if  (self.view.frame.origin.y >= 0)
        {
            [self setViewMovedUp:YES];
        }
    }
}

//method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.3]; // if you want to slide up the view

    CGRect rect = self.view.frame;
    if (movedUp)
    {
        // 1. move the view's origin up so that the text field that will be hidden come above the keyboard 
        // 2. increase the size of the view so that the area behind the keyboard is covered up.
        rect.origin.y -= kOFFSET_FOR_KEYBOARD;
        rect.size.height += kOFFSET_FOR_KEYBOARD;
    }
    else
    {
        // revert back to the normal state.
        rect.origin.y += kOFFSET_FOR_KEYBOARD;
        rect.size.height -= kOFFSET_FOR_KEYBOARD;
    }
    self.view.frame = rect;

    [UIView commitAnimations];
}


- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillShow)
                                             name:UIKeyboardWillShowNotification
                                           object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(keyboardWillHide)
                                             name:UIKeyboardWillHideNotification
                                           object:nil];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    // unregister for keyboard notifications while not visible.
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                             name:UIKeyboardWillShowNotification
                                           object:nil];

    [[NSNotificationCenter defaultCenter] removeObserver:self
                                             name:UIKeyboardWillHideNotification
                                           object:nil];
}

3
_TextField ne işe yarar? Koduma kopyaladım, _textField bildirimi yok diyor.
Cocoa Dev

"Kullanıcı burada düzenleme yaparken görünüm yukarı kaymalı" ya da başka bir şey demek için kullandığınız alandır ... Ancak daha fazla alanınız varsa bunu kaldırabilirsiniz.
patrick

keyBoardWillSHow ve KeyBoardWillHide olaylarında - (void) setViewMovedUp: (BOOL)
Abduliam Rehmanius

4
Ana görünümün dönüşlerini destekliyorsanız özellikle yararlı değildir.
FractalDoctor

2
Bu işi yapmak için textFieldDidBeginEditingbölümü yorumlamak zorunda kaldım .
avance

445

Ayrıca , bir veya daha fazlası düzenlenirken klavye tarafından gizlenecek olan UIScrollViewçoklu oluşturma ile ilgili çok sorun yaşıyordum UITextFields.

Burada, UIScrollViewdoğru şekilde kaydırma yapmıyorsanız dikkate almanız gereken bazı noktalar vardır .

1) contentSize öğenizin UIScrollViewçerçeve boyutundan büyük olduğundan emin olun . Anlamak için bir yol UIScrollViewsolduğunu UIScrollViewcontentSize tanımlanan içeriğine bir görüntüleme penceresine gibidir. Bu nedenle, UIScrollviewherhangi bir yere kaydırma yapabilmek için contentSize öğesinin UIScrollView. Aksi takdirde, contentSize öğesinde tanımlanan her şey zaten görünür olduğundan kaydırma gerekmez. BTW, varsayılan contentSize = CGSizeZero.

2) Şimdi anladığınızı UIScrollViewsenin "içerik" içine gerçekten bir penceredir, yolu klavye sizin engellemeyecek emin olmak için UIScrollView's"pencere" boyutlandırmak olacağını görüntüleme UIScrollViewböylece klavye mevcut olduğunda, sahip olduğu UIScrollViewpencereyi sadece orijinal UIScrollViewçerçeveye göre boyutlandırılır. boyut eksi klavyenin yüksekliği. Bu, pencerenizin yalnızca görüntülenebilir küçük alan olmasını sağlar.

3) İşte yakalama: Bunu ilk uyguladığımda CGRect, düzenlenmiş metin alanını almak ve UIScrollView'sscrollRecToVisible yöntemini çağırmak zorunda kalacağımı düşündüm . UITextFieldDelegateYönteme textFieldDidBeginEditingçağrı ile yöntemi uyguladım scrollRecToVisible. Bu aslında kaydırma edeceğini garip bir yan etkisi ile çalışmış ek bileşeniUITextField pozisyona. En uzun süre ne olduğunu anlayamadım. Sonra textFieldDidBeginEditingDelege yöntemini yorum ve hepsi işe yarıyor !! (???). Sonuç olarak UIScrollView, şu anda düzenlenmiş UITextFieldolanın örtülü olarak görüntülenebilir pencereye örtük olarak getirdiğine inanıyorum . UITextFieldDelegateYöntemin uygulanması ve ardından gelen çağrı scrollRecToVisiblegereksizdi ve garip yan etkinin nedeniydi.

Klavye göründüğünde doğru UITextFieldbir UIScrollViewşekilde yerine kaydırmak için adımlar .

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.

- (void)viewDidLoad 
{
    [super viewDidLoad];

    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(keyboardWillShow:) 
                                                 name:UIKeyboardWillShowNotification 
                                               object:self.view.window];
    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(keyboardWillHide:) 
                                                 name:UIKeyboardWillHideNotification 
                                               object:self.view.window];
    keyboardIsShown = NO;
    //make contentSize bigger than your scrollSize (you will need to figure out for your own use case)
    CGSize scrollContentSize = CGSizeMake(320, 345);
    self.scrollView.contentSize = scrollContentSize;
}

- (void)keyboardWillHide:(NSNotification *)n
{
    NSDictionary* userInfo = [n userInfo];

    // get the size of the keyboard
    CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;


    // resize the scrollview
    CGRect viewFrame = self.scrollView.frame;
    // I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
    viewFrame.size.height += (keyboardSize.height - kTabBarHeight);

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [self.scrollView setFrame:viewFrame];
    [UIView commitAnimations];

    keyboardIsShown = NO;
}

- (void)keyboardWillShow:(NSNotification *)n
{
    // This is an ivar I'm using to ensure that we do not do the frame size adjustment on the `UIScrollView` if the keyboard is already shown.  This can happen if the user, after fixing editing a `UITextField`, scrolls the resized `UIScrollView` to another `UITextField` and attempts to edit the next `UITextField`.  If we were to resize the `UIScrollView` again, it would be disastrous.  NOTE: The keyboard notification will fire even when the keyboard is already shown.
    if (keyboardIsShown) {
        return;
    }

    NSDictionary* userInfo = [n userInfo];

    // get the size of the keyboard
    CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;

    // resize the noteView
    CGRect viewFrame = self.scrollView.frame;
    // I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
    viewFrame.size.height -= (keyboardSize.height - kTabBarHeight);

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [self.scrollView setFrame:viewFrame];
    [UIView commitAnimations];
    keyboardIsShown = YES;
}
  1. Klavye bildirimlerine şu adresten kaydolun: viewDidLoad
  2. Adresindeki klavye bildirimlerinin kaydını kaldırın viewDidUnload
  3. Emin olun contentSizeayarlanır ve daha büyük UIScrollViewATviewDidLoad
  4. KüçültUIScrollView klavye mevcut olduğunda
  5. Geri döndürUIScrollView klavye uzağa gittiğinde.
  6. Klavye bildirimler her seferinde gönderilir beri klavye zaten ekranda gösteriliyorsa algılamak için bir ivar kullanın UITextFieldklavye önlemek için zaten mevcut olsa bile sekmeli edilir küçülenUIScrollView zaten olduğunda büzüşen

Dikkat edilmesi gereken bir şey UIKeyboardWillShowNotification, başka bir sekmeye tıkladığınızda klavye zaten ekrandayken bile ateşleneceğidir UITextField. UIScrollViewKlavyenin zaten ekranda olduğu zamanı yeniden boyutlandırmak için bir ivar kullanarak bunu hallettim . UIScrollViewKlavyenin zaten olduğu zamanları yanlışlıkla yeniden boyutlandırmak felaket olurdu!

Umarım bu kod bazılarınızı baş ağrısından kurtarır.


3
Harika, ama iki sorun: 1. UIKeyboardBoundsUserInfoKeykullanımdan kaldırıldı. 2. keyboardSize "ekran koordinatlarında" olduğundan çerçeve döndürülürse veya ölçeklenirse viewFrame hesaplarınız başarısız olur.
Martin Wickman

21
@Martin Wickman - Kullanımdan CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;kaldırıldı yerine kullanUIKeyboardBoundsUserInfoKey
sottenad

1
Merhaba, aynısını yaptım, ancak metin görünümü yalnızca kullanıcı yazmaya başladığında yukarı hareket eder? Beklenen davranış mı yoksa bir şey mi kaçırıyorum?

3
[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].sizeolmalı [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size. Harika bir çözüm olsa!
j7nn7k

1
Çözümünüzü beğendim ama daha da basitleştirebileceğimi düşünüyorum: Bildirim Gözlemcisi ile uğraşmayın; bunun yerine uygun delege yöntemleri içinde doğru animasyon yordamlarını çağırın - UITextView için bunlar textViewDidBeginEditing ve textViewDidEndEditing.
AlexChaffee

270

Aslında en iyisi, dokümanlarda belirtildiği gibi Apple'ın uygulamasını kullanmaktır . Ancak, sağladıkları kod hatalı. keyboardWasShown:Yorumların hemen altında bulunan kısmı aşağıdakilerle değiştirin:

NSDictionary* info = [aNotification userInfo];
CGRect keyPadFrame=[[UIApplication sharedApplication].keyWindow convertRect:[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue] fromView:self.view];
CGSize kbSize =keyPadFrame.size;
CGRect activeRect=[self.view convertRect:activeField.frame fromView:activeField.superview];
CGRect aRect = self.view.bounds;
aRect.size.height -= (kbSize.height);

CGPoint origin =  activeRect.origin;
origin.y -= backScrollView.contentOffset.y;
if (!CGRectContainsPoint(aRect, origin)) {
    CGPoint scrollPoint = CGPointMake(0.0,CGRectGetMaxY(activeRect)-(aRect.size.height));
    [backScrollView setContentOffset:scrollPoint animated:YES];
}

Apple'ın koduyla ilgili sorunlar şunlardır: (1) Noktanın görünümün çerçevesi içinde olup olmadığını her zaman hesaplarlar, ancak a ScrollView, bu yüzden zaten kaydırılmış olabilir ve bu ofseti hesaba katmanız gerekir:

origin.y -= scrollView.contentOffset.y

(2) ContentOffset'i klavyenin yüksekliğine göre kaydırıyorlar, ancak tam tersini istiyoruz ( contentOffsetekranda görünmeyen yüksekliğe göre değil, kaydırmak istiyoruz ):

activeField.frame.origin.y-(aRect.size.height)

1
Kaydırma görünümünün ekranı doldurmadığı durumlarda, aRect kaydırma görünümünün çerçevesine ayarlanmalıdır
mblackwell8

2
CGPoint origin = activeField.frame.origin + activeField.frame.size.height? 'I istemez misiniz? durum.
htafoya

1
Bu çözüm Yatay yönde çalışmaz - metin alanı görünüm bağlantı noktasının üstünden uçar. iOS 7.1 yüklü iPad.
Andrew

4
Daha iyi iOS 8 desteği için, klavye boyutunu elde etmek UIKeyboardFrameEndUserInfoKeyyerine kullanmanızı öneririm UIKeyboardFrameBeginUserInfoKey, çünkü bu özel klavye değişiklikleri ve akıllı metin açma / kapatma gibi şeyleri alır.
Endareth

1
@Egor: Düzeltmeniz daha iyi çalışmasını sağlıyor - ancak son satır ters olmalı:self.scrollView.contentOffset = self.currentSVoffset;
Morten Holmgaard

244

Gelen textFieldDidBeginEdittingve textFieldDidEndEditingara özelliğinin [self animateTextField:textField up:YES]şöyle:

-(void)textFieldDidBeginEditing:(UITextField *)textField 
{ 
    [self animateTextField:textField up:YES]; 
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField:textField up:NO];
}

-(void)animateTextField:(UITextField*)textField up:(BOOL)up
{
    const int movementDistance = -130; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? movementDistance : -movementDistance); 

    [UIView beginAnimations: @"animateTextField" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}

Umarım bu kod size yardımcı olur.

Swift 2'de

func animateTextField(textField: UITextField, up: Bool) 
{
     let movementDistance:CGFloat = -130
     let movementDuration: Double = 0.3

     var movement:CGFloat = 0
     if up 
     {
         movement = movementDistance
     }
     else 
     {
         movement = -movementDistance
     }
     UIView.beginAnimations("animateTextField", context: nil)
     UIView.setAnimationBeginsFromCurrentState(true)
     UIView.setAnimationDuration(movementDuration)
     self.view.frame = CGRectOffset(self.view.frame, 0, movement)
     UIView.commitAnimations()
}


func textFieldDidBeginEditing(textField: UITextField) 
{
    self.animateTextField(textField, up:true)
}

func textFieldDidEndEditing(textField: UITextField) 
{
    self.animateTextField(textField, up:false)
}

SWIFT 3

 func animateTextField(textField: UITextField, up: Bool)
    {
        let movementDistance:CGFloat = -130
        let movementDuration: Double = 0.3

        var movement:CGFloat = 0
        if up
        {
            movement = movementDistance
        }
        else
        {
            movement = -movementDistance
        }
        UIView.beginAnimations("animateTextField", context: nil)
        UIView.setAnimationBeginsFromCurrentState(true)
        UIView.setAnimationDuration(movementDuration)
        self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
        UIView.commitAnimations()
    }


    func textFieldDidBeginEditing(textField: UITextField)
    {
        self.animateTextField(textField: textField, up:true)
    }

    func textFieldDidEndEditing(textField: UITextField)
    {
        self.animateTextField(textField: textField, up:false)
    }

1
neden kullanmıyorsun [UIView animateWithDuration: animations:^{ }];?
Andre Cytryn

2
const int moveDistance = -130; // gerektiğinde tweak daha esnek olarak değiştirilmesi gerekiyor
Hammer

7
Küçük uygulamalarda inanılmaz derecede basit. ScrollViews ve Belirsiz otomatik düzen sorunlarıyla uğraşmak yok.
James Perih

4
Eee ... textField parametresini hiç kullanmıyorsunuz. O zaman neden bir fonksiyon parametresi olarak var? Ayrıca, üçlü operatör de Swift'te kullanılabilir. Kodu daha az konuşur hale getirir.
stk

1
Görünümün arka plan rengi siyah dışında bir şeyse, Pencerenin rengini, kullanıcının arkasında görmemesi için görünümünüzle eşleşecek şekilde ayarladığınızdan emin olun. yani self.window.backgroundColor = [UIColor beyazColor];
bvmobileapps

134

Yalnızca TextField öğelerini kullanarak:

1a) Kullanarak Interface Builder: Tüm TextFields => Edit => Embed In => ScrollView

1b) TextField öğelerini scrollView adı verilen UIScrollView'e manuel olarak gömme

2) Ayarlayın UITextFieldDelegate

3) Her birini ayarlayın textField.delegate = self;(veya bağlantı yapın Interface Builder)

4) Kopyala / Yapıştır:

- (void)textFieldDidBeginEditing:(UITextField *)textField {
    CGPoint scrollPoint = CGPointMake(0, textField.frame.origin.y);
    [scrollView setContentOffset:scrollPoint animated:YES];
}

- (void)textFieldDidEndEditing:(UITextField *)textField {
    [scrollView setContentOffset:CGPointZero animated:YES];
}

8
Ancak textField, zaten görünür olduğunda görünümü de yukarı taşır .
TheTiger

1
Değiştirmek CGPointMake(0, textField.frame.origin.y);gerekiyorCGPointMake(0, textField.frame.origin.y + scrollView.contentInset.top);
Fury

@Egor Yorumunuzdan sonra bile çalışmıyor. Bahsedilen "TheTiger" gibi metin alanı göründükten sonra bile görünümü yukarı taşır.
rak appdev

XCode 10 için değişiklik: "Tümünü Seç TextFields => Editör => Yerleştir => Kaydırma Görünümü"
tibalt

116

İçin Evrensel Çözüm , İşte uygulanması için benim yaklaşım oldu IQKeyboardManager .

resim açıklamasını buraya girin

Adım1: - I Eklendi küresel bildirimler UITextField, UITextViewve UIKeyboardbir Singleton sınıfında. Buna IQKeyboardManager diyorum .

Adım 2: - Eğer buldum UIKeyboardWillShowNotification, UITextFieldTextDidBeginEditingNotificationya UITextViewTextDidBeginEditingNotificationbildirimler, ben elde etmeye çalışmak topMostViewControllerdan örneğini UIWindow.rootViewControllerhiyerarşisi. Düzgün bir şekilde ortaya çıkabilmesi UITextField/ UITextViewüzerine çıkabilmesi için topMostViewController.viewçerçevesinin ayarlanması gerekir.

Adım 3: - Ben beklenen hamle mesafe hesaplanan topMostViewController.viewilk yanıt açısından UITextField/ UITextView.

Adım 4: -topMostViewController.view.frame Beklenen hareket mesafesine göre yukarı / aşağı hareket ettim .

Adım 5: - Bulunduğunda veya bildirimde bulunursa UIKeyboardWillHideNotification, yine hiyerarşiden örnek almaya çalışırım .UITextFieldTextDidEndEditingNotificationUITextViewTextDidEndEditingNotificationtopMostViewControllerUIWindow.rootViewController

Adım 6: - Orijinal mesafesine topMostViewController.viewgeri getirilmesi gereken bozuk mesafeyi hesapladım .

Adım 7: -topMostViewController.view.frame Rahatsız mesafeye göre geri yükledim .

Adım 8 : - Uygulama yükünde singleton IQKeyboardManager sınıfı örneğini başlattım, böylece uygulamadaki her UITextField/ UITextViewbeklenen hareket mesafesine göre otomatik olarak ayarlanacaktır.

Hepsi bu oluyor IQKeyboardManager ile sizin için ne KOD NO HATTI gerçekten !! sadece ilgili kaynak dosyayı projeye sürükleyip bırakmanız yeterlidir. IQKeyboardManager ayrıca Cihaz Yönlendirme , Otomatik UIToolbar Yönetimi , KeybkeyboardDistanceFromTextField ve düşündüğünüzden çok daha fazlasını destekler.


IQKeyBoardManagerSwift dizinini projeme ekle ve çalışmıyor. Çünkü AppDelegate'de tanımıyor çünkü etkinleştirilemiyor ...
user3722523

2
Bu gerçek çözüm gösterilmez kimlik avı gibi geliyor ama bunun yerine GitHub hesabına bir reklam görüyoruz.
Brian

101

Ben birlikte evrensel, düşüşü-koyduk UIScrollView, UITableViewve hatta UICollectionViewklavyenin yol içindeki tüm metin alanlarını taşınıyor ilgilenir alt sınıfı.

Klavye görünmek üzereyken, alt sınıf düzenlenmek üzere olan alt görünümü bulur ve görünümün görünür olduğundan emin olmak için klavye açılır penceresine uyacak bir animasyonla çerçevesini ve içerik ofsetini ayarlar. Klavye kaybolduğunda, önceki boyutunu geri yükler.

Temelde herhangi bir kurulumla, ya UITableViewtabanlı bir arayüzle ya da manuel olarak yerleştirilmiş görünümlerden oluşan bir kurulumla çalışmalıdır .

İşte: Metin alanlarını klavyenin dışına taşımak için çözüm


Budur! Bu en iyi, en verimli ve mükemmel çözüm! Ayrıca kaydırma görünümleri için dönüşleri düzgün şekilde işler. Dönüyorsa, dikey olarak otomatik olarak boyutlandırdığınızdan emin olun, ancak alttan tutturmayın. Benim durumumda kaydırma görünümüne bir UITextView ekledim. Teşekkürler demet!
Christopher

Güzel iş! Tabii, DIY yerine çözümünüzü kullanarak tembelim, ama patronum daha mutlu, bu yüzden evet! Birisi kendini yapmak istese bile, her denetleyiciye kod eklemek yerine alt sınıf yaklaşımınızı seviyorum.
İOS'un

Kaydırma görünümüm gibi tuhaf şeyler ekrana sığar, böylece kaydırılamaz. Klavyeyi açıp kapattıktan sonra içerik artık daha büyük (görünmez bir şey eklenmiş ve sayfanın altına kaldırılmamış gibi görünüyor) ve kaydırılabilir.
Almo

90

For Swift Programcılar:

Bu sizin için her şeyi yapar, bunları görünüm denetleyici sınıfınıza koyun ve görünüm denetleyicinize uygulayın UITextFieldDelegateve textField delegesiniself

textField.delegate = self // Setting delegate of your UITextField to self

Temsilci geri arama yöntemlerini uygulayın:

func textFieldDidBeginEditing(textField: UITextField) {
    animateViewMoving(true, moveValue: 100)
}

func textFieldDidEndEditing(textField: UITextField) {
    animateViewMoving(false, moveValue: 100)
}

// Lifting the view up
func animateViewMoving (up:Bool, moveValue :CGFloat){
    let movementDuration:NSTimeInterval = 0.3
    let movement:CGFloat = ( up ? -moveValue : moveValue)
    UIView.beginAnimations( "animateView", context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)
    UIView.setAnimationDuration(movementDuration )
    self.view.frame = CGRectOffset(self.view.frame, 0,  movement)
    UIView.commitAnimations()
}

Swift 4, 4.2, 5 için: Değiştir

self.view.frame = CGRectOffset(self.view.frame, 0,  movement)

için

self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)

Bu uygulama hakkında son not: Klavye gösterilirken yığına başka bir görünüm denetleyicisi iterseniz, görünümün orta çerçevesine geri döndürüldüğü ancak klavye ofseti sıfırlanmadığında bir hata oluşur. Örneğin, klavyeniz nameField için ilk yanıtlayıcıdır, ancak daha sonra Yardım Görünümü Denetleyicinizi yığınıza iten bir düğmeye basarsınız. Ofset hatasını düzeltmek için, textFieldDidEndEditing delege yönteminin de çağrıldığından emin olarak, görünüm denetleyicisinden ayrılmadan önce nameField.resignFirstResponder () yöntemini çağırdığınızdan emin olun. Bunu viewWillDisappear yönteminde yaparım.


3
SwiftLint beğenmedi self.view.frame = CGRectOffset(self.view.frame, 0, movement)bu yüzden bu satırı değiştirdimself.view.frame.offsetInPlace(dx: 0, dy: movement)
levibostian

2
Swift 4 self.view.frame = CGRectOffset'i (self.view.frame, 0, hareket) self.view.frame.offsetBy (dx: 0, dy: hareket) olarak değiştir
14:18 'de Asinox

FYI, bunun çalışması için koymalısın. self.view.frame = self.view.frame.offsetBy (dx: 0, dy: hareket)
Josh Wolff

64

Zaten çok sayıda cevap var, ancak yukarıdaki çözümlerin hiçbirinde "mükemmel" bir böceksiz, geriye dönük uyumlu ve titreşimsiz animasyon için gerekli tüm fantezi konumlandırma öğeleri yoktu. (çerçeve / sınırlar ve içeriği canlandırırken hata: Birlikte set, farklı arayüz yönlendirmeleri, iPad bölünmüş klavye, ...)
Çözümümü paylaşmama izin verin:
(ayarladığınızı varsayarak UIKeyboardWill(Show|Hide)Notification)

// Called when UIKeyboardWillShowNotification is sent
- (void)keyboardWillShow:(NSNotification*)notification
{
    // if we have no view or are not visible in any window, we don't care
    if (!self.isViewLoaded || !self.view.window) {
        return;
    }

    NSDictionary *userInfo = [notification userInfo];

    CGRect keyboardFrameInWindow;
    [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardFrameInWindow];

    // the keyboard frame is specified in window-level coordinates. this calculates the frame as if it were a subview of our view, making it a sibling of the scroll view
    CGRect keyboardFrameInView = [self.view convertRect:keyboardFrameInWindow fromView:nil];

    CGRect scrollViewKeyboardIntersection = CGRectIntersection(_scrollView.frame, keyboardFrameInView);
    UIEdgeInsets newContentInsets = UIEdgeInsetsMake(0, 0, scrollViewKeyboardIntersection.size.height, 0);

    // this is an old animation method, but the only one that retains compaitiblity between parameters (duration, curve) and the values contained in the userInfo-Dictionary.
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];

    _scrollView.contentInset = newContentInsets;
    _scrollView.scrollIndicatorInsets = newContentInsets;

    /*
     * Depending on visual layout, _focusedControl should either be the input field (UITextField,..) or another element
     * that should be visible, e.g. a purchase button below an amount text field
     * it makes sense to set _focusedControl in delegates like -textFieldShouldBeginEditing: if you have multiple input fields
     */
    if (_focusedControl) {
        CGRect controlFrameInScrollView = [_scrollView convertRect:_focusedControl.bounds fromView:_focusedControl]; // if the control is a deep in the hierarchy below the scroll view, this will calculate the frame as if it were a direct subview
        controlFrameInScrollView = CGRectInset(controlFrameInScrollView, 0, -10); // replace 10 with any nice visual offset between control and keyboard or control and top of the scroll view.

        CGFloat controlVisualOffsetToTopOfScrollview = controlFrameInScrollView.origin.y - _scrollView.contentOffset.y;
        CGFloat controlVisualBottom = controlVisualOffsetToTopOfScrollview + controlFrameInScrollView.size.height;

        // this is the visible part of the scroll view that is not hidden by the keyboard
        CGFloat scrollViewVisibleHeight = _scrollView.frame.size.height - scrollViewKeyboardIntersection.size.height;

        if (controlVisualBottom > scrollViewVisibleHeight) { // check if the keyboard will hide the control in question
            // scroll up until the control is in place
            CGPoint newContentOffset = _scrollView.contentOffset;
            newContentOffset.y += (controlVisualBottom - scrollViewVisibleHeight);

            // make sure we don't set an impossible offset caused by the "nice visual offset"
            // if a control is at the bottom of the scroll view, it will end up just above the keyboard to eliminate scrolling inconsistencies
            newContentOffset.y = MIN(newContentOffset.y, _scrollView.contentSize.height - scrollViewVisibleHeight);

            [_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
        } else if (controlFrameInScrollView.origin.y < _scrollView.contentOffset.y) {
            // if the control is not fully visible, make it so (useful if the user taps on a partially visible input field
            CGPoint newContentOffset = _scrollView.contentOffset;
            newContentOffset.y = controlFrameInScrollView.origin.y;

            [_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
        }
    }

    [UIView commitAnimations];
}


// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillHide:(NSNotification*)notification
{
    // if we have no view or are not visible in any window, we don't care
    if (!self.isViewLoaded || !self.view.window) {
        return;
    }

    NSDictionary *userInfo = notification.userInfo;

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[[userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[[userInfo valueForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];

    // undo all that keyboardWillShow-magic
    // the scroll view will adjust its contentOffset apropriately
    _scrollView.contentInset = UIEdgeInsetsZero;
    _scrollView.scrollIndicatorInsets = UIEdgeInsetsZero;

    [UIView commitAnimations];
}

@Shiun cevabında büyük gelişmeler. Ancak klavye gittikten sonra, görünüm 1. konuma geri dönmez. Hala harika bir iş :)
Lucien

2
Teşekkürler, bu 2017'de benim için en iyi çözüm. OdaklanmışControl'ü kendiniz izlemenize gerek olmadığını unutmayın, bunu ile belirleyebilirsiniz UIApplication.shared.sendAction(...). sendActionUygulamanızla birlikte cevabınızın Swift 3 sürümü (eksi willHide bölümü) : gist.github.com/xaphod/7aab1302004f6e933593a11ad8f5a72d
xaphod

Benim durumumda @xaphod daha fazla denetime odaklanmam gerekiyordu - örneğin, bir giriş alanının altındaki bir düğme. ancak evet bu kod şimdi 4 yaşında ve geliştirmelerden faydalanabilir.
Martin Ullrich

Bu muhtemelen doğru çözümdür. Klavye bildirimi animasyon verileri taşır, metin alanı delegasyonları animasyon süresini bilmez, bu sadece bir tahmin çalışması olacaktır.
XY

62

Shiun, "Görünüşe göre, UIScrollView'ın aslında şu anda düzenlenmiş UITextField öğesini örtük olarak görüntülenebilir pencereye getirdiğine inanıyorum" Bu, iOS 3.1.3 için doğru görünüyor, ancak 3.2, 4.0 veya 4.1 için geçerli değil. UITextField'i iOS> = 3.2'de görünür hale getirmek için açık bir scrollRectToVisible eklemek zorunda kaldım.


Bu görünümüne dolaylı düzenlenmiş UITextField kayar UIScrollView değil, onun özel çağırır UITextField [UITextField scrollTextFieldToVisibleIfNecessary]dönüş çağrıları yöntemi denir. Bkz. Github.com/leopatras/ios_textfields_on_scrollview . Kısıtlamalar ve görünüm denetleyicileri düzgün bir şekilde ayarlandıysa, aslında (en azından IOS 11'den beri) açıkça çağrı yapmaya gerek yoktur . [UIScrollView scrollRectToVisible][UITextField becomeFirstResponder]scrollRectToVisible
Leo

48

Dikkate almanız gereken bir şey, a'yı UITextFieldtek başına kullanmak isteyip istemediğinizdir . Aslında UITextFieldsdışında kullanılan iyi tasarlanmış bir iPhone uygulamasına rastlamadım UITableViewCells.

Ekstra bir çalışma olacak, ancak tüm veri girişi görünümlerini tablo görünümlerini uygulamanızı öneririm. Bir ekleme UITextViewadresinden Müşteri UITableViewCells.


1
Uygulamalarımdan birinin kullanıcıların serbest biçimli notlar eklemesine izin vermesi gerekiyor, bu nedenle evet bazen bir UITextField kullanmak yararlı olabilir.
Peter Johnson

1
Bu yönteme katılıyorum. Sıfır çalışma veya kod bu şekilde. Serbest form notuna ihtiyacınız olsa bile, hala bir tablo hücresiyle yapabilirsiniz
RJH

UITableViewne yazık ki gitmek için tek yol. Klavye bildirimleri kırılgandır ve fazla mesaiyi değiştirmiştir. Yığın Taşması üzerinde örnek kod: stackoverflow.com/a/32390936/218152
SwiftArchitect

Bu cevap yaklaşık beş yıldır . Tek modern çözüm böyle bir şeydir ... stackoverflow.com/a/41808338/294884
Fattie

47

Bu belge, bu soruna bir çözüm sunmaktadır. 'Klavyenin Altında Bulunan İçeriği Taşıma' altındaki kaynak koduna bakın. Oldukça basit.

EDIT: Örnekte bir çiş aksaklık olduğunu fark ettim. Muhtemelen UIKeyboardWillHideNotificationyerine dinlemek isteyeceksiniz UIKeyboardDidHideNotification. Aksi takdirde, klavyenin arkasındaki kaydırma görünümü, klavye kapanma animasyonu süresince kırpılır.


32

En kolay çözüm bulundu

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    [self animateTextField: textField up: YES];
}


- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField: textField up: NO];
}

- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
    const int movementDistance = 80; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? -movementDistance : movementDistance);

    [UIView beginAnimations: @"anim" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}

Ekran, altta olmasa bile yukarı hareket eder. yani, metin alanı üstte ise ekranın dışına çıkar. Bu dava nasıl kontrol edilir?
MELWIN

@MELWIN Sadece bu satırdan sonra ekleyin: int movement = (up ? -movementDistance : movementDistance); if (textField.frame.origin.y < self.view.frame.size.height - keyboard.height) { movementDistance = 0 }Lütfen keyboarddeğişkenin, yaptığınız açılır klavyenin CGRect'i olmadığını unutmayın:let keyboard = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey]!.CGRectValue())!
CapturedTree

31

Birçok UITextField için çalışan küçük düzeltme

#pragma mark UIKeyboard handling

#define kMin 150

-(void)textFieldDidBeginEditing:(UITextField *)sender
{
   if (currTextField) {
      [currTextField release];
   }
   currTextField = [sender retain];
   //move the main view, so that the keyboard does not hide it.
   if (self.view.frame.origin.y + currTextField.frame.origin. y >= kMin) {
        [self setViewMovedUp:YES]; 
   }
}



//method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
   [UIView beginAnimations:nil context:NULL];
   [UIView setAnimationDuration:0.3]; // if you want to slide up the view

   CGRect rect = self.view.frame;
   if (movedUp)
   {
      // 1. move the view's origin up so that the text field that will be hidden come above the keyboard 
      // 2. increase the size of the view so that the area behind the keyboard is covered up.
      rect.origin.y = kMin - currTextField.frame.origin.y ;
   }
   else
   {
      // revert back to the normal state.
      rect.origin.y = 0;
   }
   self.view.frame = rect;

   [UIView commitAnimations];
}


- (void)keyboardWillShow:(NSNotification *)notif
{
   //keyboard will be shown now. depending for which textfield is active, move up or move down the view appropriately

   if ([currTextField isFirstResponder] && currTextField.frame.origin.y + self.view.frame.origin.y >= kMin)
   {
      [self setViewMovedUp:YES];
   }
   else if (![currTextField isFirstResponder] && currTextField.frame.origin.y  + self.view.frame.origin.y < kMin)
   {
      [self setViewMovedUp:NO];
   }
}

- (void)keyboardWillHide:(NSNotification *)notif
{
   //keyboard will be shown now. depending for which textfield is active, move up or move down the view appropriately
   if (self.view.frame.origin.y < 0 ) {
      [self setViewMovedUp:NO];
   }

}


- (void)viewWillAppear:(BOOL)animated
{
   // register for keyboard notifications
   [[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]; 
}

- (void)viewWillDisappear:(BOOL)animated
{
   // unregister for keyboard notifications while not visible.
   [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; 
}

rect.origin.y=+currTextField.frame.origin.yiyi çalışıyor teşekkür ederim
u.gen

30

RPDP'nin kodu metin alanını klavyeden başarıyla kaldırır. Ancak klavyeyi kullandıktan ve çıkardıktan sonra yukarı kaydırdığınızda, üst görünümden yukarı kaydırılmıştır. Bu, Simülatör ve cihaz için geçerlidir. Bu görünümün üst kısmındaki içeriği okumak için, kişinin görünümü yeniden yüklemesi gerekir.

Aşağıdaki kodunun görüşü geri getirmesi gerekmez mi?

else
{
    // revert back to the normal state.
    rect.origin.y += kOFFSET_FOR_KEYBOARD;
    rect.size.height -= kOFFSET_FOR_KEYBOARD;
}

23

Görünümü yukarı taşımak doğru bir yaklaşım olup olmadığından emin değilim, UIScrollView yeniden boyutlandırma, farklı bir şekilde yaptım. Küçük bir makalede ayrıntılı olarak açıkladım


Makalenin bağlantısı öldü.
Teo

22

Orijinal görünüm durumuna geri dönmek için şunu ekleyin:

-(void)textFieldDidEndEditing:(UITextField *)sender

{
    //move the main view, so that the keyboard does not hide it.
    if  (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}

20

Bu kısa numarayı deneyin.

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    [self animateTextField: textField up: YES];
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField: textField up: NO];
}

- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
    const int movementDistance = textField.frame.origin.y / 2; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? -movementDistance : movementDistance);

    [UIView beginAnimations: @"anim" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}

19

Çok fazla çözüm var, ama çalışmaya başlamadan önce birkaç saat geçirdim. Yani, bu kodu buraya koydum (sadece projeye yapıştırın, herhangi bir değişiklik gerekmez):

@interface RegistrationViewController : UIViewController <UITextFieldDelegate>{
    UITextField* activeField;
    UIScrollView *scrollView;
}
@end

- (void)viewDidLoad
{
    [super viewDidLoad];

    scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];

    //scrool view must be under main view - swap it
    UIView* natView = self.view;
    [self setView:scrollView];
    [self.view addSubview:natView];

    CGSize scrollViewContentSize = self.view.frame.size;
    [scrollView setContentSize:scrollViewContentSize];

    [self registerForKeyboardNotifications];
}

- (void)viewDidUnload {
    activeField = nil;
    scrollView = nil;
    [self unregisterForKeyboardNotifications];
    [super viewDidUnload];
}

- (void)registerForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShown:)
                                                 name:UIKeyboardWillShowNotification object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillBeHidden:)
                                                 name:UIKeyboardWillHideNotification object:nil];

}

-(void)unregisterForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:UIKeyboardWillShowNotification
                                                  object:nil];
    // unregister for keyboard notifications while not visible.
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:UIKeyboardWillHideNotification
                                                  object:nil];
}

- (void)keyboardWillShown:(NSNotification*)aNotification
{
    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    CGRect frame = self.view.frame;
    frame.size.height -= kbSize.height;
    CGPoint fOrigin = activeField.frame.origin;
    fOrigin.y -= scrollView.contentOffset.y;
    fOrigin.y += activeField.frame.size.height;
    if (!CGRectContainsPoint(frame, fOrigin) ) {
        CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y + activeField.frame.size.height - frame.size.height);
        [scrollView setContentOffset:scrollPoint animated:YES];
    }
}

- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
     [scrollView setContentOffset:CGPointZero animated:YES];
}

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    activeField = textField;
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    activeField = nil;
}

-(BOOL) textFieldShouldReturn:(UITextField *)textField
{
    [textField resignFirstResponder];
    return YES;
}

PS: Umarım kod birisinin istenen etkiyi hızlı bir şekilde yapmasına yardımcı olur. (Xcode 4.5)


Merhaba Hotjard, ben [self.view addSubview: natView] EXE_BAD_ACCESS alıyorum;
Bala

18

@ user271753

Görünümünüzü orijinal eklentiye geri döndürmek için:

-(BOOL)textFieldShouldReturn:(UITextField *)textField{
   [textField resignFirstResponder];
   [self setViewMovedUp:NO];
   return YES;
}

16

Görünüm çerçevesini hareket ettirmek için bir kaydırma görünümü gerektirmez. Bir viewcontroller'sgörünümün çerçevesini , tüm görünümün ilk yanıtlayan metin alanını klavyenin üzerine yerleştirecek kadar yukarı hareket etmesi için değiştirebilirsiniz. Bu problemle karşılaştığımda bir alt sınıf oluşturdum.UIViewController . Klavyenin bildirim göreceğini gözlemler ve ilk yanıtlayan alt görünümünü bulur ve (gerekirse) ana görünümü yeterince yanıtlar. Böylece ilk yanıtlayıcı klavyenin üzerinde olur. Klavye gizlendiğinde, görünümü eski haline geri getirir.

Bu alt sınıfı kullanmak için özel görünüm denetleyicinizi bir GMKeyboardVC alt sınıfı yapın ve bu özelliği devralır (yalnızca uygulayıp uygulamadığınızı viewWillAppearve viewWillDisappearsüper çağırmaları gerektiğini unutmayın). Sınıf github üzerindedir .


Hangi lisans? Bazı dosyalarınız açık kaynak lisansına sahip, bazıları ise yok.
jaime

Uyarı: Bu kod ARC projeleriyle uyumlu değildir.
Almo

Bunların ARC olmayan dosyalar olduğunu belirtmek için derleme seçeneğini eklersiniz veya ARC'ye dönüştürüp bir çekme isteği gönderebilirsiniz.
progrmr

14

Hızlı 4 .

Kolayca Yukarı ve aşağı hareket edebilmesi UITextFieldVeya UIViewile UIKeyBoardBirlikteAnimation resim açıklamasını buraya girin

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var textField: UITextField!
    @IBOutlet var chatView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange), name: .UIKeyboardWillChangeFrame, object: nil)
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        textField.resignFirstResponder()
    }

    @objc func keyboardWillChange(notification: NSNotification) {

        let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
        let curve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt
        let curFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
        let targetFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
        let deltaY = targetFrame.origin.y - curFrame.origin.y
        print("deltaY",deltaY)

        UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: {
            self.chatView.frame.origin.y+=deltaY // Here You Can Change UIView To UITextField
        },completion: nil)
    }

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

}

2
Neredeyse mükemmel. İPhone X'de klavye ve metin alanı arasında garip bir boşluk var.
Houman

12

İşte belirli bir düzen için bulduğum kesmek çözümü. Bu çözüm, bir bölümü görünüme kaydıran Matt Gallagher çözümüne benzer. Hala iPhone geliştirmede yeniyim ve mizanpajların nasıl çalıştığını bilmiyorum. Böylece, bu kesmek.

Uygulamamın bir alanı tıklarken kaydırmayı ve kullanıcı klavyede bir sonraki seçtiğinde kaydırmayı desteklemesi gerekiyordu.

Yüksekliği 775 olan bir UIView'im vardı. Kontroller temel olarak 3'lü gruplar halinde geniş bir alana yayıldı. Sonunda aşağıdaki IB düzenini buldum.

UIView -> UIScrollView -> [UI Components]

İşte kesmek geliyor

UIScrollView yüksekliğini gerçek düzenden (1250) 500 birim daha büyük olarak ayarladım. Daha sonra, kaydırmam gereken mutlak konumlara ve IB Etiket numarasına göre almak için basit bir işleve sahip bir dizi oluşturdum.

static NSInteger stepRange[] = {
    0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 140, 140, 140, 140, 410
};

NSInteger getScrollPos(NSInteger i) {
    if (i < TXT_FIELD_INDEX_MIN || i > TXT_FIELD_INDEX_MAX) {
        return 0 ;
    return stepRange[i] ;
}

Şimdi tek yapmanız gereken textFieldDidBeginEditing ve textFieldShouldReturn'de (bir sonraki alan gezintisi oluşturuyorsanız ikincisi) aşağıdaki iki kod satırını kullanmaktır

CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ;
[self.scrollView setContentOffset:point animated:YES] ;

Bir örnek.

- (void) textFieldDidBeginEditing:(UITextField *)textField
{
    CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ;
    [self.scrollView setContentOffset:point animated:YES] ;
}


- (BOOL)textFieldShouldReturn:(UITextField *)textField {

    NSInteger nextTag = textField.tag + 1;
    UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];

    if (nextResponder) {
        [nextResponder becomeFirstResponder];
        CGPoint point = CGPointMake(0, getScrollPos(nextTag)) ;
        [self.scrollView setContentOffset:point animated:YES] ;
    }
    else{
        [textField resignFirstResponder];
    }

    return YES ;
}

Diğer yöntemlerde olduğu gibi bu yöntem 'geri gitmez'. Bu bir gereklilik değildi. Yine bu oldukça 'uzun' bir UIView içindi ve iç düzen motorlarını öğrenmek için günlerim yoktu.


12

Gereğince dokümanlar , iOS 3.0 itibarıyla, UITableViewControllersınıf otomatik boyutlandırır ve metin alanlarının düzenleme in-line olduğunda onun masa görünümü yeniden konumlandırır. Bence metin alanınıUITableViewCellBazılarının belirttiği gibi .

Gönderen docs :

Tablo görünümü denetleyicisi, tablo görünümü satırlarının satır içi düzenlemesini destekler; Örneğin, satırlar düzenleme modunda katıştırılmış metin alanlarına sahipse, düzenlenen satırı görüntülenen sanal klavyenin üzerinde kaydırır.


Aynı yorumu buldum. Evet bu doğrudur. Garip bir şey, bir UITabelViewController ve ikincisinde çalışmıyor olmasıdır. Ama benim uygulamamda hiçbir farklılık bulamadım.
Morpheus78

11

Burada tuş takımını işlemek için en basit çözümü buldum.

Sadece örnek kodun altına kopyalayıp yapıştırmanız ve metin alanınızı veya yukarı taşımak istediğiniz herhangi bir görünümü değiştirmeniz yeterlidir.

Aşama 1

Denetleyicinizdeki iki yöntemin altına kopyalayıp yapıştırmanız yeterlidir

- (void)registerForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:)
                                                 name:UIKeyboardDidShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:)
                                                 name:UIKeyboardWillHideNotification object:nil];
}

- (void)deregisterFromKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

Adım 2

viewWillAppear ve viewWillDisappear yöntemlerinde sırasıyla Tuş Takımı Bildirimleri'ni kaydetme ve kaydını silme .

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self registerForKeyboardNotifications];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [self deregisterFromKeyboardNotifications];
    [super viewWillDisappear:animated];
}

Aşama 3

İşte ruh kısmı geliyor, Sadece metin alanınızı değiştirin ve yukarı doğru ne kadar hareket etmek istediğinizi değiştirin.

- (void)keyboardWasShown:(NSNotification *)notification
{
    NSDictionary* info = [notification userInfo];
    CGSize currentKeyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    //you need replace your textfield instance here
    CGPoint textFieldOrigin = self.tokenForPlaceField.frame.origin;
    CGFloat textFieldHeight = self.tokenForPlaceField.frame.size.height;

    CGRect visibleRect = self.view.frame;
    visibleRect.size.height -= currentKeyboardSize.height;

    if (!CGRectContainsPoint(visibleRect, textFieldOrigin))
    {
        //you can add yor desired height how much you want move keypad up, by replacing "textFieldHeight" below

        CGPoint scrollPoint = CGPointMake(0.0, textFieldOrigin.y - visibleRect.size.height  + textFieldHeight); //replace textFieldHeight to currentKeyboardSize.height, if you want to move up with more height
        [self.scrollView setContentOffset:scrollPoint animated:YES];
    }
}

- (void)keyboardWillBeHidden:(NSNotification *)notification
{
    [self.scrollView setContentOffset:CGPointZero animated:YES];
}

Referans : iyi, bu adamı takdir edin bu güzel kod snip, temiz çözümü paylaşan bu .

Umarım bu somurtkan birileri var.


Bunun en iyisi olduğunu sanmıyorum. Ithink @Dheeraj VS doğru: Bu metin alanı bir tablonun hücresindeyse (table.scrollable = NO olsa bile) kolayca ve otomatik olarak yapılabilir . NOT : tablonun konumu ve boyutu makul olmalıdır. örneğin: - tablonun y konumu görünümün altından 100 olarak sayılırsa, 300 yükseklik klavyesi tüm tablonun üzerine gelir. - tablonun yüksekliği = 10 ise ve klavyenin görünmesi için içindeki metin alanının 100 yukarı kaydırılması gerekiyorsa, o metin alanı tablonun sınırının dışında olacaktır.
samthui7

@ samthui7 Dheeraj yanıtı yalnızca bir tableview değil, yalnızca TableViewController kullanıyorsanız çalışır. Bazen uygun olmayan bir kısıtlama haline getirir.
Ben G

10

Bu konuda yeni başlayanlar için iyi bir öğretici arıyor, burada en iyi öğretici bulundu .

Eğiticinin altındaki MIScrollView.hörnekte şuraya boşluk bıraktığınızdan emin olun:

@property (nonatomic, retain) id backgroundTapDelegate;

Gördüğünüz gibi.


Merhaba savagenoob, sağlanan bağlantı için teşekkürler ve stackoverflow hoş geldiniz. Lütfen (gelecekteki) soruları cevaplarken mümkün olduğunca çok bilgi sağlayın - basit bağlantılar biraz kırılgandır. Bununla birlikte, cevap göz ardı edilebilecek iyi bir öğreticiye bağlantı ise.
Maarten Bodewes

10

Ne zaman UITextFielda içindedirUITableViewCell kaydırma otomatik kurulum olmalıdır.

Değilse, büyük olasılıkla tablo görünümünün yanlış kodu / kurulumu nedeniyle.

Örneğin, uzun masamı UITextFieldaşağıdaki gibi bir tane ile yeniden yüklediğimde ,

-(void) viewWillAppear:(BOOL)animated
{
   [self.tableview reloadData];
}

alt kısımdaki metin alanım, metin alanının içine tıkladığımda ortaya çıkan klavye tarafından gizlendi.

Bunu düzeltmek için bunu yapmam gerekiyordu -

-(void) viewWillAppear:(BOOL)animated
{
    //add the following line to fix issue
    [super viewWillAppear:animated];
    [self.tableview reloadData];
}

Bu kodun ne için olduğunu kafam karıştı? Klavye gösterildiğinde, viewWillAppearçağrılmaz. Ve reloadDatagizlenmiş satırları görünür hale getirmez.
Adam Johns

10

Bir satır bile yazmanıza gerek olmayan bu üçüncü tarafı kullanın

https://github.com/hackiftekhar/IQKeyboardManager

projeyi indirin ve projenize sürükleyip bırakın IQKeyboardManager. Herhangi bir sorunla karşılaşırsanız lütfen READMEbelgeyi okuyun .

Çocuklar gerçekten klavye yönetmek için baş ağrısı kaldırmak.


8

Not : Bu cevap, textField öğenizin scrollView içinde olduğunu varsayar.

Benim görünüm çerçeveler ile karışıklık yerine scrollContentInset ve scrollContentOffset kullanarak bu başa çıkmak tercih.

Önce klavye bildirimlerini dinleyelim

//call this from viewWillAppear
-(void)addKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShow:)
                                                 name:UIKeyboardWillShowNotification
                                               object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillHide:)
                                                 name:UIKeyboardWillHideNotification
                                               object:nil];
}
//call this from viewWillDisappear
-(void)removeKeyboardNotifications{
    [[NSNotificationCenter default
    Center] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

Sonraki adım, geçerli ilk yanıtlayıcıyı (şu anda klavyeye sahip olan UITextfield / UITextVIew) temsil eden bir özelliği korumaktır.

Bu özelliği ayarlamak için delege yöntemlerini kullanırız. Başka bir bileşen kullanıyorsanız, benzer bir şeye ihtiyacınız olacaktır.

Metin alanı için bunu didBeginEditing'e ve shouldBeginEditing'de textView için ayarladığımızı unutmayın. Bunun nedeni, textViewDidBeginEditing'in UIKeyboardWillShowNotification işleminden sonra herhangi bir nedenle çağrılmasıdır.

-(BOOL)textViewShouldBeginEditing:(UITextView * )textView{
    self.currentFirstResponder = textView;
    return YES;
}

-(void)textFieldDidBeginEditing:(UITextField *)textField{
    self.currentFirstResponder = textField;
}

Son olarak, işte sihir

- (void)keyboardWillShow:(NSNotification*)aNotification{
    NSDictionary* info = [aNotification userInfo];
    CGRect kbFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];


    /*if currentFirstResponder is overlayed by the keyboard, move it so it bottom ends where the keyboard begins*/
    if(self.currentFirstResponder){

        //keyboard origin in currentFirstResponderFrame
        CGPoint keyboardOrigin = [self.currentFirstResponder convertPoint:kbFrame.origin fromView:nil];

        float spaceBetweenFirstResponderAndKeyboard = abs(self.currentFirstResponder.frame.size.height-keyboardOrigin.y);

        //only scroll the scrollview if keyboard overlays the first responder
        if(spaceBetweenFirstResponderAndKeyboard>0){
            //if i call setContentOffset:animate:YES it behaves differently, not sure why
            [UIView animateWithDuration:0.25 animations:^{
                [self.scrollView setContentOffset:CGPointMake(0,self.scrollView.contentOffset.y+spaceBetweenFirstResponderAndKeyboard)];
            }];
        }
    }

    //set bottom inset to the keyboard height so you can still scroll the whole content

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbFrame.size.height, 0.0);
    _scrollView.contentInset = contentInsets;
    _scrollView.scrollIndicatorInsets = contentInsets;

}

- (void)keyboardWillHide:(NSNotification*)aNotification{
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    _scrollView.contentInset = contentInsets;
    _scrollView.scrollIndicatorInsets = contentInsets;
}

8

Swift kullanan çözüm budur.

import UIKit

class ExampleViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var scrollView: UIScrollView!

    @IBOutlet var textField1: UITextField!
    @IBOutlet var textField2: UITextField!
    @IBOutlet var textField3: UITextField!
    @IBOutlet var textField4: UITextField!
    @IBOutlet var textField5: UITextField!

    var activeTextField: UITextField!

    // MARK: - View
    override func viewDidLoad() {
        super.viewDidLoad()
        self.textField1.delegate = self
        self.textField2.delegate = self
        self.textField3.delegate = self
        self.textField4.delegate = self
        self.textField5.delegate = self
    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        self.registerForKeyboardNotifications()
    }

    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
        self.unregisterFromKeyboardNotifications()
    }

    // MARK: - Keyboard

    // Call this method somewhere in your view controller setup code.
    func registerForKeyboardNotifications() {
        let center:  NSNotificationCenter = NSNotificationCenter.defaultCenter()
        center.addObserver(self, selector: "keyboardWasShown:", name: UIKeyboardDidShowNotification, object: nil)
        center.addObserver(self, selector: "keyboardWillBeHidden:", name: UIKeyboardWillHideNotification, object: nil)
    }

    func unregisterFromKeyboardNotifications () {
        let center:  NSNotificationCenter = NSNotificationCenter.defaultCenter()
        center.removeObserver(self, name: UIKeyboardDidShowNotification, object: nil)
        center.removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
    }

    // Called when the UIKeyboardDidShowNotification is sent.
    func keyboardWasShown (notification: NSNotification) {
        let info : NSDictionary = notification.userInfo!
        let kbSize = (info.objectForKey(UIKeyboardFrameBeginUserInfoKey)?.CGRectValue() as CGRect!).size

        let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
        scrollView.contentInset = contentInsets;
        scrollView.scrollIndicatorInsets = contentInsets;

        // If active text field is hidden by keyboard, scroll it so it's visible
        // Your app might not need or want this behavior.
        var aRect = self.view.frame
        aRect.size.height -= kbSize.height;
        if (!CGRectContainsPoint(aRect, self.activeTextField.frame.origin) ) {
            self.scrollView.scrollRectToVisible(self.activeTextField.frame, animated: true)
        }
    }

    // Called when the UIKeyboardWillHideNotification is sent
    func keyboardWillBeHidden (notification: NSNotification) {
        let contentInsets = UIEdgeInsetsZero;
        scrollView.contentInset = contentInsets;
        scrollView.scrollIndicatorInsets = contentInsets;
    }

    // MARK: -  Text Field

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

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

}

Doğru cevap, ancak hem TextField hem de TextView kullanırken nil sorun yaşıyorum. Herhangi bir yardım?
Thiha Aung

@Thiha Aung, Kaynak kodunuzdaki IBOutlet değişkenleriniz IB'ye bağlı mı?
Homam

Evet onlar da bağlı.OIT satırında UITextView kullanırken bu hatayı yaşıyorum: if (! CGRectContainsPoint (aRect, self.activeTextField.frame.origin)) {
Aung

Self.activeTextField sıfır
Aung
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.