wake-up-neo.net

Wie verschiebe ich eine UIScrollView programmgesteuert, um mich auf ein Steuerelement über der Tastatur zu konzentrieren?

Ich habe 6 UITextFields auf meinem UIScrollView. Jetzt kann ich nach Userwunsch scrollen. Wenn die Tastatur angezeigt wird, werden einige Textfelder ausgeblendet.

Das ist nicht benutzerfreundlich.

Wie scrolle ich programmatisch in der Ansicht, damit die Tastatur das Textfeld nicht verdeckt?

50
mamcx

Zum Schluss eine einfache Lösung:

UIScrollView* v = (UIScrollView*) self.view ;
CGRect rc = [textField bounds];
rc = [textField convertRect:rc toView:v];
rc.Origin.x = 0 ;
rc.Origin.y -= 60 ;

rc.size.height = 400;
[self.scroll scrollRectToVisible:rc animated:YES];

Jetzt denke ich ist das nur noch mit dem obigen Link kombinierbar und gesetzt!

38
mamcx

Hier ist, was für mich gearbeitet hat. Eine Instanzvariable, die den Wert des Versatzes von UIScrollView enthält bevor die Ansicht für die Tastatur angepasst wird, damit Sie den vorherigen Status wiederherstellen können, nachdem UITextField zurückgegeben hat:

//header
@interface TheViewController : UIViewController <UITextFieldDelegate> {
    CGPoint svos;
}


//implementation
- (void)textFieldDidBeginEditing:(UITextField *)textField {
    svos = scrollView.contentOffset;
    CGPoint pt;
    CGRect rc = [textField bounds];
    rc = [textField convertRect:rc toView:scrollView];
    pt = rc.Origin;
    pt.x = 0;
    pt.y -= 60;
    [scrollView setContentOffset:pt animated:YES];           
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    [scrollView setContentOffset:svos animated:YES]; 
    [textField resignFirstResponder];
    return YES;
}
77
james_womack

Ich habe eine universelle Drop-In-Unterklasse für UIScrollView und UITableView zusammengestellt, die dafür sorgt, dass alle darin enthaltenen Textfelder nicht mehr auf der Tastatur angezeigt werden.

Wenn die Tastatur angezeigt wird, findet die Unterklasse die zu bearbeitende Unteransicht und passt den Frame- und Inhaltsversatz an, um sicherzustellen, dass die Ansicht sichtbar ist. Die Animation passt sich dem Tastatur-Popup an. Wenn die Tastatur ausgeblendet wird, wird die vorherige Größe wiederhergestellt.

Grundsätzlich sollte es mit jedem Setup funktionieren, entweder mit einer UITableView-basierten Schnittstelle oder einer aus manuell platzierten Ansichten bestehenden.

hier es ist.


(For google: TPKeyboardAvoiding, TPKeyboardAvoidingScrollView, TPKeyboardAvoidingCollectionView.)
Editor's note: TPKeyboardAvoiding seems to be continually updated and fresh, as of 2014.
22
Michael Tyson

einfach und am besten

- (void)textFieldDidBeginEditing:(UITextField *)textField
{

  // self.scrlViewUI.contentOffset = CGPointMake(0, textField.frame.Origin.y);
    [_scrlViewUI setContentOffset:CGPointMake(0,textField.center.y-90) animated:YES];
    tes=YES;
    [self viewDidLayoutSubviews];
}
2
NRV

Wenn Sie das delegate Ihrer Textfelder auf ein Controller-Objekt in Ihrem Programm setzen, können Sie das textFieldDidBeginEditing: und textFieldShouldReturn: Methoden. Mit der ersten Methode können Sie dann zu Ihrem Textfeld blättern und mit der zweiten Methode können Sie zurückblättern.

Code, den ich dafür verwendet habe, finden Sie in meinem Blog: Verschieben von UITextViews, um die Tastatur zu umgehen . Ich habe diesen Code nicht für Textansichten in einem UIScrollView getestet, aber es sollte funktionieren.

2
Matt Gallagher

Die bisher geposteten Antworten haben bei mir nicht funktioniert, da ich eine ziemlich tief verschachtelte Struktur von UIViews habe. Außerdem hatte ich das Problem, dass einige dieser Antworten nur bei bestimmten Geräteorientierungen funktionierten.

Hier ist meine Lösung, mit der Sie hoffentlich weniger Zeit verlieren.

