2009-03-12 15 views
37

Ich möchte eine Bildlaufansicht mit einer Bildansicht haben. Das Bild ist eigentlich eine Karte, die viel größer ist als der Bildschirm. Die Karte sollte sich anfangs in der Mitte der Bildlaufansicht befinden, wie Fotos in der Fotos-App, wenn Sie das iPhone in Querformat drehen.UIScrollView mit zentrierten UIImageView, wie Fotos App

alt text

Ich schaffe es nicht mit der richtigen Zoomen und Scrollen in der gleichen Zeit die Karte in der Mitte zu haben. Vorausgesetzt, dass das Kartenbild beginnt vom oberen Rand des Bildschirms (im Hochformat), sieht der Code so etwas wie:

- (void)loadView { 
    mapView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"map.jpg"]]; 
    CGFloat mapHeight = MAP_HEIGHT * SCREEN_WIDTH/MAP_WIDTH; 
    mapView.frame = CGRectMake(0, 0, SCREEN_WIDTH, mapHeight); 
    scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)]; 
    scrollView.delegate = self; 
    scrollView.contentSize = mapView.frame.size; 
    scrollView.maximumZoomScale = MAP_WIDTH/SCREEN_WIDTH; 
    scrollView.minimumZoomScale = 1; 
    [scrollView addSubview:mapView]; 
    self.view = scrollView; 
} 

Wenn ich den Bildrahmen in die Mitte, das Bild wächst nur von der Spitze der sein Rahmen nach unten. Ich habe versucht, mit der mapView-Transformation herumzuspielen, mit dynamisch wechselndem Frame des imageView. Nichts funktioniert für mich bisher.

+0

Martin, sieht aus wie Shizam hat eine funktionierende Lösung für 3.2+ Betriebssysteme zur Verfügung gestellt. Vielleicht möchtest du seine Antwort als die beste auswählen. –

+0

Hier ist der beste Weg, um dieses Problem zu lösen: http://blog.proculo.de/archives/180-Paging-enabled-UIScrollView-With-Previews.html Es funktionierte sehr gut für mich. – ararog

+0

Das ist ein ganz anderes Problem als das, was hier beschrieben wird. – Jonah

Antwort

2

Ich vermute, dass Sie die UIScrollView 's contentOffset einstellen müssen.

1

Ich wünschte, es wäre so einfach. Ich recherchierte im Internet und fand heraus, dass es nicht nur mein Problem ist, sondern dass viele Leute mit dem gleichen Problem kämpfen, nicht nur auf dem iPhone, sondern auch auf Apples Desktop-Cocoa. Siehe folgende Links:

http://www.iphonedevsdk.com/forum/iphone-sdk-development/5740-uiimageview-uiscrollview.html
Die beschriebene Lösung basiert auf der Eigenschaft UIViewContentModeScaleAspectFit des Bildes, aber leider funktioniert es nicht sehr gut .Das Bild zentriert ist und wächst richtig, aber das Prellen Bereich scheint zu sein, viel größer als das Bild.

Dieser Kerl nicht die Antwort bekommen haben entweder:
http://discussions.apple.com/thread.jspa?messageID=8322675

Und schließlich das gleiche Problem auf Apples Desktop Cocoa:
http://www.cocoadev.com/index.pl?CenteringInsideNSScrollView
Ich nehme an, die Lösung funktioniert, aber es basiert auf der NSClipView, das ist nicht auf iPhone ...

Hat jemand eine Lösung auf dem iPhone arbeiten?

1

Das ist für mich gearbeitet:

- (void) scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale { 
CGFloat tempx = view.center.x-160; 
CGFloat tempy = view.center.y-160; 
myScrollViewOffset = CGPointMake(tempx,tempy); 

}

wo 160 die halbe Breite/Höhe Ihres UIScrollView.

Dann später setze ich den Contentoffset auf den hier erfassten.

+0

In welcher Methode setzen Sie das contentOffSet auf myScrollViewOffSet? –

