Центральное содержимое UIScrollView при меньшем - программирование

Центральное содержимое UIScrollView при меньшем

У меня есть UIImageView внутри UIScrollView который я использую для масштабирования и прокрутки. Если изображение/содержимое представления прокрутки больше, чем представление прокрутки, все работает нормально. Однако когда изображение становится меньше, чем представление с прокруткой, оно прикрепляется к верхнему левому углу представления с прокруткой. Я бы хотел, чтобы он был в центре, как приложение "Фотографии".

Какие-нибудь идеи или примеры о том, как держать содержимое UIScrollView центру, когда оно меньше?

Я работаю с iPhone 3.0.

Следующий код почти работает. Изображение вернется в верхний левый угол, если я ущипну его после достижения минимального уровня масштабирования.

- (void)loadView {
    [super loadView];

    // set up main scroll view
    imageScrollView = [[UIScrollView alloc] initWithFrame:[[self view] bounds]];
    [imageScrollView setBackgroundColor:[UIColor blackColor]];
    [imageScrollView setDelegate:self];
    [imageScrollView setBouncesZoom:YES];
    [[self view] addSubview:imageScrollView];

    UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"WeCanDoIt.png"]];
    [imageView setTag:ZOOM_VIEW_TAG];
    [imageScrollView setContentSize:[imageView frame].size];
    [imageScrollView addSubview:imageView];

    CGSize imageSize = imageView.image.size;
    [imageView release];

    CGSize maxSize = imageScrollView.frame.size;
    CGFloat widthRatio = maxSize.width / imageSize.width;
    CGFloat heightRatio = maxSize.height / imageSize.height;
    CGFloat initialZoom = (widthRatio > heightRatio) ? heightRatio : widthRatio;

    [imageScrollView setMinimumZoomScale:initialZoom];
    [imageScrollView setZoomScale:1];

    float topInset = (maxSize.height - imageSize.height) / 2.0;
    float sideInset = (maxSize.width - imageSize.width) / 2.0;
    if (topInset < 0.0) topInset = 0.0;
    if (sideInset < 0.0) sideInset = 0.0;
    [imageScrollView setContentInset:UIEdgeInsetsMake(topInset, sideInset, -topInset, -sideInset)];
}

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    return [imageScrollView viewWithTag:ZOOM_VIEW_TAG];
}

/************************************** NOTE **************************************/
/* The following delegate method works around a known bug in zoomToRect:animated: */
/* In the next release after 3.0 this workaround will no longer be necessary      */
/**********************************************************************************/
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
    [scrollView setZoomScale:scale+0.01 animated:NO];
    [scrollView setZoomScale:scale animated:NO];
    // END Bug workaround

    CGSize maxSize = imageScrollView.frame.size;
    CGSize viewSize = view.frame.size;
    float topInset = (maxSize.height - viewSize.height) / 2.0;
    float sideInset = (maxSize.width - viewSize.width) / 2.0;
    if (topInset < 0.0) topInset = 0.0;
    if (sideInset < 0.0) sideInset = 0.0;
    [imageScrollView setContentInset:UIEdgeInsetsMake(topInset, sideInset, -topInset, -sideInset)];
}
4b9b3361

Ответ 1

В настоящее время я подклассифицирую UIScrollView и переопределяя setContentOffset:, чтобы настроить смещение на основе contentSize. Он работает как с щепоткой, так и с программным масштабированием.

@implementation HPCenteringScrollView

- (void)setContentOffset:(CGPoint)contentOffset
{
    const CGSize contentSize = self.contentSize;
    const CGSize scrollViewSize = self.bounds.size;

    if (contentSize.width < scrollViewSize.width)
    {
        contentOffset.x = -(scrollViewSize.width - contentSize.width) / 2.0;
    }

    if (contentSize.height < scrollViewSize.height)
    {
        contentOffset.y = -(scrollViewSize.height - contentSize.height) / 2.0;
    }

    [super setContentOffset:contentOffset];
}