Mein UIViewTextView leitet sich von UIView ab, ist ein UITextView-Delegat und fügt ein UITextView hinzu, nachdem einige Parameter aus einer XML-Datei für dieses UITextView gelesen wurden (dieser XML-Teil ist hier aus Gründen der Klarheit weggelassen).

Hier ist die Definition der privaten Schnittstelle:

#import "UIViewTextView.h"
#import <CoreGraphics/CoreGraphics.h>
#import <CoreGraphics/CGColor.h>

@interface UIViewTextView (/**/) {
  @private
  UITextView *tf;

  /*
   * Current content scroll view
   * position and frame
   */
  CGFloat currentScrollViewPosition;
  CGFloat currentScrollViewHeight;
  CGFloat kbHeight;
  CGFloat kbTop;

  /*
   * contentScrollView is the UIScrollView
   * that contains ourselves.
   */
  UIScrollView contentScrollView;
}
@end

In der init-Methode muss ich die Event-Handler registrieren:

@implementation UIViewTextView

- (id) initWithScrollView:(UIScrollView*)scrollView {
  self              = [super init];

  if (self) {
    contentScrollView = scrollView;

    // ...

    tf = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, 241, 31)];

    // ... configure tf and fetch data for it ...

    tf.delegate = self;

    // ...

    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    [nc addObserver:self selector:@selector(keyboardWasShown:) name: UIKeyboardWillShowNotification object:nil];
    [nc addObserver:self selector:@selector(keyboardWasHidden:) name: UIKeyboardWillHideNotification object:nil];
    [self addSubview:tf];
  }

  return(self);
}

Sobald dies erledigt ist, müssen wir das Keyboard-Show-Ereignis behandeln. Dies wird aufgerufen, bevor textViewBeginEditing aufgerufen wird, sodass wir damit einige Eigenschaften der Tastatur herausfinden können. Im Wesentlichen möchten wir die Höhe der Tastatur kennen. Dies muss leider aus seiner width-Eigenschaft im Querformat entnommen werden:

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

  CGRect screenRect                  = [[UIScreen mainScreen] bounds];
  CGFloat sWidth                     = screenRect.size.width;
  CGFloat sHeight                    = screenRect.size.height;

  UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];

  if ((orientation == UIDeviceOrientationPortrait)
      ||(orientation == UIDeviceOrientationPortraitUpsideDown)) {
    kbHeight     = kbSize.height;
    kbTop        = sHeight - kbHeight;
  } else {
    //Note that the keyboard size is not oriented
    //so use width property instead
    kbHeight     = kbSize.width;
    kbTop        = sWidth - kbHeight;
  }

Als nächstes müssen wir tatsächlich einen Bildlauf durchführen, wenn wir mit der Bearbeitung beginnen. Das machen wir hier:

- (void) textViewDidBeginEditing:(UITextView *)textView {
  /*
   * Memorize the current scroll position
   */
  currentScrollViewPosition = contentScrollView.contentOffset.y;

  /*
   * Memorize the current scroll view height
   */
  currentScrollViewHeight   = contentScrollView.frame.size.height;

  // My top position
  CGFloat myTop    = [self convertPoint:self.bounds.Origin toView:[UIApplication sharedApplication].keyWindow.rootViewController.view].y;

  // My height
  CGFloat myHeight = self.frame.size.height;

  // My bottom
  CGFloat myBottom = myTop + myHeight;

  // Eventual overlap
  CGFloat overlap  = myBottom - kbTop;

  /*
   * If there's no overlap, there's nothing to do.
   */
  if (overlap < 0) {
    return;
  }

  /*
   * Calculate the new height
   */
  CGRect crect = contentScrollView.frame;
  CGRect nrect = CGRectMake(crect.Origin.x, crect.Origin.y, crect.size.width, currentScrollViewHeight + overlap);

  /*
   * Set the new height
   */
  [contentScrollView setFrame:nrect];

  /*
   * Set the new scroll position
   */
  CGPoint npos;

  npos.x = contentScrollView.contentOffset.x;
  npos.y = contentScrollView.contentOffset.y + overlap;

  [contentScrollView setContentOffset:npos animated:NO];
}

Wenn wir die Bearbeitung beenden, setzen wir die Bildlaufposition zurück:

- (void) textViewDidEndEditing:(UITextView *)textView {
  /*
   * Reset the scroll view position
   */
  CGRect crect = contentScrollView.frame;
  CGRect nrect = CGRectMake(crect.Origin.x, crect.Origin.y, crect.size.width, currentScrollViewHeight);

  [contentScrollView setFrame:nrect];

  /*
   * Reset the scroll view height
   */
  CGPoint npos;

  npos.x = contentScrollView.contentOffset.x;
  npos.y = currentScrollViewPosition;

  [contentScrollView setContentOffset:npos animated:YES];
  [tf resignFirstResponder];

  // ... do something with your data ...

}

Es ist nichts mehr zu tun auf der Tastatur, sondern ein versteckter Event-Handler. wir belassen es trotzdem:

-(void)keyboardWasHidden:(NSNotification*)aNotification {
}

Und das ist es.

/*
   // Only override drawRect: if you perform custom drawing.
   // An empty implementation adversely affects performance during animation.
   - (void)drawRect:(CGRect)rect
   {
   // Drawing code
   }
 */

@end
1
Matthias Nott

Sie können es überprüfen: https://github.com/michaeltyson/TPKeyboardAvoiding (Ich habe dieses Beispiel für meine Apps verwendet). Es funktioniert so gut. Ich hoffe das hilft dir.


Im Folgenden finden Sie ein vollständiges Tutorial zur Verwendung von TPKeyboardAvoiding, das möglicherweise jemandem hilft

(1) Laden Sie die Zip-Datei vom Github-Link herunter. Füge diese vier Dateien zu deinem Xcode Projekt hinzu:

enter image description here

(2) Bauen Sie Ihre schöne Form in IB. Fügen Sie eine UIScrollView hinzu. Setzen Sie die Formularelemente IN die Bildlaufansicht. (Hinweis - äußerst nützlicher Tipp zum Interface Builder: https://stackoverflow.com/a/16952902/294884 )

enter image description here

(3) Klicken Sie auf in der Bildlaufansicht. Dann sehen Sie oben rechts auf der dritten Schaltfläche das Wort "UIScrollView". mit Kopieren und Einfügen Ändern in "TPKeyboardAvoidingScrollView"

enter image description here

(4) das war's. Legen Sie die App in den App Store und stellen Sie sie Ihrem Kunden in Rechnung.

(Klicken Sie auch einfach auf die Registerkarte "Informationen" in der Bildlaufansicht. Möglicherweise möchten Sie das Bouncen und die Bildlaufleisten lieber ein- oder ausschalten.)

Persönlicher Kommentar - Ich empfehle dringend, in fast allen Fällen die Bildlauf- oder Sammlungsansicht für Eingabeformulare zu verwenden. Verwenden Sie keine Tabellenansicht. Dies ist aus vielen Gründen problematisch. und ganz einfach, es ist unglaublich einfacher, eine Bildlaufansicht zu verwenden. Leg es einfach so aus, wie du willst. Es ist 100% wysiwyg in Interface Builder. hoffe es hilft

1
hightech

Dies ist mein Code, hoffe, es wird Ihnen helfen. Es funktioniert in Ordnung, wenn Sie viele Textfelder haben

CGPoint contentOffset;
bool isScroll;
- (void)textFieldDidBeginEditing:(UITextField *)textField {
    contentOffset = self.myScroll.contentOffset;
    CGPoint newOffset;
    newOffset.x = contentOffset.x;
    newOffset.y = contentOffset.y;
    //check Push return in keyboar
    if(!isScroll){
        //180 is height of keyboar
        newOffset.y += 180;
        isScroll=YES;
    }
   [self.myScroll setContentOffset:newOffset animated:YES];
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField{
    //reset offset of content
    isScroll = NO;
    [self.myScroll setContentOffset:contentOffset animated:YES];
    [textField endEditing:true];
    return  true;
}

wir haben einen Punkt contentOffset zum Speichern des Inhaltsoffsets der Scrollansicht vor der Keyboar-Show. Dann scrollen wir den Inhalt für y ungefähr 180 (Höhe des Keyboars). Wenn Sie im Keyboar die Eingabetaste berühren, wird der Inhalt zum alten Punkt gescrollt (es ist contentOffset). Wenn Sie viele Textfelder haben, berühren Sie Return in Keyboar nicht, aber Sie berühren ein anderes Textfeld, es wird +180. Also haben wir check touch return

1

Ich weiß, dass dies alt ist, aber immer noch hatte keine der obigen Lösungen das ausgefallene Positionierungsmaterial, das für diese "perfekte" fehlerfreie, abwärtskompatible und flimmerfreie Animation erforderlich war.
Lassen Sie mich meine Lösung mitteilen (vorausgesetzt, Sie haben UIKeyboardWill(Show|Hide)Notification eingerichtet):

// 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];
}
1
Martin Ullrich

Ich habe einige der oben genannten Lösungen geändert, um das Verständnis und die Verwendung zu vereinfachen. Ich habe ein IBOutlet verwendet, damit mehrere Textfelder unter "Gesendete Ereignisse" der Textfelder mit der Funktion "Bearbeiten hat begonnen" verknüpft werden können. ** Vergessen Sie nicht, eine Steckdose für Ihre Scroll-Ansicht zu haben

- (IBAction)moveViewUpwards:(id)sender
{
    CGRect rc = [sender convertRect:[sender bounds] toView:scrollView];
    rc.Origin.x = 0 ;
    rc.Origin.y -= 60 ;

    rc.size.height = 400;
    [scrollView scrollRectToVisible:rc animated:YES];

}
0
pdpAxis

Verwenden Sie eine dieser,

CGPoint bottomOffset = CGPointMake(0, self.MainScrollView.contentSize.height - self.MainScrollView.bounds.size.height);

[self.MainScrollView setContentOffset:bottomOffset animated:YES];

oder

[self.MainScrollView scrollRectToVisible:CGRectMake(0, self.MainScrollView.contentSize.height - self.MainScrollView.bounds.size.height-30, MainScrollView.frame.size.width, MainScrollView.frame.size.height) animated:YES];
0
Ramdhas

In Swift 1.2+ mache so etwas:

class YourViewController: UIViewController, UITextFieldDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        _yourTextField.delegate = self //make sure you have the delegate set to this view controller for each of your textFields so textFieldDidBeginEditing can be called for each one
        ...

    }

    func textFieldDidBeginEditing(textField: UITextField) {
        var point = textField.convertPoint(textField.frame.Origin, toView: _yourScrollView)
        point.x = 0.0 //if your textField does not have an Origin at 0 for x and you don't want your scrollView to shift left and right but rather just up and down
        _yourScrollView.setContentOffset(point, animated: true)
    }

    func textFieldDidEndEditing(textField: UITextField) {
        //Reset scrollview once done editing
        scrollView.setContentOffset(CGPoint.zero, animated: true)
    }

}
0
ColossalChris