0

Hinweis: Diese Methode funktioniert. Wenn das Bild kleiner als die Bildansicht ist, wird es teilweise vom Bildschirm abrollen. Keine große Sache, aber auch nicht so schön wie die Fotos App.

Zunächst ist es wichtig zu verstehen, dass es sich um zwei Ansichten handelt, die Bildansicht mit dem Bild darin und die Bildansicht mit der Bildansicht darin. Also, zuerst die Bildansicht auf die Größe des Bildschirms:

[myImageView setFrame:self.view.frame]; 

Dann Zentrieren Sie Ihr Bild in der Imageview:

myImageView.contentMode = UIViewContentModeCenter; 

Hier ist meine gesamte Code:

- (void)viewDidLoad { 
AppDelegate *appDelegate = (pAppDelegate *)[[UIApplication sharedApplication] delegate]; 
[super viewDidLoad]; 
NSString *Path = [[NSBundle mainBundle] bundlePath]; 
NSString *ImagePath = [Path stringByAppendingPathComponent:(@"data: %@", appDelegate.MainImageName)]; 
UIImage *tempImg = [[UIImage alloc] initWithContentsOfFile:ImagePath]; 
[imgView setImage:tempImg]; 

myScrollView = [[UIScrollView alloc] initWithFrame:[[self view] bounds]]; 
[myScrollView addSubview:myImageView]; 

//Set ScrollView Appearance 
[myScrollView setBackgroundColor:[UIColor blackColor]]; 
myScrollView.indicatorStyle = UIScrollViewIndicatorStyleWhite; 

//Set Scrolling Prefs 
myScrollView.bounces = YES; 
myScrollView.delegate = self; 
myScrollView.clipsToBounds = YES; // default is NO, we want to restrict drawing within our scrollview 
[myScrollView setCanCancelContentTouches:NO]; 
[myScrollView setScrollEnabled:YES]; 


//Set Zooming Prefs 
myScrollView.maximumZoomScale = 3.0; 
myScrollView.minimumZoomScale = CGImageGetWidth(tempImg.CGImage)/320; 
myScrollView.zoomScale = 1.01; //Added the .01 to enable scrolling immediately upon view load. 
myScrollView.bouncesZoom = YES; 


[myImageView setFrame:self.view.frame];//rect];// .frame.size.height = imageHeight; 
myImageView.contentMode = UIViewContentModeCenter; 
self.view = myScrollView; 
[tempImg release]; 
} 
0

Ok Ich denke, ich habe eine ziemlich gute Lösung für dieses Problem gefunden. Der Trick besteht darin, den Bildansichtsrahmen ständig neu zu justieren. Ich finde das funktioniert viel besser als ständig die contentInsets oder contentOffSets anzupassen. Ich musste ein wenig zusätzlichen Code hinzufügen, um sowohl Hoch- als auch Querformatbilder aufzunehmen.

Hier ist der Code:

- (void) scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale { 

CGSize screenSize = [[self view] bounds].size; 

if (myScrollView.zoomScale <= initialZoom +0.01) //This resolves a problem with the code not working correctly when zooming all the way out. 
{ 
    imageView.frame = [[self view] bounds]; 
    [myScrollView setZoomScale:myScrollView.zoomScale +0.01]; 
} 

if (myScrollView.zoomScale > initialZoom) 
{ 
    if (CGImageGetWidth(temporaryImage.CGImage) > CGImageGetHeight(temporaryImage.CGImage)) //If the image is wider than tall, do the following... 
    { 
      if (screenSize.height >= CGImageGetHeight(temporaryImage.CGImage) * [myScrollView zoomScale]) //If the height of the screen is greater than the zoomed height of the image do the following... 
      { 
        imageView.frame = CGRectMake(0, 0, 320*(myScrollView.zoomScale), 368); 
      } 
      if (screenSize.height < CGImageGetHeight(temporaryImage.CGImage) * [myScrollView zoomScale]) //If the height of the screen is less than the zoomed height of the image do the following... 
      { 
        imageView.frame = CGRectMake(0, 0, 320*(myScrollView.zoomScale), CGImageGetHeight(temporaryImage.CGImage) * [myScrollView zoomScale]); 
      } 
    } 
    if (CGImageGetWidth(temporaryImage.CGImage) < CGImageGetHeight(temporaryImage.CGImage)) //If the image is taller than wide, do the following... 
    { 
      CGFloat portraitHeight; 
      if (CGImageGetHeight(temporaryImage.CGImage) * [myScrollView zoomScale] < 368) 
      { portraitHeight = 368;} 
      else {portraitHeight = CGImageGetHeight(temporaryImage.CGImage) * [myScrollView zoomScale];} 

      if (screenSize.width >= CGImageGetWidth(temporaryImage.CGImage) * [myScrollView zoomScale]) //If the width of the screen is greater than the zoomed width of the image do the following... 
      { 
        imageView.frame = CGRectMake(0, 0, 320, portraitHeight); 
      } 
      if (screenSize.width < CGImageGetWidth (temporaryImage.CGImage) * [myScrollView zoomScale]) //If the width of the screen is less than the zoomed width of the image do the following... 
      { 
        imageView.frame = CGRectMake(0, 0, CGImageGetWidth(temporaryImage.CGImage) * [myScrollView zoomScale], portraitHeight); 
      } 
    } 
    [myScrollView setZoomScale:myScrollView.zoomScale -0.01]; 
} 
0

Eine elegante Möglichkeit, dies der Gehalt an UISCrollView ist zu zentrieren.

Fügen Sie einen Beobachter zum Content Ihrer UIScrollView, so wird diese Methode bei jedem Besuch der Inhaltsänderung aufgerufen werden ...

[myScrollView addObserver:delegate 
       forKeyPath:@"contentSize" 
        options:(NSKeyValueObservingOptionNew) 
        context:NULL]; 

Jetzt auf Ihrem Beobachter Methode:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 

    // Correct Object Class. 
    UIScrollView *pointer = object; 

    // Calculate Center. 
    CGFloat topCorrect = ([pointer bounds].size.height - [pointer viewWithTag:100].bounds.size.height * [pointer zoomScale])/2.0 ; 
      topCorrect = (topCorrect < 0.0 ? 0.0 : topCorrect); 

    topCorrect = topCorrect - ( pointer.frame.origin.y - imageGallery.frame.origin.y); 

    // Apply Correct Center. 
    pointer.center = CGPointMake(pointer.center.x, 
           pointer.center.y + topCorrect); } 
  • Sie sollten die [pointer viewWithTag:100] ändern. Ersetzen Sie durch Ihre Inhaltsansicht UIView.

    • Ändern Sie auch imageGallery zeigt auf Ihre Fenstergröße.

Dies wird das Zentrum des Inhalts jedes Mal seine Größe ändern korrigieren.

HINWEIS: Die einzige Möglichkeit, dass dieser Inhalt nicht sehr gut funktioniert, ist mit Standard-Zoom-Funktionalität der UIScrollView.

23

Hier ist, was ich in Betracht ziehen würde, die Lösung als in ihr verhält sich genau wie Apple's Foto App. Ich hatte Lösungen wurde unter Verwendung, die verwendet:

-(void) scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale 

rieren, aber ich habe nicht wie diese Lösung, da nach dem Zoomen fertig war, es prallen würde dann schnell ‚Sprung‘ in den Mittelpunkt, die sehr un-sexy waren . wenn Sie stellte sich heraus, ziemlich viel tun, die genau die gleiche Logik, aber in diesem Delegatfunktion:

-(void)scrollViewDidZoom:(UIScrollView *)pScrollView 

es beide beginnt zentriert und wenn Sie es verkleinern Aufenthalte zentriert:

-(void)scrollViewDidZoom:(UIScrollView *)pScrollView { 
CGRect innerFrame = imageView.frame; 
CGRect scrollerBounds = pScrollView.bounds; 

if ((innerFrame.size.width < scrollerBounds.size.width) || (innerFrame.size.height < scrollerBounds.size.height)) 
{ 
    CGFloat tempx = imageView.center.x - (scrollerBounds.size.width/2); 
    CGFloat tempy = imageView.center.y - (scrollerBounds.size.height/2); 
    CGPoint myScrollViewOffset = CGPointMake(tempx, tempy); 

    pScrollView.contentOffset = myScrollViewOffset; 

} 

UIEdgeInsets anEdgeInset = { 0, 0, 0, 0}; 
if (scrollerBounds.size.width > innerFrame.size.width) 
{ 
    anEdgeInset.left = (scrollerBounds.size.width - innerFrame.size.width)/2; 
    anEdgeInset.right = -anEdgeInset.left; // I don't know why this needs to be negative, but that's what works 
} 
if (scrollerBounds.size.height > innerFrame.size.height) 
{ 
    anEdgeInset.top = (scrollerBounds.size.height - innerFrame.size.height)/2; 
    anEdgeInset.bottom = -anEdgeInset.top; // I don't know why this needs to be negative, but that's what works 
} 
pScrollView.contentInset = anEdgeInset; 
} 

Wo Imageview 'ist die UIImageView, die Sie verwenden.

+2

Danke Mann! Das funktioniert wirklich (auf 3.2+, da scrollViewDidZoom nur 3.2 ist). Für diejenigen, die es ausprobieren werden: contentSize sollte anscheinend contentInset enthalten, also setze contentSize immer auf scrollView.bounds.size. Außerdem habe ich festgestellt, dass die Einstellung von contentOffset in scrollViewDidZoom nicht notwendig ist, und das Entfernen dieses Elements hilft tatsächlich, kleinere Sprünge zu vermeiden. Auch imageView.frame ist nach dem Zoomen undefiniert (obwohl es für den Moment funktioniert), sollte durch imageView.bounds.size * zoomScale ersetzt werden. Hier ist meine Sicht darauf: http://gist.github.com/384389 –

+0

Ja, ja, ja! Brillant. Der wirkliche Schlüssel hier ist, dass in 3.2, UIScrollView scheint schließlich contentInset für nicht-1.0 Zoom-Maßstäbe zu respektieren. –

+0

Dies ist die einzige echte Kopie und Paste-voll-funktionierende Antwort auf dieses Problem. Schön gemacht, Shizam! – thgc

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. Ich kann den Code wegen der Geheimhaltungsvereinbarung hier nicht angeben, aber hier ist ein 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

+1

Danke ... Ich brauchte eine Sekunde, um es zu finden, aber das ist, was ich brauchte. Es befindet sich im PhotoScroller-Beispiel. Sehen Sie sich die ImageScrollView-Klasse an. – respectTheCode

40

Dieser Code auf den meisten Versionen von iOS funktionieren sollte (und wurde auf 3.1 nach oben arbeiten getestet) .

Es basiert auf dem Apple WWDC-Code, der in Jonahs Antwort erwähnt wird.

die unten an der Unterklasse von UIScrollView hinzufügen und ersetzen tileContainerView mit der Ansicht Ihr Bild oder Fliesen enthalten:

- (void)layoutSubviews { 
    [super layoutSubviews]; 

    // center the image as it becomes smaller than the size of the screen 
    CGSize boundsSize = self.bounds.size; 
    CGRect frameToCenter = tileContainerView.frame; 

    // center horizontally 
    if (frameToCenter.size.width < boundsSize.width) 
     frameToCenter.origin.x = (boundsSize.width - frameToCenter.size.width)/2; 
    else 
     frameToCenter.origin.x = 0; 

    // center vertically 
    if (frameToCenter.size.height < boundsSize.height) 
     frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height)/2; 
    else 
     frameToCenter.origin.y = 0; 

    tileContainerView.frame = frameToCenter; 
} 
+7