@end

Помимо короткого и сладкого, этот код обеспечивает гораздо более плавное масштабирование, чем решение @Erdemus. Вы можете увидеть его в действии в демонстрации RMGallery.

Ответ 2

У меня очень простое решение! Все, что вам нужно, - это обновить центр вашего подвью (просмотр изображения) при масштабировании ScrollViewDelegate. Если увеличенное изображение меньше прокрутки, настройте subview.center else center (0,0).

- (void)scrollViewDidZoom:(UIScrollView *)scrollView 
{
    UIView *subView = [scrollView.subviews objectAtIndex:0];

    CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5, 0.0);
    CGFloat offsetY = MAX((scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5, 0.0);

    subView.center = CGPointMake(scrollView.contentSize.width * 0.5 + offsetX, 
                                 scrollView.contentSize.height * 0.5 + offsetY);
}

Ответ 3

Ответ @EvelynCordner был лучшим в моем приложении. Гораздо меньше кода, чем другие варианты тоже.

Вот версия Swift, если кому-то это нужно:

func scrollViewDidZoom(_ scrollView: UIScrollView) {
    let offsetX = max((scrollView.bounds.width - scrollView.contentSize.width) * 0.5, 0)
    let offsetY = max((scrollView.bounds.height - scrollView.contentSize.height) * 0.5, 0)
    scrollView.contentInset = UIEdgeInsetsMake(offsetY, offsetX, 0, 0)
}

Ответ 4

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

Я в основном пережил все, что у всех: поиск StackOverflow, Форумы разработчиков Apple, рассмотрел код для трех20, ScrollingMadness, ScrollTestSuite и т.д. Я попытался расширить кадр UIImageView, играя с смещением UIScrollView и/или вставки из ViewController и т.д., но ничего не работало отлично (как выяснили все остальные).

После сна, я попробовал пару альтернативных углов:

  • Подклассификация UIImageView, так что динамически изменяет ее собственный размер динамически - это совсем не сработало.
  • Подклассификация UIScrollView, поэтому она динамически изменяет свой собственный контентОбновление - это тот, который кажется мне победителем.

С помощью этого подкласса метода UIScrollView я переопределяю мутатор contentOffset, поэтому он не устанавливает {0,0}, когда изображение масштабируется меньше, чем область просмотра, вместо этого оно устанавливает смещение таким образом, чтобы изображение сохранялось в центре в окне просмотра. Пока это работает. Я проверил его с широкими, высокими, крошечными и крупными изображениями и не имел "работы, но щепотку при минимальном разрыве".

Я загрузил пример проекта в github, который использует это решение, вы можете найти его здесь: http://github.com/nyoron/NYOBetterZoom

Ответ 5

Этот код должен работать в большинстве версий 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;
}

Ответ 6

Для решения, более подходящего для прокрутки, использующего автозапуск, используйте вставки содержимого прокрутки, а не обновление фреймов в виде просмотра прокрутки.

- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
    CGFloat offsetX = MAX((scrollView.bounds.size.width - scrollView.contentSize.width) * 0.5, 0.0);
    CGFloat offsetY = MAX((scrollView.bounds.size.height - scrollView.contentSize.height) * 0.5, 0.0);

    self.scrollView.contentInset = UIEdgeInsetsMake(offsetY, offsetX, 0.f, 0.f);
}

Ответ 7

Я провел день, борясь с этой проблемой, и закончил реализацию scrollViewDidEndZooming: withView: atScale: следующим образом:

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
    CGFloat screenWidth = [[UIScreen mainScreen] bounds].size.width;
    CGFloat screenHeight = [[UIScreen mainScreen] bounds].size.height;
    CGFloat viewWidth = view.frame.size.width;
    CGFloat viewHeight = view.frame.size.height;

    CGFloat x = 0;
    CGFloat y = 0;

    if(viewWidth < screenWidth) {
        x = screenWidth / 2;
    }
    if(viewHeight < screenHeight) {
        y = screenHeight / 2 ;
    }

    self.scrollView.contentInset = UIEdgeInsetsMake(y, x, y, x);
}

