2009-05-11 6 views
22

UIScrollView im Paging-Modus setzt voraus, dass die Seiten direkt nebeneinander ohne Lücke sind. Wenn Sie jedoch ein Foto in der Fotos App öffnen und durch Fotos wischen, können Sie sehen, dass zwischen den Seiten eine Lücke besteht. Ich will diese Lücken auch.Fotos app-like Lücke zwischen den Seiten in UIScrollView mit pagingEnabled

Ich suche nach vorhandenen Lösungen, wenn überhaupt, oder für einige bizarrere Ideen über die Implementierung der Seitenlücken neben dem, den ich unten erklärt habe. Oder vielleicht gibt es eine offensichtliche einfache Möglichkeit, die ich vermisse?

Um es klar zu stellen: Ich möchte, dass die Lücke nur beim Scrollen sichtbar ist, so dass ich den Seiteninhalt nicht einfach einfügen kann.


Mein Plan ist, den Inhalt der Seite von innen scrollViewDidScroll Rückruf zu versuchen, zu bewegen, dass so (vorausgesetzt, Sie auf der rechten Seite sind scrollen) zunächst die Zielseite leicht nach rechts von seiner Seitenbegrenzungen versetzt ist, und durch Wenn Sie auf der Zielseite ankommen, befindet es sich wieder an der richtigen Position und die Quellseite ist leicht nach links von den Grenzen versetzt. (Oder vielleicht, anstatt die Dinge kontinuierlich zu bewegen, bin ich besser dran, die Offsets zu verschieben, sagen wir genau auf halbem Weg zwischen den Seiten.)

Ich bin der Autor des ScrollingMadness article+example, auf den ich einige Leute hier hingewiesen habe. Ich habe programmatisches Zoomen implementiert und das In-Photo Zooming + Scrolling mit dem Inter-Photo Paging kombiniert. Ich weiß also, wie man mit UIScrollView spielt, und suche nach den fortgeschrittenen Sachen.

Bitte weisen Sie mich nicht auf TTScrollView. Ich habe bereits viele Leute darauf hingewiesen, aber ich denke, es fühlt sich zu weit vom nativen UIScrollView-Verhalten entfernt an und möchte es nicht in meinen Projekten verwenden.

+0

Toller Start w ih ScrollingMadness! Ich wünschte, das Paging würde funktionieren, während man ein Bild vergrößert hat. Es scheint, dass niemand in der Lage war, herauszufinden, wie man das ähnlich macht wie mit der Fotos App. Three20 ist in der Nähe, aber die Bildlaufleistung auf einem gezoomten Bild ist schlecht. Es ist wie Apple gerade ihre Fotos App gemacht, um alle verrückt zu machen! Es ist so, als hätten sie absichtlich bestimmte APIs weggelassen, um eine einfache Duplizierung und Verbesserung ihrer App zu verhindern. Danke für die Hilfe. – Jonah

+0

Jonah, mit den neuesten Versionen von iPhone OS (vielleicht sogar ab 3.0) UIScrollView unterstützt Verschachtelung, und es funktioniert fast genau wie Phone.app (der Unterschied ist jetzt wirklich gering, und dies ist Apples Standard, den jeder auch einhalten sollte, anstatt zu versuchen replizieren Sie die datierte Phone.app). Gehen Sie einfach weiter, verwenden Sie die äußere Bildlaufansicht für das Blättern und die innere Bildlaufansicht für das Zoomen/Scrollen, und es würde funktionieren. –

+0

Versuchen Sie dies zu implementieren Paging mit Lücke und auch mit Web-Bild herunterladen http://github.com/aliseymen/PagedScrollView –

Antwort

27

Beachten Sie, dass diese Antwort ist ziemlich alt. Das grundlegende Konzept funktioniert nach wie vor, aber Sie sollte nicht schwer Größen in iOS7 und 8. Auch Codierung Ansicht, wenn Sie diesen Rat ignorieren, sollten Sie nicht 480 oder 330 verwenden