Dies ist * bei weitem * die beste Antwort. Sauber, einfach und genau das schlägt Apple in der WWDC 2010 104 Session vor. Bitte abstimmen! –

+0

Warnung: Dieser Code ist NICHT perfekt - Äpfel Rendering auf iPad Layout Layout etwas langsamer als ihre Einsätze Handhabung, so ist diese Lösung spürbar weniger glatt als Shizam Antwort. Shizams Antwort hat jedoch einen kleinen Fehler, bei dem es plötzlich schnappt, wenn Sie an den Rändern zoomen. Getestet auf ipad2 mit ios5 – Adam

+1

Shizams Antwort funktionierte überhaupt nicht für mich. Das hat es getan. Obwohl manchmal sogar das auf einen Zoom nicht reagiert. Pinches scheinen immer zu funktionieren. – Alyoshak

0

In Monotouch, die für mich gearbeitet.

this._scroll.ScrollRectToVisible(new RectangleF(_scroll.ContentSize.Width/2, _scroll.ContentSize.Height/2,1,1),false); 
+0

Um die Qualität Ihrer Antwort zu verbessern, geben Sie bitte an, wie/warum Ihr Beitrag das Problem löst. –

0

Hier ist eine alternative Lösung, ähnlich wie @ JosephH Antwort, aber diese berücksichtigt die tatsächlichen Abmessungen des Bildes. Wenn der Benutzer schwenkt/zoomt, haben Sie auf dem Bildschirm nie mehr Leerzeichen als erforderlich. Dies ist ein häufiges Problem, wenn Sie beispielsweise ein Landschaftsbild auf einem Porträtbildschirm anzeigen. Es wird Whitespace über und unter dem Bild sein, wenn das gesamte Bild auf dem Bildschirm ist (Aspect Fit). Beim Zoomen betrachten die anderen Lösungen den Whitespace als Teil des Bildes, da er sich in der imageView befindet. Mit ihnen können Sie den Großteil des Bildes außerhalb des Bildschirms schwenken, wobei nur der weiße Bereich sichtbar bleibt. Das sieht für den Benutzer schlecht aus.

Mit dieser Klasse müssen Sie die ImageView übergeben, mit der sie arbeitet. Ich war versucht, es automatisch erkennen zu lassen, aber das ist schneller und Sie wollen die ganze Geschwindigkeit, die Sie in der layoutSubviews Methode bekommen können.

Hinweis: Dies erfordert, dass AutoLayout für scrollView nicht aktiviert ist.

// 
// CentringScrollView.swift 
// Cerebral Gardens 
// 
// Created by Dave Wood 
// Copyright © 2016 Cerebral Gardens Inc. All rights reserved. 
// 

import UIKit 

class CentringScrollView: UIScrollView { 

    var imageView: UIImageView? 

    override func layoutSubviews() { 
     super.layoutSubviews() 

     guard let superview = superview else { return } 
     guard let imageView = imageView else { return } 
     guard let image = imageView.image else { return } 

     var frameToCentre = imageView.frame 

     let imageWidth = image.size.width 
     let imageHeight = image.size.height 

     let widthRatio = superview.bounds.size.width/imageWidth 
     let heightRatio = superview.bounds.size.height/imageHeight 

     let minRatio = min(widthRatio, heightRatio, 1.0) 

     let effectiveImageWidth = minRatio * imageWidth * zoomScale 
     let effectiveImageHeight = minRatio * imageHeight * zoomScale 

     contentSize = CGSize(width: max(effectiveImageWidth, bounds.size.width), height: max(effectiveImageHeight, bounds.size.height)) 

     frameToCentre.origin.x = (contentSize.width - frameToCentre.size.width)/2 
     frameToCentre.origin.y = (contentSize.height - frameToCentre.size.height)/2 

     imageView.frame = frameToCentre 
    } 
}