Это гарантирует, что, когда изображение меньше экрана, у него все еще есть достаточное пространство, чтобы вы могли расположить его в нужное место.

(предполагая, что ваш UIScrollView содержит UIImageView для хранения изображения)

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

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

Ответ 8

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

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

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

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

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

Ответ 9

Хорошо, это решение работает для меня. У меня есть подкласс UIScrollView со ссылкой на отображаемый UIImageView. Всякий раз, когда масштабируется UIScrollView, изменяется свойство contentSize. В настройщике я соответствующим образом масштабирую UIImageView, а также отрегулирую его центральное положение.

-(void) setContentSize:(CGSize) size{
CGSize lSelfSize = self.frame.size;
CGPoint mid;
if(self.zoomScale >= self.minimumZoomScale){
    CGSize lImageSize = cachedImageView.initialSize;
    float newHeight = lImageSize.height * self.zoomScale;

    if (newHeight < lSelfSize.height ) {
        newHeight = lSelfSize.height;
    }
    size.height = newHeight;

    float newWidth = lImageSize.width * self.zoomScale;
    if (newWidth < lSelfSize.width ) {
        newWidth = lSelfSize.width;
    }
    size.width = newWidth;
    mid = CGPointMake(size.width/2, size.height/2);

}
else {
    mid = CGPointMake(lSelfSize.width/2, lSelfSize.height/2);
}

cachedImageView.center = mid;
[super  setContentSize:size];
[self printLocations];
NSLog(@"zoom %f setting size %f x %f",self.zoomScale,size.width,size.height);
}

Evertime Я установил изображение на UIScrollView, я изменил его размер. UIScrollView в scrollview также является настраиваемым классом, который я создал.

-(void) resetSize{
    if (!scrollView){//scroll view is view containing imageview
        return;
    }

    CGSize lSize = scrollView.frame.size;

    CGSize lSelfSize = self.image.size; 
    float lWidth = lSize.width/lSelfSize.width;
    float lHeight = lSize.height/lSelfSize.height;

    // choose minimum scale so image width fits screen
    float factor  = (lWidth<lHeight)?lWidth:lHeight;

    initialSize.height = lSelfSize.height  * factor;
    initialSize.width = lSelfSize.width  * factor;

    [scrollView setContentSize:lSize];
    [scrollView setContentOffset:CGPointZero];
    scrollView.userInteractionEnabled = YES;
}

С помощью этих двух методов я могу иметь представление, которое ведет себя так же, как приложение для фотографий.

Ответ 10

Вы можете посмотреть свойство contentSize UIScrollView (используя наблюдение за ключом или подобное) и автоматически отрегулировать contentInset, когда изменения contentSize будут меньше, чем размер прокрутки.

Ответ 11

То, как я это сделал, - добавить дополнительный вид в иерархию:

UIScrollView -> UIView -> UIImageView

Придайте UIView те же пропорции, что и ваш UIScrollView, и центрируйте свой UIImageView в этом.

Ответ 12

Один из элегантных способов сосредоточиться на содержании 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.

Ответ 13

Вы обнаружите, что решение, размещенное Erdemus, действительно работает, но... Есть случаи, когда метод scrollViewDidZoom не запускается, и ваше изображение застревает в верхнем левом углу. Простое решение заключается в явном вызове метода при первоначальном отображении изображения, например:

[self scrollViewDidZoom: scrollView];

Во многих случаях вы можете использовать этот метод дважды, но это более чистое решение, чем некоторые другие ответы в этом разделе.

Ответ 14

Это мое решение этой проблемы, которая отлично работает для любого вида внутри прокрутки.