Ich denke, es ist besser, Tastaturbenachrichtigungen zu verwenden, da Sie nicht wissen, ob der Ersthelfer (das Steuerelement mit Fokus auf) ein Textfeld oder eine Textansicht (oder was auch immer) ist. Erstellen Sie also eine Kategorie, um den Ersthelfer zu finden:

#import "UIResponder+FirstResponder.h"

static __weak id currentFirstResponder;

@implementation UIResponder (FirstResponder)

+(id)currentFirstResponder {
   currentFirstResponder = nil;
   [[UIApplication sharedApplication] sendAction:@selector(findFirstResponder:) to:nil from:nil forEvent:nil];
   return currentFirstResponder;
}

-(void)findFirstResponder:(id)sender {
   currentFirstResponder = self;
}

@end

dann

-(void)keyboardWillShowNotification:(NSNotification*)aNotification{

    contentScrollView.delegate=nil;
    contentScrollView.scrollEnabled=NO;
    contentScrollViewOriginalOffset = contentScrollView.contentOffset;

    UIResponder *lc_firstResponder = [UIResponder currentFirstResponder];
    if([lc_firstResponder isKindOfClass:[UIView class]]){

        UIView *lc_view = (UIView *)lc_firstResponder;

        CGRect lc_frame = [lc_view convertRect:lc_view.bounds toView:contentScrollView];
        CGPoint lc_point = CGPointMake(0, lc_frame.Origin.y-lc_frame.size.height);
        [contentScrollView setContentOffset:lc_point animated:YES];
    }
}

Deaktivieren Sie schließlich den Bildlauf, und setzen Sie den Delegaten auf Null. Stellen Sie ihn dann wieder her, um einige Aktionen während der Ausgabe des ersten Responders zu vermeiden. Behalten Sie, wie james_womack sagte, den ursprünglichen Offset bei, um ihn in einer keyboardWillHideNotification-Methode wiederherzustellen.

-(void)keyboardWillHideNotification:(NSNotification*)aNotification{

    contentScrollView.delegate=self;
    contentScrollView.scrollEnabled=YES;
    [contentScrollView setContentOffset:contentScrollViewOriginalOffset animated:YES];
}
0
Bejil