Haben Sie versucht, den Rahmen der Herstellung UIScrollView etwas größer als der Bildschirm ist (unter der Annahme, dass Sie Ihre Bilder Vollbild angezeigt werden sollen und dann Subviews auf denselben leicht-larger-than-the-Bildschirm Grenzen zu arrangieren.

#define kViewFrameWidth 330; // i.e. more than 320 

CGRect scrollFrame; 
scrollFrame.origin.x = 0; 
scrollFrame.origin.y = 0; 
scrollFrame.size.width = kViewFrameWidth; 
scrollFrame.size.height = 480; 

UIScrollView* myScrollView = [[UIScrollView alloc] initWithFrame:scrollFrame]; 
myScrollView.bounces = YES; 
myScrollView.pagingEnabled = YES; 
myScrollView.backgroundColor = [UIColor redColor]; 

UIImage* leftImage = [UIImage imageNamed:@"ScrollTestImageL.png"]; 
UIImageView* leftView = [[UIImageView alloc] initWithImage:leftImage]; 
leftView.backgroundColor = [UIColor whiteColor]; 
leftView.frame = CGRectMake(0,0,320,480); 

UIImage* rightImage = [UIImage imageNamed:@"ScrollTestImageR.png"]; 
UIImageView* rightView = [[UIImageView alloc] initWithImage:rightImage]; 
rightView.backgroundColor = [UIColor blackColor]; 
rightView.frame = CGRectMake(kViewFrameWidth * 2,0,320,480); 

UIImage* centerImage = [UIImage imageNamed:@"ScrollTestImageC.png"]; 
UIImageView* centerView = [[UIImageView alloc] initWithImage:centerImage]; 
centerView.backgroundColor = [UIColor grayColor]; 
centerView.frame = CGRectMake(kViewFrameWidth,0,320,480); 

[myScrollView addSubview:leftView]; 
[myScrollView addSubview:rightView]; 
[myScrollView addSubview:centerView]; 

[myScrollView setContentSize:CGSizeMake(kViewFrameWidth * 3, 480)]; 
[myScrollView setContentOffset:CGPointMake(kViewFrameWidth, 0)]; 

[leftView release]; 
[rightView release]; 
[centerView release]; 

Entschuldigt, wenn dies nicht kompilieren, Ich habe es in einer Querformat-App getestet und es von Hand auf das Portrait zurückgeschnitten, aber ich bin mir sicher, dass Sie die Idee verstehen liegt auf dem Superview Clipping welches für eine Vollbildansicht immer der Fall sein wird.

+1

Hm. Danke vielmals. Das Lustige ist, dass ich dieselbe Sache in einem Kommentar zu einer anderen Antwort beschrieben habe: "Ich könnte die Bildlaufansicht breiter machen, um die Seite breiter zu machen, aber dann wären die Bildlaufleisten etwas außerhalb des Bildschirms, was hässlich ist" . Aber dank deines Posts habe ich ein zweites Mal darüber nachgedacht und festgestellt, dass ich im Paging-Modus keine Bildlaufleisten brauche. Dumm dumm ich. Nochmals vielen Dank, ich werde Ihnen das ScrollingMadness-Beispiel gutschreiben, wenn ich diesen Trick hinzufüge. –

+0

Danke Ich habe auch meine leicht falsche erste Antwort gelöscht. Froh, dass es funktioniert hat. –

+10

Nur eine kurze Notiz über Andreys Kommentar: Sie können die Scrollindikatoren mit der scrollIndicatorInsets-Eigenschaft von UIScrollView verschieben. –

0

Dies ist nur eine Ahnung, also entschuldigt, wenn völlig falsch, aber ist es möglich, dass die contentSize nur etwas breiter als die Bildschirmbreite eingestellt ist.

Die richtige Information wird dann innerhalb der Sicht auf die Bildschirmbreite gerendert und UIScrollView kümmert sich um den Rest?

+0

Vielen Dank für den Vorschlag. Das Problem ist, dass contentSize die Größe aller Seiten zusammen angibt; Es gibt keine Möglichkeit, die Größe einer einzelnen Seite zu steuern - es ist immer die Breite der Bildlaufansicht. (Wir sprechen hier von pagingEnabled-Modus!) Ich könnte die Bildlaufansicht breiter machen, um die Seite breiter zu machen, aber dann wären die Bildlaufleisten etwas außerhalb des Bildschirms, was hässlich ist. –

+0

Es stellt sich heraus, dass ich die richtige Lösung in einem Kommentar zu Ihrer Antwort beschrieben habe. Im Paging-Modus gibt es keine Bildlaufleisten, daher ist die Bildlaufansicht größer als der Bildschirm. Ich habe es erst jetzt erkannt, nachdem eine andere Person das vorgeschlagen hat, aber es war deine Antwort, die mich dazu inspirierte, über die richtige Idee überhaupt nachzudenken. Also, mach weiter Ahnungen, sie helfen! :) –