-(void)scrollViewDidZoom:(__unused UIScrollView *)scrollView 
    {
    CGFloat top;
    CGFloat left;
    CGFloat bottom;
    CGFloat right;

    if (_scrollView.contentSize.width < scrollView.bounds.size.width) {
        DDLogInfo(@"contentSize %@",NSStringFromCGSize(_scrollView.contentSize));

        CGFloat width = (_scrollView.bounds.size.width-_scrollView.contentSize.width)/2.0;

        left = width;
        right = width;


    }else {
        left = kInset;
        right = kInset;
    }

    if (_scrollView.contentSize.height < scrollView.bounds.size.height) {

        CGFloat height = (_scrollView.bounds.size.height-_scrollView.contentSize.height)/2.0;

        top = height;
        bottom = height;

    }else {
        top = kInset;
        right = kInset;
    }

    _scrollView.contentInset = UIEdgeInsetsMake(top, left, bottom, right);



  if ([self.tiledScrollViewDelegate respondsToSelector:@selector(tiledScrollViewDidZoom:)])
  {
        [self.tiledScrollViewDelegate tiledScrollViewDidZoom:self];
  }
}

Ответ 15

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

Сначала определите небольшой помощник:

CGSize CGSizeWithAspectFit(CGSize containerSize, CGSize contentSize) {
    CGFloat containerAspect = containerSize.width / containerSize.height,
            contentAspect = contentSize.width / contentSize.height;

    CGFloat scale = containerAspect > contentAspect
                    ? containerSize.height / contentSize.height
                    : containerSize.width / contentSize.width;

    return CGSizeMake(contentSize.width * scale, contentSize.height * scale);
}

Чтобы сохранить оригинальные вставки, заданное поле:

UIEdgeInsets originalScrollViewInsets;

И где-то в viewDidLoad залейте его:

originalScrollViewInsets = self.scrollView.contentInset;

Поместить UIImageView в UIScrollView (при условии, что сам UIImage находится в loadImage var):

CGSize containerSize = self.scrollView.bounds.size;
containerSize.height -= originalScrollViewInsets.top + originalScrollViewInsets.bottom;
containerSize.width -= originalScrollViewInsets.left + originalScrollViewInsets.right;

CGSize contentSize = CGSizeWithAspectFit(containerSize, loadedImage.size);

UIImageView *imageView = [[UIImageView alloc] initWithFrame:(CGRect) { CGPointZero, contentSize }];
imageView.autoresizingMask = UIViewAutoresizingNone;
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.image = loadedImage;

[self.scrollView addSubview:imageView];
self.scrollView.contentSize = contentSize;

[self centerImageViewInScrollView];

scrollViewDidZoom: из UIScrollViewDelegate для этого прокрутки:

- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
    if (scrollView == self.scrollView) {
        [self centerImageViewInScrollView];
    }
}

Наконец, центрируя себя:

- (void)centerImageViewInScrollView {
    CGFloat excessiveWidth = MAX(0.0, self.scrollView.bounds.size.width - self.scrollView.contentSize.width),
            excessiveHeight = MAX(0.0, self.scrollView.bounds.size.height - self.scrollView.contentSize.height),
            insetX = excessiveWidth / 2.0,
            insetY = excessiveHeight / 2.0;

    self.scrollView.contentInset = UIEdgeInsetsMake(
            MAX(insetY, originalScrollViewInsets.top),
            MAX(insetX, originalScrollViewInsets.left),
            MAX(insetY, originalScrollViewInsets.bottom),
            MAX(insetX, originalScrollViewInsets.right)
    );
}

Я еще не тестировал изменение ориентации (т.е. правильная реакция на изменение размера самого UIScrollView), но исправление для этого должно быть относительно простым.

Ответ 16

Apple Photo Scroller Пример делает именно то, что вы ищете. Поместите это в свой подкласс UIScrollView и измените _zoomView как ваш UIImageView.

-(void)layoutSubviews{
  [super layoutSubviews];
  // center the zoom view as it becomes smaller than the size of the screen
  CGSize boundsSize = self.bounds.size;
  CGRect frameToCenter = self.imageView.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;
  }
  self.imageView.frame = frameToCenter; 
}

