UIScrollView с центрированным UIImageView, как приложение Фотографии - PullRequest
37 голосов
/ 12 марта 2009

Я хотел бы иметь представление прокрутки с представлением содержимого изображения. Изображение на самом деле карта, которая намного больше, чем экран. Сначала карта должна находиться в центре представления с прокруткой, как фотографии в приложении «Фотографии», когда вы переключаете iPhone в альбомную ориентацию.

alt text

Мне не удалось разместить карту в центре с правильным масштабированием и прокруткой одновременно. При условии, что изображение карты начинается сверху экрана (в портретной ориентации), код выглядит примерно так:

- (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. Пока у меня ничего не работает.

Ответы [ 11 ]

40 голосов
/ 13 августа 2010

Этот код должен работать на большинстве версий iOS (и был протестирован на работу с версией 3.1 и выше).

Он основан на коде 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;
}
23 голосов
/ 03 февраля 2010

Вот то, что я рассмотрю, решение, поскольку в нем ведет себя точно так же, как приложение 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, который вы используете.

7 голосов
/ 20 июня 2010

Apple выпустила видео сессий WWDC 2010 для всех участников программы для разработчиков iphone. Одна из обсуждаемых тем - как они создали приложение для фотографий !!! Они шаг за шагом создают очень похожее приложение и сделали весь код бесплатным.

Он также не использует приватный API. Я не могу поместить здесь какой-либо код из-за соглашения о неразглашении, но здесь есть ссылка на пример загрузки кода Вам, вероятно, понадобится войти, чтобы получить доступ.

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

И вот ссылка на страницу iTunes WWDC:

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

2 голосов
/ 12 марта 2009

Я подозреваю, что вам нужно установить UIScrollView contentOffset.

1 голос
/ 31 марта 2009

Это сработало для меня:

- (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.

Затем я установил смещение содержимого на то, что здесь записано.

1 голос
/ 13 марта 2009

Хотелось бы, чтобы все было так просто. Я провел некоторое исследование в сети и обнаружил, что это не только моя проблема, но многие люди борются с той же проблемой не только на iPhone, но и на какао на рабочем столе Apple. Смотрите следующие ссылки:

http://www.iphonedevsdk.com/forum/iphone-sdk-development/5740-uiimageview-uiscrollview.html
Описанное решение основано на свойстве UIViewContentModeScaleAspectFit изображения, но, к сожалению, оно работает не очень хорошо. Изображение отцентрировано и растет должным образом, но область отскока кажется намного больше, чем изображение.

Этот парень тоже не получил ответа:
http://discussions.apple.com/thread.jspa?messageID=8322675

И, наконец, та же проблема на рабочем столе Apple, Какао:
http://www.cocoadev.com/index.pl?CenteringInsideNSScrollView
Полагаю, решение работает, но оно основано на NSClipView, которого нет на iPhone ...

У кого-нибудь есть какое-нибудь решение, работающее на iPhone?

0 голосов
/ 05 мая 2016

Вот альтернативное решение, похожее на ответ @ JosephH, но оно учитывает фактические размеры изображения. Таким образом, когда пользователь выполняет панорамирование / масштабирование, у вас никогда не будет больше свободного места на экране, чем требуется. Это распространенная проблема, например, при отображении ландшафтного изображения на портретном экране. Там будет пробел выше и ниже изображения, когда все изображение на экране (Aspect Fit). Затем при увеличении другие решения рассматривают этот пробел как часть изображения, потому что он находится в imageView. Они позволят вам панорамировать большую часть изображения за пределы экрана, оставляя видимым только пробел. Это выглядит плохо для пользователя.

С этим классом вам нужно передать ему изображение, с которым он работает. Я испытал желание иметь автоматическое обнаружение, но это быстрее, и вам нужна вся скорость, которую вы можете получить с помощью метода layoutSubviews.

Примечание. Для этого требуется, чтобы автоматическая разметка не была включена для 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
    }
}
0 голосов
/ 08 августа 2012

В Monotouch, который работал для меня.

this._scroll.ScrollRectToVisible(new RectangleF(_scroll.ContentSize.Width/2, _scroll.ContentSize.Height/2,1,1),false);
0 голосов
/ 03 ноября 2009

Один элегантный способ центрировать содержимое UISCrollView - это.

Добавьте одного наблюдателя к contentSize вашего UIScrollView, поэтому этот метод будет вызываться при каждом изменении содержимого ...

[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.

0 голосов
/ 04 октября 2009

Хорошо, я думаю, что нашел довольно хорошее решение этой проблемы. Хитрость заключается в том, чтобы постоянно перенастраивать кадр 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];
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...