+0

Froh, von etwas Hilfe zu sein;) –

0

Vielleicht möchten Sie die contentInset-Eigenschaft von UIScrollView ausprobieren?

myScrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 10.0); 
+0

Danke für den Vorschlag; contentInset sieht in der Tat vielversprechend aus und war eines der ersten Dinge, die ich ausprobiert habe.Leider fügt es den ganzen Inhalt ein, nicht um jede Seite herum (und es spielt überhaupt nicht gut mit dem Paging-Modus). –

4

Der Weg, dies zu tun ist wie du sagtest, eine Kombination aus ein paar Dingen.

Wenn Sie eine Lücke von 20px zwischen Ihren Bildern möchten, müssen Sie:

Zuerst Ihre Scroll-Ansicht der Gesamtbreite von 20 Pixel erweitern und verschieben Sie sie durch 10px links.

Zweitens, wenn Sie die xLoc Ihrer Bilder legen, 20px für jedes Bild hinzufügen, so dass sie 20px auseinander liegen. Drittens, legen Sie die ursprüngliche xLoc Ihrer Bilder auf 10px statt 0px.

Viertens stellen Sie sicher, dass Sie die Inhaltsgröße Ihrer Bildlaufansicht so einstellen, dass für jedes Bild 20px hinzugefügt werden. Also, wenn Sie kNumImages Bilder haben und jeweils kScrollObjWidth, dann gehen Sie wie folgt aus:

[scrollView setContentSize:CGSizeMake((kNumImages * (kScrollObjWidth+20)), kScrollObjHeight)]; 

Es sollte danach die Arbeit!

+0

Paul, ich fürchte, Sie sind ein bisschen spät: das wurde bereits vorgeschlagen und funktioniert in der Tat, aber trotzdem danke. –

+0

Es gibt ein Problem damit, dass ich bemerkt habe. Wenn die Bildlaufansicht kleiner als die Gesamtgröße des Bildschirms ist, werden Sie beim Blättern feststellen, dass die Seiten außerhalb der angegebenen Breite liegen. Dies ist wiederum ein Problem, wenn Ihre Bildlaufansicht kleiner als die gesamte Bildschirmbreite ist. Wenn jemand irgendwelche Workarounds hat, wäre es eine große Hilfe, sie zu hören. –

+0

Eine Schnittmaske würde funktionieren, aber ich konnte keine finden. Es ist möglicherweise möglich, drawRect für die Bildlaufansicht zu überladen, um sie anzuweisen, nur innerhalb eines bestimmten Rechtecks ​​zu zeichnen. –

22

So habe ich nicht genug "rep", um einen Kommentar zu der obigen Antwort zu posten. Diese Antwort ist korrekt, aber es gibt ein großes Problem zu beachten:

Wenn Sie eine UIScrollView in einem ViewController, der Teil eines UINavigationController ist, verwenden, wird der Navigationscontroller die Größe des Rahmens Ihrer ScrollView.

Das heißt, Sie haben eine App, die einen UINavigationController verwendet, um zwischen verschiedenen Ansichten zu wechseln. Sie drücken einen ViewController, der über eine scrollView verfügt, und Sie erstellen diese scrollView in der View-Controller -init-Methode. Sie weisen ihm einen Rahmen von (0, 0, 340, 480) zu.

