webentwicklung-frage-antwort-db.com.de

Das Animieren von UIScrollView contentInset verursacht Sprungstottern

Ich habe ein benutzerdefiniertes Aktualisierungssteuerelement implementiert (meine eigene Klasse, keine Unterklasse), und aus irgendeinem Grund, seit ich auf iOS 8 umgestiegen bin, führt das Festlegen des contentInset der Bildlaufansicht (insbesondere UICollectionView) zum Starten der Aktualisierungsanimation zu einem seltsamen Sprung/Stottern. Hier ist mein Code:

- (void)containingScrollViewDidScroll:(UIScrollView *)scrollView
{
    CGFloat scrollPosition = scrollView.contentOffset.y + scrollView.contentInset.top;

    if( scrollPosition > 0 || self.isRefreshing )
    {
        return;
    }

    CGFloat percentWidth = fabs( scrollPosition ) / self.frame.size.height / 2;

    CGRect maskFrame = self.maskLayer.frame;

    maskFrame.size.width = self.imageLayer.frame.size.width * percentWidth;

    [CATransaction begin];
    [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
    self.maskLayer.frame = maskFrame;
    [CATransaction commit];
}

- (void)containingScrollViewDidEndDragging:(UIScrollView *)scrollView
{
    if( ( self.maskLayer.frame.size.width >= self.imageLayer.frame.size.width ) && !self.isRefreshing )
    {
        self.isRefreshing = YES;
        [self setLoadingScrollViewInsets:scrollView];
        [self startAnimation];
        [self sendActionsForControlEvents:UIControlEventValueChanged];
    }
}

- (void)setLoadingScrollViewInsets:(UIScrollView *)scrollView
{
    UIEdgeInsets loadingInset = scrollView.contentInset;
    loadingInset.top += self.frame.size.height;

    UIViewAnimationOptions options = UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState;

    [UIView animateWithDuration:0.2 delay:0 options:options animations:^
    {
        scrollView.contentInset = loadingInset;
    }
    completion:nil];
}

Grundsätzlich animiere ich das contentInset auf der Höhe des Refresh-Steuerelements, sobald der Benutzer die Aktualisierung freigibt. Ich denke, die Animation würde das Stottern/Ruckeln reduzieren, wie es in iOS 7 der Fall war. Wenn in iOS 8 die ScrollView-Funktion jedoch vom Ziehen befreit wird, springt der Inhalt der Scroll-Ansicht nicht nur zum contentInset, sondern springt tatsächlich vom Release-Punkt ab schnell und animiert dann reibungslos. Ich bin mir nicht sicher, ob dies ein Fehler in iOS 8 ist oder was. Ich habe auch versucht, Folgendes hinzuzufügen:

scrollView.contentOffset = CGPointZero;

im Animationsblock, der nichts geändert hat.

Hat jemand irgendwelche Ideen? Jede Hilfe wäre sehr dankbar. Vielen Dank!

28
ryanthon

Ich habe die Methode mit meinem Animationsblock geändert in: 

- (void)setLoadingScrollViewInsets:(UIScrollView *)scrollView
{
    UIEdgeInsets loadingInset = scrollView.contentInset;
    loadingInset.top += self.view.frame.size.height;

    CGPoint contentOffset = scrollView.contentOffset;

    [UIView animateWithDuration:0.2 animations:^
    {
        scrollView.contentInset = loadingInset;
        scrollView.contentOffset = contentOffset;
    }];
}
30
ryanthon
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
    CGPoint point = scrollView.contentOffset;

    static CGFloat refreshViewHeight = 200.0f;

    if (scrollView.contentInset.top == refreshViewHeight) {
        //openning
        if (point.y>-refreshViewHeight) {
            //will close
            //必须套两层animation才能避免闪动!
            [UIView animateWithDuration:0 animations:NULL completion:^(BOOL finished) {
                [UIView animateWithDuration:0.25 animations:^{
                    scrollView.contentOffset = CGPointMake(0.0f, 0.0f);
                    scrollView.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f);
                } completion:NULL];
            }];

        }
    }
    else{
        //closing
        static CGFloat openCriticalY = 40.0f;//会执行打开的临界值
        if(point.y<-openCriticalY){
            //will open
            [UIView animateWithDuration:0 animations:NULL completion:^(BOOL finished) {
                [UIView animateWithDuration:0.25 animations:^{
                    scrollView.contentInset = UIEdgeInsetsMake(refreshViewHeight, 0.0f, 0.0f, 0.0f);
                    scrollView.contentOffset = CGPointMake(0.0f, -refreshViewHeight);
                } completion:NULL];
            }];
        }
    }
}

sie können dies versuchen, der Schlüssel verwendet zwei Animationen.

5
user3044484

Um den Sprung zu entfernen, wird Ryanthons Antwort den Trick ausführen. In meiner App mit iOS9.0 ist kein Animationsblock erforderlich, um den Sprung zu entfernen. Das Zurücksetzen von contentOffset nach dem Festlegen von contentInset führt dazu.

Wenn Sie die Scrollgeschwindigkeit von tableView beim Ausblenden der Aktualisierungsansicht steuern möchten, lösen doppelte Animationsblöcke in der Antwort von user3044484 die Zauberei aus. Ein Animationsblock reicht nicht aus. 

if (self.tableView.contentInset.top == 0)
{
    UIEdgeInsets tableViewInset = self.tableView.contentInset;
    tableViewInset.top = -1.*kTableHeaderHeight;
    [UIView animateWithDuration: 0 animations: ^(void){} completion:^(BOOL finished) {
        [UIView animateWithDuration: 0.5 animations:^{
        //no need to set contentOffset, setting contentInset will change contentOffset accordingly.
            self.tableView.contentInset = tableViewInset;  
        }];
    }];
}
3
Zack Zhu

Ich hatte das gleiche Problem und verschob meinen Animationsblock auf den ...

- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView

delegieren Sie stattdessen die Methode. Nicht perfekt, aber ich kann das Problem noch nicht genau bestimmen. Sieht aus wie scrollViews -layoutSubviews Methode wird unter iOS8 häufiger aufgerufen, was zu nervösen Animationen führt.

2
Siôn

Zwei Lösungen:

  1. deaktivieren Sie Bounce beim Starten und aktivieren Sie Bounce, wenn das Laden abgeschlossen ist
  2. nachdem Sie beim Laden des Inhalts den Inhalt eingestellt haben, setzen Sie den Inhalt mit einem konstanten Wert
2
Linkou Bian

Ich habe beschlossen durch: -

  • Deaktivieren Sie die Absprungfunktion, wenn ContentInset auf contentOffsetY aktualisiert wird.
  • Behalten Sie den Überblick über contentOffset und Update auf tableView ContentInset.
 var contentOffsetY:CGFloat = 100
    func tracScrollContent(location: UIScrollView) {
        if(location.contentOffset.y  == -contentOffsetY ) {
            tblView.isScrollEnabled = true
            tblView.bounces = false
            tblView.contentInset = UIEdgeInsets(top: contentOffsetY, left: 0, bottom: 0, right: 0)
        }else if(location.contentOffset.y >= 0 ){
            tblView.bounces = true
            tblView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
        }
    }

Github Demo Swift-3

1
Shrawan

animieren in - (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView funktioniert leider nicht für mich. Der Scrollview springt trotzdem ab.

Laut Bounds ändert sich automatisch in UIScrollView mit Content-Insets das scheint mir ein iOS8-Bug.

0
niggeulimann