ios - page - title center ionic




Centralize o conteúdo do UIScrollView quando menor (16)

A Apple lançou os vídeos da sessão da WWDC de 2010 para todos os membros do programa de desenvolvedores do iPhone. Um dos temas discutidos é como eles criaram o aplicativo de fotos !!! Eles criam um aplicativo passo a passo muito semelhante e disponibilizaram todo o código gratuitamente.

Ele não usa API privada também. Eu não posso colocar qualquer código aqui por causa do acordo de não divulgação, mas aqui está um link para o download do código de amostra. Você provavelmente precisará fazer o login para obter acesso.

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

E aqui está um link para a página WWDC do 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

Eu tenho um UIImageView dentro de um UIScrollView que eu uso para zoom e rolagem. Se a imagem / conteúdo da visualização de rolagem for maior que a visualização de rolagem, tudo funcionará bem. No entanto, quando a imagem fica menor que a vista de rolagem, ela fica no canto superior esquerdo da visualização de rolagem. Gostaria de mantê-lo centralizado, como o aplicativo Fotos.

Alguma idéia ou exemplo sobre como manter o conteúdo do UIScrollView centrado quando é menor?

Eu estou trabalhando com o iPhone 3.0.

O código a seguir quase funciona. A imagem retorna ao canto superior esquerdo se eu a apertar depois de atingir o nível mínimo de zoom.

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

A maneira como fiz isso é adicionar uma visão extra na hierarquia:

UIScrollView -> UIView -> UIImageView

Dê ao seu UIView o mesmo formato que o seu UIScrollView e centralize seu UIImageView .


Atualmente estou subclassificando o UIScrollView e substituindo setContentOffset: para ajustar o deslocamento com base no contentSize . Funciona tanto com pinch quanto com zoom programático.

@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

Além de ser curto e doce, esse código produz um zoom muito mais suave do que a solução @Erdemus. Você pode ver isso em ação na demonstração do RMGallery .


Esta é a minha solução para o problema que funciona muito bem para qualquer tipo de visão dentro de uma visão de rolagem.

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

Eu tenho uma solução muito simples! Tudo o que você precisa é atualizar o centro de sua subvisualização (imageview) enquanto amplia o ScrollViewDelegate. Se a imagem com zoom for menor que a vista de rolagem, ajuste subview.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);
}

Há uma abundância de soluções aqui, mas eu arriscaria colocar aqui as minhas. É bom por dois motivos: ele não atrapalha a experiência de zoom, como atualizar o quadro de visualização de imagem em andamento, e também respeita inserções de exibição de rolagem originais (digamos, definidas no xib ou storyboard para uma manipulação elegante de barras de ferramentas semi-transparentes etc) .

Primeiro, defina um pequeno ajudante:

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

Para reter inserções originais, campo definido:

UIEdgeInsets originalScrollViewInsets;

E em algum lugar no viewDidLoad preencha:

originalScrollViewInsets = self.scrollView.contentInset;

Para colocar o UIImageView no UIScrollView (supondo que o próprio UIImage esteja em 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: de UIScrollViewDelegate para essa visualização de rolagem:

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

Finalmente, centrando-se:

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

Eu não testei a mudança de orientação ainda (isto é, reação apropriada para redimensionar o próprio UIScrollView), mas consertar isso deve ser relativamente fácil.


Ok, esta solução está funcionando para mim. Eu tenho uma subclasse de UIScrollView com uma referência ao UIImageView que está sendo exibido. Sempre que o UIScrollView aumenta, a propriedade contentSize é ajustada. É no setter que eu dimensiono o UIImageView apropriadamente e também ajustei sua posição central.

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

Sempre que eu definir a imagem no UIScrollView eu redimensiono. O UIScrollView no scrollview também é uma classe customizada que eu criei.

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

Com esses dois métodos, posso ter uma visão que se comporta exatamente como o aplicativo de fotos.


Ok, eu tenho lutado contra isso nos últimos dois dias e tendo finalmente chegado a uma solução confiável (até agora ...) eu pensei que deveria compartilhá-la e salvar os outros de alguma dor. :) Se você encontrar um problema com esta solução, por favor, grite!