Nun gehen Sie zu Ihrer ViewController-ViewDidAppear-Methode, erhalten Sie den Rahmen der ScrollView, die Sie erstellt haben. Sie werden feststellen, dass die Breite auf 320 Pixel reduziert wurde. Daher funktioniert das Paging nicht korrekt. Sie werden erwarten, dass sich scrollView um 340 Pixel bewegt, aber stattdessen wird es um 320 verschoben.

UINavigationController ist ein wenig berüchtigt für das Durcheinander mit Unteransichten. Es bewegt sie und passt ihre Größe an die Navigationsleiste an. Kurz gesagt, es ist kein Teamplayer - besonders in diesem Fall. An anderen Stellen im Internet wird empfohlen, UINavigationController nicht zu verwenden, wenn Sie die Größe und Position Ihrer Ansichten präzise steuern möchten. Sie schlagen stattdessen vor, dass Sie basierend auf UINavigationBar eine eigene navigationController-Klasse erstellen.

Nun, das ist eine Tonne Arbeit. Glücklicherweise gibt es eine einfachere Lösung: Setzen Sie den Rahmen der scrollView in der ViewDidAppear-Methode von viewController. An diesem Punkt ist UINavigationController fertig mit dem Rahmen zu tun, so dass Sie es auf das zurücksetzen können, was es sein sollte und die scrollView wird sich richtig verhalten.

Dies ist relevant für OS 3.0. Ich habe 3.1 oder 2.2.1 nicht getestet. Ich habe auch einen Fehlerbericht mit Apple eingereicht, der vorschlägt, dass sie UINavigationController mit einem BOOL wie "-shouldAutoarrangeSubviews" modifizieren, so dass wir diese Klasse dazu bringen können, ihre schmutzigen Hände von Subviews zu behalten.

Bis das kommt, wird die obige Lösung Lücken in einem paginierten UIScrollView innerhalb eines UINavigationController geben.

+1

Brian, danke für deine Warnung. Persönlich denke ich, es ist eine gute Sache, dass UINavigationController meine Ansichten ändert, um z.B. Schnittstellendrehung oder In-Call-Statusleiste. Daher bevorzuge ich meinen UIScrollView als Kind einer generischen UIView, die mit UINavigationController verwaltet wird. Auf diese Weise habe ich immer noch die Kontrolle über die Positionierung, kann aber die automatische Größenanpassung nutzen. –

+1

Sie haben gerade 4 Stunden Frustration beendet und mich 8 weitere gerettet. Ich würde zweimal upvote, wenn ich könnte. Dies ist immer noch ein Problem in OS 4.0. – alexantd

+0

Ich habe bereits einige Stunden für diese Ausgabe verbracht, danke bryan das ist ein toller Kommentar, ich schätze das wirklich –

7

Apple hat die 2010 WWDC Session Videos an alle Mitglieder des iphone Entwicklerprogramms freigegeben. Eines der diskutierten Themen ist, wie sie die Fotos App erstellt haben !!! Sie bauen Schritt für Schritt eine sehr ähnliche App auf und haben den gesamten Code kostenlos zur Verfügung gestellt.

Es verwendet auch keine private API. Hier finden Sie einen Link zum Download des Beispielcodes. Sie werden sich wahrscheinlich einloggen müssen, um Zugang zu erhalten.

http://connect.apple.com/cgi-bin/WebObjects/MemberSite.woa/wa/getSoftware?code=y&source=x&bundleID=20645

Und hier ist ein Link zum iTunes WWDC Seite:

http://insideapple.apple.com/redir/cbx-cgi.do?v=2&la=en&lc=&a=kGSol9sgPHP%2BtlWtLp%2BEP%2FnxnZarjWJglPBZRHd3oDbACudP51JNGS8KlsFgxZto9X%2BTsnqSbeUSWX0doe%2Fzv%2FN5XV55%2FomsyfRgFBysOnIVggO%2Fn2p%2BiweDK%2F%2FmsIXj

+0

Yup, UIScrollViews in UIScrollViews, einfach, schön. – jbm

+0

