Wyśrodkuj zawartość UIScrollView, gdy jest mniejsza

Mam UIImageView wewnątrz UIScrollView, którego używam do powiększania i przewijania. Jeśli obraz / zawartość widoku przewijania jest większa niż widok przewijania, wszystko działa poprawnie. Jednak gdy obraz stanie się mniejszy niż widok przewijania, zostanie przyklejony do lewego górnego rogu widoku przewijania. Chciałbym, aby było wyśrodkowane, jak aplikacja Zdjęcia.

Jakieś pomysły lub przykłady dotyczące utrzymywania treści UIScrollView w Centrum, gdy jest mniejsza?

Pracuję z iPhonem 3.0.

Poniższy kod prawie działa. Obraz wraca do lewego górnego rogu, jeśli go uszczypnę po osiągnięciu minimalnego poziomu powiększenia.

- (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)];
}
Author: Pang, 2009-08-22

24 answers

Mam bardzo proste rozwiązanie! Wystarczy zaktualizować środek subview (imageview)podczas powiększania widoku przewijania. Jeśli powiększony obraz jest mniejszy niż widok przewijania, dostosuj podgląd podrzędny.center else center is (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);
}
 222
Author: Erdemus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-08 12:55:00

@odpowiedź EvelynCordner była ta, która najlepiej działała w mojej aplikacji. Dużo mniej kodu niż inne opcje też.

Oto wersja Swift, jeśli ktoś jej potrzebuje:

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)
}
 33
Author: William T.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-18 18:59:06

Ok, walczę z tym przez ostatnie dwa dni i w końcu doszedłem do dość wiarygodnego (jak na razie...) rozwiązanie pomyślałem, że powinienem się nim podzielić i zaoszczędzić innym trochę bólu. :) Jeśli znajdziesz problem z tym rozwiązaniem proszę krzyczeć!

W zasadzie przejrzałem to, co wszyscy inni mają: przeszukanie StackOverflow, Forum programistów Apple, spojrzałem na kod dla three20, ScrollingMadness, ScrollTestSuite itp. Próbowałem powiększyć ramkę UIImageView, bawiąc się przesunięcie i / lub wstawki UIScrollView z kontrolera ViewController, itd. ale nic nie działało świetnie (jak wszyscy inni też się dowiedzieli).

Po spaniu na nim, próbowałem kilka alternatywnych kątów:

  1. Podklasyfikować UIImageView tak, aby dynamicznie zmieniać swój własny rozmiar - to nie działa dobrze w ogóle.
  2. Podklasowanie UIScrollView tak, aby dynamicznie zmieniał swój własny contentOffset - to jest ten, który wydaje się być dla mnie zwycięzcą.

Z tym podklasowanie metody UIScrollView nadpisuję mutator contentOffset, więc nie ustawia {0,0}, gdy obrazek jest skalowany mniej niż viewport - zamiast tego ustawia przesunięcie tak, aby obrazek był wyśrodkowany w viewport. Jak na razie to zawsze działa. Sprawdziłem to z szerokimi, wysokimi, małymi i dużymi obrazami i nie ma problemu z "działa, ale szczypta przy minimalnym powiększeniu łamie".

Wgrałem przykładowy projekt na github, który używa tego rozwiązania, można go znaleźć tutaj: http://github.com/nyoron/NYOBetterZoom

 25
Author: Liam Jones,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-14 14:28:48

Ten kod powinien działać na większości wersji iOS (i został przetestowany do pracy na 3.1 w górę).

Jest oparty na kodzie Apple WWDC dla photoscoller.

Dodaj poniżej do swojej podklasy UIScrollView i zamień tileContainerView na widok zawierający Twój obraz lub kafelki:

- (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
Author: JosephH,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-13 16:56:34

Obecnie podklasuję UIScrollView i nadpisuję setContentOffset:, aby dopasować offset na podstawie contentSize. Działa zarówno z pinch, jak i programowym powiększaniem.

@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

Oprócz tego, że jest krótki i słodki, kod ten generuje znacznie płynniejszy zoom niż rozwiązanie @ Erdemus. Możesz zobaczyć go w akcji w rmgallery demo.

 19
Author: hpique,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-21 22:36:05

Aby rozwiązanie lepiej pasowało do widoków przewijania, które korzystają z autolayout, używaj wstawek zawartości widoku przewijania zamiast aktualizowania ramek podwidywalnych widoku przewijania.

- (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);
}
 18