Пример кода прокрутки Apple Photo

Ответ 17

Чтобы сделать поток анимации приятным, установите

self.scrollview.bouncesZoom = NO;

и использовать эту функцию (найти центр, используя метод этот ответ)

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {
    [UIView animateWithDuration:0.2 animations:^{
        float offsetX = MAX((scrollView.bounds.size.width-scrollView.contentSize.width)/2, 0);
        float offsetY = MAX((scrollView.bounds.size.height-scrollView.contentSize.height)/2, 0);
        self.imageCoverView.center = CGPointMake(scrollView.contentSize.width*0.5+offsetX, scrollView.contentSize.height*0.5+offsetY);
    }];
}

Это создает эффект подпрыгивания, но не предполагает каких-либо внезапных движений заранее.

Ответ 18

Только одобренный ответ в быстром, но без подкласса с использованием делегата

func centerScrollViewContents(scrollView: UIScrollView) {
    let contentSize = scrollView.contentSize
    let scrollViewSize = scrollView.frame.size;
    var contentOffset = scrollView.contentOffset;

    if (contentSize.width < scrollViewSize.width) {
        contentOffset.x = -(scrollViewSize.width - contentSize.width) / 2.0
    }

    if (contentSize.height < scrollViewSize.height) {
        contentOffset.y = -(scrollViewSize.height - contentSize.height) / 2.0
    }

    scrollView.setContentOffset(contentOffset, animated: false)
}

// UIScrollViewDelegate    
func scrollViewDidZoom(scrollView: UIScrollView) {
    centerScrollViewContents(scrollView)
}

Ответ 19

Если ваш внутренний imageView имеет начальную определенную ширину (например, 300), и вы просто хотите центрировать его ширину только при масштабировании, меньшем, чем его начальная ширина, это также может помочь вам.

 func scrollViewDidZoom(scrollView: UIScrollView){
    if imageView.frame.size.width < 300{
        imageView.center.x = self.view.frame.width/2
    }
  }

Ответ 20

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

Ответ 21

Вот так я и делаю эту работу. Это лучше, но все же не идеально. Попробуйте установить:

 myScrollView.bouncesZoom = YES; 

чтобы устранить проблему, если представление не центрируется, когда на minZoomScale.

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGSize screenSize = [[self view] bounds].size;//[[UIScreen mainScreen] bounds].size;//
CGSize photoSize = [yourImage size];
CGFloat topInset = (screenSize.height - photoSize.height * [myScrollView zoomScale]) / 2.0;
CGFloat sideInset = (screenSize.width - photoSize.width * [myScrollView zoomScale]) / 2.0;

if (topInset < 0.0)
{ topInset = 0.0; }
if (sideInset < 0.0)
{ sideInset = 0.0; } 
[myScrollView setContentInset:UIEdgeInsetsMake(topInset, sideInset, -topInset, -sideInset)];
ApplicationDelegate *appDelegate = (ApplicationDelegate *)[[UIApplication sharedApplication] delegate];

CGFloat scrollViewHeight; //Used later to calculate the height of the scrollView
if (appDelegate.navigationController.navigationBar.hidden == YES) //If the NavBar is Hidden, set scrollViewHeight to 480
{ scrollViewHeight = 480; }
if (appDelegate.navigationController.navigationBar.hidden == NO) //If the NavBar not Hidden, set scrollViewHeight to 360
{ scrollViewHeight = 368; }

imageView.frame = CGRectMake(0, 0, CGImageGetWidth(yourImage)* [myScrollView zoomScale], CGImageGetHeight(yourImage)* [myScrollView zoomScale]);

[imageView setContentMode:UIViewContentModeCenter];
}

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

- (void) scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
myScrollView.frame = CGRectMake(0, 0, 320, 420);
 //put the correct parameters for your scroll view width and height above
}

Ответ 22

Просто отключите разбиение на страницы, чтобы он работал нормально:

scrollview.pagingEnabled = NO;

Ответ 23