FYI: Dies ist WWDC 2010 Sitzung 104 Entwerfen von Apps mit Scroll-Ansichten - nur für den Fall, dass die Links nicht mehr funktionieren. –

0

Ich dachte, ich würde die Lösung hier für die Nachwelt füge ich mit am Ende gehen. Für eine lange Zeit habe ich Bryan's Lösung verwendet, um den Rahmen in -viewDidAppear zu justieren, und das hat brillant funktioniert. Seit iOS Multitasking eingeführt hat, habe ich jedoch ein Problem festgestellt, bei dem der Bildlaufrahmen geändert wird, wenn die Anwendung vom Hintergrund aus fortgesetzt wird. In diesem Fall wurde -viewDidAppear nicht aufgerufen und ich konnte keine Delegate-Methode finden, die zur richtigen Zeit aufgerufen würde, um die Änderung rückgängig zu machen. Daher entschied ich mich, meine Bildlaufansicht in eine Unteransicht der Ansicht meines View Controllers zu verwandeln, und dies schien das Problem zu beheben. Dies hat auch den Vorteil, dass Sie -viewDidAppear nicht verwenden müssen, um den Rahmen zu ändern - Sie können es direkt nach dem Erstellen der Bildlaufansicht tun. Meine Frage here hat die Details, aber ich werde sie hier posten auch:

CGRect frame = CGRectMake(0, 0, 320, 460); 
scrollView = [[UIScrollView alloc] initWithFrame:frame]; 

// I do some things with frame here 

CGRect f = scrollView.frame; 
f.size.width += PADDING; // PADDING is defined as 20 elsewhere 
scrollView.frame = f; 

[self.view addSubview:scrollView]; 
0

mit UIScrollView des Rahmens durcheinander zu vermeiden, könnten Sie UIScrollView Unterklasse und layoutSubviews außer Kraft setzen eine auf jeder Seite Offset anzuwenden.

Die Idee basiert auf den folgenden Beobachtungen:

  1. Wenn zoomScale = 1, Offset ist Null, wenn es auf der linken/rechten Rand ist
  2. Wenn zoomScale == 1 ist der Versatz Null, wenn es an dem sichtbaren rect Zentrum

dann der folgende Code abgeleitet:

- (void) layoutSubviews 
{ 
    [super layoutSubviews]; 

    // Find a reference point to calculate the offset: 
    CGRect bounds = self.bounds; 
    CGFloat pageGap = 8.f; 
    CGSize pageSize = bounds.size; 
    CGFloat pageWidth = pageSize.width; 
    CGFloat halfPageWidth = pageWidth/2.f; 
    CGFloat scale = self.zoomScale; 
    CGRect visibleRect = CGRectMake(bounds.origin.x/scale, bounds.origin.y/scale, bounds.size.width/scale, bounds.size.height/scale); 
    CGFloat totalWidth = [self contentSize].width/scale; 
    CGFloat scrollWidth = totalWidth - visibleRect.size.width; 
    CGFloat scrollX = CGRectGetMidX(visibleRect) - visibleRect.size.width/2.f; 
    CGFloat scrollPercentage = scrollX/scrollWidth; 
    CGFloat referencePoint = (totalWidth - pageWidth) * scrollPercentage + halfPageWidth; 

    // (use your own way to get all visible pages, each page is assumed to be inside a common container) 
    NSArray * visiblePages = [self visiblePages]; 

    // Layout each visible page: 
    for (UIView * view in visiblePages) 
    { 
     NSInteger pageIndex = [self pageIndexForView:view]; // (use your own way to get the page index) 

     // make a gap between pages 
     CGFloat actualPageCenter = pageWidth * pageIndex + halfPageWidth; 
     CGFloat distanceFromRefPoint = actualPageCenter - referencePoint; 
     CGFloat numOfPageFromRefPoint = distanceFromRefPoint/pageWidth; 
     CGFloat offset = numOfPageFromRefPoint * pageGap; 
     CGFloat pageLeft = actualPageCenter - halfPageWidth + offset; 

     view.frame = CGRectMake(pageLeft, 0.f, pageSize.width, pageSize.height); 
    } 
}