Author: Evelyn Cordner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-15 04:45:10

Spędziłem dzień walcząc z tym problemem, a skończyło się na zaimplementowaniu scrollViewDidEndZooming: withView: atScale: w następujący sposób:

- (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);
}

Zapewnia to, że gdy obraz jest mniejszy niż ekran, nadal jest wokół niego odpowiednia przestrzeń, dzięki czemu można go ustawić dokładnie w żądanym miejscu.

(zakładając, że uiscrollview zawiera interfejs UIImageView do przechowywania obrazu)

Zasadniczo sprawdza to, czy szerokość / wysokość widoku obrazu jest mniejszy niż szerokość / wysokość ekranu, a jeśli tak, Utwórz wstawkę o połowie szerokości / wysokości ekranu (prawdopodobnie możesz ją powiększyć, jeśli chcesz, aby obraz wyszedł poza granice ekranu).

Zauważ, że ponieważ jest to metoda UIScrollViewDelegate , nie zapomnij dodać jej do deklaracji kontrolera widoku, aby uniknąć problemów z kompilacją.

 12
Author: Vicarius,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-22 20:32:50

Apple udostępniło filmy z sesji WWDC 2010 wszystkim członkom programu iPhone developer. Jednym z poruszanych tematów jest to, jak stworzyli aplikację Zdjęcia!!! Budują bardzo podobną aplikację Krok po kroku i udostępnili cały kod ZA DARMO.

Nie używa również prywatnego api. Nie mogę umieścić żadnego kodu tutaj ze względu na umowę non disclosure, ale tutaj jest link do przykładowego kodu do pobrania. Prawdopodobnie będziesz musiał się zalogować, aby zyskać dostęp.

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

A oto link do strony 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

 7
Author: Jonah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-20 19:51:05

Ok, To rozwiązanie działa dla mnie. Mam podklasę UIScrollView z odniesieniem do UIImageView, którą wyświetla. Przy każdym powiększeniu UIScrollView właściwość contentSize jest dostosowywana. To właśnie w seterze odpowiednio skaluję UIImageView, a także dostosowuję jego pozycję środkową.

-(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);
}

Zawsze ustawiam obrazek na UIScrollView zmieniam jego rozmiar. UIScrollView w widoku przewijania jest również niestandardową klasą, którą stworzyłem.

-(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;
}

Tymi dwiema metodami jestem w stanie mieć pogląd, który zachowuje się właśnie jak aplikacja Zdjęcia.

 2
Author: AHA,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-11 07:02:16

Możesz obserwować Właściwość contentSize UIScrollView (używając obserwacji wartości klucza lub podobnej) i automatycznie dopasowywać contentInset za każdym razem, gdy contentSize zmienia się na mniejszy niż rozmiar widoku przewijania.

 1
Author: Tim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-22 17:17:59

Jednym z eleganckich sposobów na wyśrodkowanie zawartości UISCrollView jest to.

Dodaj jednego obserwatora do contentSize twojego UIScrollView, więc ta metoda będzie wywoływana za każdym razem, gdy zmieni się zawartość...

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

Teraz w metodzie obserwatora:

- (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 ); }
  • Powinieneś zmienić [pointer viewWithTag:100]. Zastąp swoją content view UIView.

    • Zmień również imageGallery wskazując rozmiar okna.

To poprawi środek treści za każdym razem jego rozmiar się zmienił.

Uwaga: jedynym sposobem, aby ta zawartość nie działała zbyt dobrze, jest standardowa funkcja zoomu UIScrollView.

 1
Author: SEQOY Development Team,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-03 17:09:11

To jest moje rozwiązanie tego problemu, które działa całkiem dobrze dla każdego rodzaju widoku wewnątrz widoku przewijania.

-(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];
  }
}
 1
Author: beat843796,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-07 00:42:57

Jest tu wiele rozwiązań, ale zaryzykowałbym postawienie tutaj własnych. Jest to dobre z dwóch powodów: nie psuje powiększania, podobnie jak aktualizowanie ramki widoku obrazu w toku, a także szanuje oryginalne wstawki widoku przewijania (powiedzmy, zdefiniowane w xib lub storyboard dla płynnej obsługi półprzezroczystych pasków narzędzi itp.).

Najpierw zdefiniuj mały pomocnik:

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);
}

Aby zachować oryginalne wstawki, zdefiniowane pole:

UIEdgeInsets originalScrollViewInsets;

I gdzieś w viewDidLoad fill it:

originalScrollViewInsets = self.scrollView.contentInset;

Aby umieścić UIImageView w UIScrollView (zakładając, że sam UIImage jest w loadedimage 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: from uiscrollviewdelegate for that scroll view:

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

Wreszcie, centrując się:

- (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)
    );
}

Nie testowałem jeszcze zmiany orientacji (tj. właściwej reakcji na zmianę rozmiaru samego UIScrollView), ale naprawienie tego powinno być stosunkowo łatwe.

 1
Author: jazzcat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-20 20:09:08

Przekonasz się, że rozwiązanie opublikowane przez Erdemusa działa, ale... są przypadki, w których metoda scrollViewDidZoom nie jest wywoływana i twój obraz jest przyklejony do lewego górnego rogu. Prostym rozwiązaniem jest jawne wywołanie metody, gdy początkowo wyświetlany jest obraz, w następujący sposób:

[self scrollViewDidZoom: scrollView];

W wielu przypadkach możesz powoływać się na tę metodę dwa razy, ale jest to czystsze rozwiązanie niż niektóre z innych odpowiedzi w tym temacie.

 1
Author: russes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-09 07:51:43

Przykład Apple Photo Scroller robi dokładnie to, czego szukasz. Umieść to w swojej podklasie UIScrollView i zmień _zoomView na 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; 
}

Przykładowy kod Apple Photo Scroller

 1
Author: Korey Hinton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-18 13:17:58

Sposób, w jaki to zrobiłem, polega na dodaniu dodatkowego widoku do hierarchii:

UIScrollView -> UIView -> UIImageView

Podaj swój UIView ten sam współczynnik proporcji, co twój UIScrollView, i wyśrodkuj swój UIImageView w to.

 1
Author: hatfinch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-19 12:58:20

Aby animacja przebiegała ładnie, ustaw

self.scrollview.bouncesZoom = NO;

I użyj tej funkcji (znalezienie środka za pomocą metody w ta ODPOWIEDŹ)

- (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);
    }];
}
Tworzy to efekt odbicia, ale nie wiąże się z nagłymi ruchami.
 1
Author: ldanilek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:38

Tylko zatwierdzona odpowiedź w języku swift, ale bez podklasowania przy użyciu delegata

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)
}
 1
Author: LightMan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-12 10:25:30

300) i chcesz wyśrodkować jego szerokość tylko {[3] } przy powiększeniu mniejszym niż jego początkowa szerokość, to może Ci również pomóc.

 func scrollViewDidZoom(scrollView: UIScrollView){
    if imageView.frame.size.width < 300{
        imageView.center.x = self.view.frame.width/2
    }
  }
 1
Author: dejix,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-26 14:06:34

Oto obecny sposób, w jaki to działa. Jest lepiej, ale nadal nie idealnie. Spróbuj ustawić:

 myScrollView.bouncesZoom = YES; 

Aby Rozwiązać problem z widokiem Nie centrującym się, gdy w 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];
}

Wykonuję również następujące czynności, aby zapobiec przyklejaniu się obrazu z boku po powiększeniu.

- (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
}
 0
Author: Jonah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-19 12:57:42

Ok, myślę, że znalazłem całkiem dobre rozwiązanie tego problemu. Sztuką jest ciągłe ustawianie imageView's Ramki. Uważam, że działa to znacznie lepiej niż ciągłe dostosowywanie contentInsets lub contentOffSets. Musiałem dodać trochę dodatkowego kodu, aby pomieścić zarówno obrazy pionowe, jak i krajobrazowe.

Oto kod:

- (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
Author: Jonah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-19 12:58:12

Po prostu wyłącz stronicowanie, żeby działało dobrze:

scrollview.pagingEnabled = NO;
 0
Author: Nagaraj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-19 13:04:21

Miałem dokładnie ten sam problem. Oto jak rozwiązałem

Ten kod powinien zostać wywołany jako wynik 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);
 0
Author: aryaxt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-03 03:17:38

Chociaż pytanie jest trochę stare, ale problem nadal istnieje. Rozwiązałem to w Xcode 7 , zmieniając pionowe ograniczenie przestrzeni najwyższego elementu (w tym przypadku topLabel) na superViews (the scrollView) top an IBOutlet, a następnie przeliczając jego stałą za każdym razem, gdy zawartość zmienia się w zależności od wysokości podviewów przewijania (topLabel i 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's text …

        self.view.layoutIfNeeded()
    }
}
 0
Author: Nick Podratz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-05 10:20:07