Eu basicamente passei pelo que todo mundo tem: pesquisando o , os fóruns de desenvolvedor da Apple, olhei para o código de three20, ScrollingMadness, ScrollTestSuite, etc. Eu tentei ampliar o frame UIImageView, brincando com o deslocamento e / ou inserções do UIScrollView do ViewController, etc, mas nada funcionou muito bem (como todo mundo descobriu também).

Depois de dormir, tentei alguns ângulos alternativos:

  1. Subclassificando o UIImageView para que ele altere seu próprio tamanho dinamicamente - isso não funcionou bem.
  2. Subclassificando o UIScrollView para que ele altere seu próprio contentOffset dinamicamente - este é o que parece ser um vencedor para mim.

Com esse método UIScrollView de subclasse, estou sobrescrevendo o mutador contentOffset, portanto, não está definindo {0,0} quando a imagem é menor do que a janela de visualização - em vez disso, está definindo o deslocamento para que a imagem seja mantida centralizada na viewport. Até agora, parece sempre funcionar. Eu verifiquei com imagens largas, altas, minúsculas e grandes e não tem o problema "funciona, mas belisque com zoom mínimo".

Eu enviei um projeto de exemplo para o github que usa essa solução, você pode encontrá-lo aqui: http://github.com/nyoron/NYOBetterZoom


Para uma solução mais adequada para exibições de rolagem que usam o autolayout, use inserções de conteúdo da visualização de rolagem em vez de atualizar os quadros das subvisualizações da visualização de rolagem.

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

Passei um dia lutando com esse problema e acabei implementando o scrollViewDidEndZooming: withView: atScale: da seguinte maneira:

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

Isso garante que, quando a imagem for menor que a tela, ainda haja espaço adequado ao redor para que você possa posicioná-la no local exato desejado.

(supondo que seu UIScrollView contenha um UIImageView para manter a imagem)

Essencialmente, o que isso faz é verificar se a largura / altura da visualização da imagem é menor do que a largura / altura da tela e, em caso afirmativo, criar uma inserção de metade da largura / altura da tela (você provavelmente aumentaria se quisesse que a imagem fosse sair dos limites da tela).

Observe que, como esse é um método UIScrollViewDelegate , não se esqueça de adicioná-lo à declaração do controlador de visualização, para evitar problemas de compilação.


Você pode observar a propriedade contentSize do UIScrollView (usando a observação de valor-chave ou similar) e ajustar automaticamente o contentInset sempre que o contentSize alterado para menos que o tamanho da exibição de rolagem.


Você verá que a solução postada pelo Erdemus funciona, mas… Existem alguns casos em que o método scrollViewDidZoom não é invocado e sua imagem fica presa no canto superior esquerdo. Uma solução simples é invocar explicitamente o método quando você inicialmente exibe uma imagem, assim:

[self scrollViewDidZoom: scrollView];

Em muitos casos, você pode estar invocando esse método duas vezes, mas essa é uma solução mais limpa do que algumas das outras respostas neste tópico.


Apenas desabilite a paginação, para que funcione bem:

scrollview.pagingEnabled = NO;

Aqui está a maneira atual de fazer esse trabalho. É melhor, mas ainda não é perfeito. Tente definir:

 myScrollView.bouncesZoom = YES; 

para corrigir o problema com a visualização não centralizada quando em 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];
}

Além disso, eu faço o seguinte para evitar que a imagem cole o lado depois de diminuir o zoom.

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

Embora a questão seja um pouco antiga, o problema ainda existe. Eu resolvi isso no Xcode 7 fazendo a restrição de espaço vertical do item mais alto (neste caso o topLabel) para o superViews (o scrollView) top an IBOutlete então recalculando sua constante toda vez que o conteúdo muda dependendo da altura das subviews do scrollView ( topLabele 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()
    }
}

Eu tive o mesmo problema. Aqui está como eu resolvi

Este código deve ser chamado como resultado de 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);




uiimageview