У меня была такая же проблема. Вот как я решил

Этот код должен вызываться как результат scrollView:DidScroll:

CGFloat imageHeight = self.imageView.frame.size.width * self.imageView.image.size.height / self.imageView.image.size.width;
BOOL imageSmallerThanContent = (imageHeight < self.scrollview.frame.size.height) ? YES : NO;
CGFloat topOffset = (self.imageView.frame.size.height - imageHeight) / 2;

// If image is not large enough setup content offset in a way that image is centered and not vertically scrollable
if (imageSmallerThanContent) {
     topOffset = topOffset - ((self.scrollview.frame.size.height - imageHeight)/2);
}

self.scrollview.contentInset = UIEdgeInsetsMake(topOffset * -1, 0, topOffset * -1, 0);

Ответ 24

Хотя вопрос еще немного, но проблема все еще существует. Я решил это в Xcode 7, установив ограничение вертикального пространства самого верхнего элемента (в данном случае topLabel) на superViews (scrollView) вверх a IBOutlet, а затем пересчитав его постоянный каждый раз, когда содержимое изменяется в зависимости от высоты подсмотра scrollView (topLabel и bottomLabel).

class MyViewController: UIViewController {

    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var topLabel: UILabel!
    @IBOutlet weak var bottomLabel: UILabel!
    @IBOutlet weak var toTopConstraint: NSLayoutConstraint!

    override func viewDidLayoutSubviews() {
        let heightOfScrollViewContents = (topLabel.frame.origin.y + topLabel.frame.size.height - bottomLabel.frame.origin.y)
        // In my case abs() delivers the perfect result, but you could also check if the heightOfScrollViewContents is greater than 0.
        toTopConstraint.constant = abs((scrollView.frame.height - heightOfScrollViewContents) / 2)
    }

    func refreshContents() {
        // Set the label text …

        self.view.layoutIfNeeded()
    }
}

Ответ 25

Я знаю, что некоторые ответы выше верны, но я просто хочу дать свой ответ с некоторыми пояснениями, комментарии помогут вам понять, почему мы так поступаем.

Когда я загружаю scrollView в первый раз, я пишу следующий код, чтобы сделать его центром.

    scrollView.maximumZoomScale = 8
    scrollView.minimumZoomScale = 1

    // set vContent frame
    vContent.frame = CGRect(x: 0,
                            y: 0  ,
                            width: vContentWidth,
                            height: vContentWidth)
    // set scrollView.contentSize
    scrollView.contentSize = vContent.frame.size

    //on the X direction, if contentSize.width > scrollView.bounds.with, move scrollView from 0 to offsetX to make it center(using 'scrollView.contentOffset')
    // if not, don't need to set offset, but we need to set contentInset to make it center.(using 'scrollView.contentInset')
    // so does the Y direction.
    let offsetX = max((scrollView.contentSize.width - scrollView.bounds.width) * 0.5, 0)
    let offsetY = max((scrollView.contentSize.height - scrollView.bounds.height) * 0.5, 0)
    scrollView.contentOffset = CGPoint(x: offsetX, y: offsetY)

    let topX = max((scrollView.bounds.width - scrollView.contentSize.width) * 0.5, 0)
    let topY = max((scrollView.bounds.height - scrollView.contentSize.height) * 0.5, 0)
    scrollView.contentInset = UIEdgeInsets(top: topY, left: topX, bottom: 0, right: 0)

Затем, когда я зажимаю vContent, я пишу следующий код, чтобы сделать его центром.

func scrollViewDidZoom(_ scrollView: UIScrollView) {
    //we just need to ensure that the content is in the center when the contentSize is less than scrollView.size.
    let topX = max((scrollView.bounds.width - scrollView.contentSize.width) * 0.5, 0)
    let topY = max((scrollView.bounds.height - scrollView.contentSize.height) * 0.5, 0)
    scrollView.contentInset = UIEdgeInsets(top: topY, left: topX, bottom: 0, right: 0)
}