UIScrollView avec UIImageView centrée, comme l'application Photos
j'aimerais avoir une vue de défilement avec une vue de contenu d'image. L'image est en fait une carte qui est beaucoup plus grande que l'écran. La carte doit être initialement au centre de la vue de défilement, comme les photos dans L'application Photos lorsque vous tournez iPhone à l'orientation paysage.
Je n'ai pas réussi à avoir la carte au centre avec le zoom correct et le défilement en même temps. À condition que l'image de la carte commence à partir du haut de l'écran (en portrait l'orientation), le code ressemble à quelque chose comme:
- (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;
}
quand je déplace le cadre image au centre, l'image ne croît que du haut de son cadre vers le bas. J'ai essayé de jouer avec mapView transform, avec le changement dynamique du cadre d'imageView. Rien ne fonctionne pour moi jusqu'à présent.
11 réponses
ce code devrait fonctionner sur la plupart des versions d'iOS (et a été testé pour fonctionner sur la version 3.1 vers le haut).
il est basé sur le code Apple WWDC mentionné dans la réponse de Jonah.
ajouter ce qui suit à votre sous-classe de UIScrollView, et remplacer tileContainerView avec la vue contenant votre image ou des tuiles:
- (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;
}
voici ce que je considérerais, la solution telle qu'elle se comporte exactement comme l'application photo d'apple. J'avais été à l'aide de solutions:
-(void) scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
pour recentrer mais je n'aime pas cette solution, car après le zoom a été fait, il avait un de rebondir ensuite rapide "sauter" dans le centre, ce qui était très sexy. Il s'avère que si vous faites à peu près la même logique mais dans cette fonction de délégué:
-(void)scrollViewDidZoom:(UIScrollView *)pScrollView
il commence à la fois centré et quand vous zoomez sur lui reste centré:
-(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;
}
où 'imageView' est le UIImageView
vous êtes en utilisant.
Apple a publié les vidéos de la session 2010 de la WWDC à tous les membres du programme des développeurs iphone. L'un des thèmes abordés est comment ils ont créé l'application photos!!! Ils construisent une application très similaire étape par étape et ont rendu tout le code disponible gratuitement.
il n'utilise pas non plus d'api privée. Je ne peux pas mettre tout le code ici à cause de l'accord de non divulgation, Mais voici un lien vers le téléchargement de code échantillon. Vous aurez probablement besoin de connexion à gagner accès.
Et voici un lien vers l'iTunes de la WWDC page:
je soupçonne que vous avez besoin de mettre le UIScrollView
contentOffset
.
j'aimerais que ce soit aussi simple. J'ai fait quelques recherches sur le net et j'ai découvert que ce n'est pas seulement mon problème, mais que beaucoup de gens se débattent avec le même problème non seulement sur iPhone, mais aussi sur Apple bureau cacao. Voir les liens suivants:
http://www.iphonedevsdk.com/forum/iphone-sdk-development/5740-uiimageview-uiscrollview.html
La solution décrite est basée sur la propriété UIViewContentModeScaleAspectFit de l'image, mais malheureusement il ne fonctionne pas très bien .L'image est centrée et se développe correctement, mais la zone rebondissante semble être beaucoup plus grande que l'image.
ce type n'a pas eu la réponse non plus:
http://discussions.apple.com/thread.jspa?messageID=8322675
et enfin, le même problème sur le cacao de bureau D'Apple:
http://www.cocoadev.com/index.pl?CenteringInsideNSScrollView
je suppose que la solution fonctionne, mais il est basé sur le NSClipView, qui n'est pas sur iPhone...
Quelqu'un a une solution qui fonctionne sur iPhone?
Cela a fonctionné pour moi:
- (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);
}
UIScrollView.puis plus tard j'ai mis le contenuoffset sur celui capturé ici.
Note: cette méthode fonctionne en quelque sorte. si l'image est plus petite que imageView, elle défile partiellement de l'écran. Pas une grosse affaire, mais pas aussi beau que l'application photos.
tout d'abord, il est important de comprendre que nous avons affaire à 2 vues, l'imageview avec l'image dedans, et la scrollview avec l'imageview dedans. Donc, tout d'abord définir l'imageview à la taille de l'écran:
[myImageView setFrame:self.view.frame];
puis, centrez votre image dans le imageview:
myImageView.contentMode = UIViewContentModeCenter;
voici tout mon 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];
}
OK, je pense que j'ai trouvé une assez bonne solution à ce problème. Le truc est de réajuster constamment le cadre d'imageView. Je trouve que cela fonctionne beaucoup mieux que de constamment ajuster les contenutinsets ou contentOffSets. J'ai dû ajouter un peu de code supplémentaire pour accommoder les images de portrait et de paysage.
Voici le 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];
}
une façon élégante de centrer le contenu de UISCrollView
est-ce.
Ajouter un observateur à l' contentSize de votre UIScrollView
, donc cette méthode sera appelée à chaque fois que le contenu change...
[myScrollView addObserver:delegate
forKeyPath:@"contentSize"
options:(NSKeyValueObservingOptionNew)
context:NULL];
Maintenant sur votre observateur de la méthode:
- (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 ); }
vous devriez changer le
[pointer viewWithTag:100]
. Remplacer par votre l'affichage du contenuUIView
.- modifier
imageGallery
pointant vers votre fenêtre taille.
- modifier
ceci corrigera le centre du contenu chaque fois que sa taille change.
NOTE: la seule façon dont ce contenu ne fonctionne pas très bien est avec la fonctionnalité de zoom standard du UIScrollView
.
en Monotouch ça a marché pour moi.
this._scroll.ScrollRectToVisible(new RectangleF(_scroll.ContentSize.Width/2, _scroll.ContentSize.Height/2,1,1),false);
avec cette classe, vous devez lui passer l'imageView avec laquelle il travaille. J'ai été tenté de faire l'auto détecter, mais c'est plus rapide et vous voulez que tout le vitesse vous pouvez obtenir dans le layoutSubviews
méthode.
Note: comme tel, cela nécessite que L'AutoLayout ne soit pas activé pour le scrollView.
//
// 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
}
}