我想要一个带有图像内容视图的滚动视图。图像实际上是比屏幕大得多的地图。地图应该最初位于滚动视图的中心,就像将iPhone转为横向时照片应用中的照片一样。
我没有设法让中心的地图同时正确缩放和滚动。 如果地图图像从屏幕顶部开始(纵向),则代码如下所示:
- (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;
}
当我将图像帧移动到中心时,图像仅从其帧的顶部向下生长。我尝试使用mapView变换,动态更改imageView的帧。到目前为止,对我来说没有任何作用。
答案 0 :(得分:40)
此代码适用于大多数iOS版本(已经过测试,可以在3.1版本上运行)。
它基于Jonah的回答中提到的Apple WWDC代码。
将以下内容添加到UIScrollView的子类中,并将tileContainerView替换为包含图像或切片的视图:
- (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;
}
答案 1 :(得分:23)
以下是我要考虑的内容,其中的解决方案与Apple的照片应用程序完全相同。我一直在使用以下解决方案:
-(void) scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
重新定位,但我不喜欢这个解决方案,因为在缩放完成后,它会反弹然后快速'跳'到中心,这非常不性感。如果你几乎完全相同的逻辑,但在这个委托函数中,结果证明:
-(void)scrollViewDidZoom:(UIScrollView *)pScrollView
它都从中心开始,当你缩小它时保持居中:
-(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;
}
'imageView'是您正在使用的UIImageView
。
答案 2 :(得分:7)
Apple已向iphone开发者计划的所有成员发布了2010 WWDC会话视频。讨论的主题之一是他们如何创建照片应用程序!他们逐步构建一个非常相似的应用程序,并使所有代码免费提供。
它也不使用私人api。由于非公开协议,我不能在此处放置任何代码,但这里是示例代码下载的链接。您可能需要登录才能获得访问权限。
并且,这是iTunes WWDC页面的链接:
答案 3 :(得分:2)
我怀疑您需要设置UIScrollView
的{{1}}。
答案 4 :(得分:1)
我希望就这么简单。我在网上做了一些研究,发现这不仅仅是我的问题,但很多人不仅在iPhone上遇到同样的问题,而且在Apple的台式机Cocoa上也是如此。 请参阅以下链接:
http://www.iphonedevsdk.com/forum/iphone-sdk-development/5740-uiimageview-uiscrollview.html
所描述的解决方案基于图像的属性UIViewContentModeScaleAspectFit,但不幸的是它不能很好地工作。图像居中并且正常生长,但弹跳区域似乎比图片大得多。
这家伙也没有得到答案:
http://discussions.apple.com/thread.jspa?messageID=8322675
最后,Apple的桌面Cocoa也存在同样的问题:
http://www.cocoadev.com/index.pl?CenteringInsideNSScrollView
我认为解决方案有效,但它基于NSClipView,它不在iPhone上......
有人在iPhone上有解决方案吗?
答案 5 :(得分:1)
这对我有用:
- (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);
}
其中160是UIScrollView
的宽度/高度的一半。
然后我将contentoffset设置为此处捕获的内容。
答案 6 :(得分:0)
注意:此方法有点工作。如果图像小于imageView,它将部分滚动屏幕。没什么大不了的,但也不如照片应用程序那么好。
首先,重要的是要了解我们正在处理2个视图,其中包含图像的imageview,以及包含imageview的scrollview。 因此,首先将imageview设置为屏幕大小:
[myImageView setFrame:self.view.frame];
然后,将图像置于图像视图中心:
myImageView.contentMode = UIViewContentModeCenter;
这是我的整个代码:
- (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];
}
答案 7 :(得分:0)
好的,我想我找到了一个很好的解决方案。诀窍是不断重新调整imageView的框架。我发现这比不断调整contentInsets或contentOffSets要好得多。我不得不添加一些额外的代码来容纳纵向和横向图像。
以下是代码:
- (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];
}
答案 8 :(得分:0)
将UISCrollView
的内容集中在一起的一种优雅方式就是这个。
将一位观察者添加到UIScrollView
的 contentSize 中,因此每次内容更改时都会调用此方法...
[myScrollView addObserver:delegate
forKeyPath:@"contentSize"
options:(NSKeyValueObservingOptionNew)
context:NULL];
现在用你的观察者方法:
- (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 ); }
您应该更改[pointer
viewWithTag:100]
。替换你的
内容视图UIView
。
imageGallery
指向您的窗口大小。每当他的尺寸发生变化时,这将更正内容的中心。
注意:此内容效果不佳的唯一方法是使用UIScrollView
的标准缩放功能。
答案 9 :(得分:0)
Monotouch对我有用。
this._scroll.ScrollRectToVisible(new RectangleF(_scroll.ContentSize.Width/2, _scroll.ContentSize.Height/2,1,1),false);
答案 10 :(得分:0)
这是一个替代解决方案,类似于@ JosephH的答案,但是这个解决方案考虑了图像的实际尺寸。因此,当用户平移/缩放时,屏幕上的空格永远不会超过所需的空白。例如,在纵向屏幕上显示风景图像时,这是一个常见问题。当整个图像在屏幕上时(Aspect Fit),图像上方和下方将有空白区域。然后,当放大时,其他解决方案将空白视为图像的一部分,因为它位于imageView中。它们可以让你在屏幕上平移大部分图像,只留下可见的空白。这看起来对用户不利。
使用这个类,你需要传递它正在使用的imageView。我很想让它自动检测,但这更快,你想要layoutSubviews
方法可以获得的所有速度。
注意:按原样,这需要为scrollView启用AutoLayout。
//
// 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
}
}