ios - tutorial - xcode auto layout scrollview




Como posso usar o Autolayout para definir restrições no meu UIScrollview? (12)

Passei dois dias experimentando as várias soluções para abordagens de Autolayout Misto e Puro para obter o que era uma configuração de rolagem trivial antes do autolayout, e agora é oficial - devo ser muito estúpido. Eu estou configurando isso principalmente no Storyboard (bem, é apenas o jeito que é).

Então aqui está meu pedido de ajuda.

Viewtree:

UIView
-UIView
-UIView
..-UIScrollview
...-UIButton
...-UIButton
...-UIButton

Os botões devem rolar horizontalmente (da esquerda para a direita e vice-versa). Alguém pode por favor deixe-me saber como definir as restrições para conseguir isso usando puro Autolayout ???

-

Eu tentei a abordagem mista, assim:

UIView
- UIView
- UIView
..-UIScrollview
...-UIView (contentview)
....-UIButton
....-UIButton
....-UIButton

... e definindo restrições de largura e altura fixas para o contentview e as configurações translatesAutoresizingMaskIntoConstraints conforme o TechNote da Apple. Os botões e scrollview são configurados usando restrições. Isto obtém a rolagem da rolagem (yay) mas, infelizmente, ela rola muito! Tanto quanto eu posso dizer, a largura de rolagem é de alguma forma duplicada do que eu definir o contentview em ???

Eu tentei a abordagem puramente autolayout, tanto com contentview quanto sem. Todas as visualizações são translatesAutoresizingMaskIntoConstraints=NO , exceto para self.view . Os botões têm restrições de largura / altura fixas e são fixados em todas as quatro arestas da vista de rolagem. Nada rola.

Então eu estou totalmente perplexo porque eu não consigo fazer isso funcionar corretamente. Qualquer ajuda é muito apreciada, e se você precisar de qualquer outra informação, por favor, pergunte!

Screenshot ATUALIZADO com as restrições solution - buttonZ:

EDIT @ Jamie Forrest Assim, a solução acaba por ser a restrição à direita no último botão. Em vez de 6441, o valor que eu havia definido era negativo, -6441. O mais complicado é que, ao definir o valor no storyboard, há duas opções na barra de ferramentas Pin:

O Valor do Canvas Atual é negativo (levando a nenhum deslocamento) e a opção abaixo é positiva (rolagem de ativação). Isso significa que eu não sou burro, mas pelo menos meio cego, eu acho. Embora, em minha defesa, não seja um pouco preocupante que o XCode não mostre um erro para a configuração "incorreta"?

EDITADO NOVAMENTE Agora, isso é engraçado ... alterar o valor à direita de -6441 (sem rolagem) para 6441 ativado. Mas meu velho amigo "muito conteúdo" estava de volta, levando a um tamanho de conteúdo duas vezes maior do que deveria ser! A solução para obter a rolagem de conteúdo correta foi definir a restrição à direita como ZERO! Isso não é óbvio quando se trabalha no Storyboard, mas olhando para o código do @Infinity James, é como deveria ser.


Exemplo Simples Auto-Contido

A julgar pelo alto número de votos sobre a questão e o baixo número de votos nas respostas, as pessoas não estão encontrando uma solução compreensível e rápida aqui. Deixe-me tentar adicionar um. Este projeto é um exemplo independente feito completamente no Construtor de Interface. Você deve ser capaz de trabalhar em 10 minutos ou menos. Então você pode aplicar os conceitos aprendidos ao seu próprio projeto.

A pergunta original pergunta sobre os botões de rolagem. Aqui eu uso apenas o UIView mas eles podem representar qualquer visão que você goste. Eu também escolhi a rolagem horizontal porque as capturas de tela do storyboard são mais compactas para esse formato. Os princípios são os mesmos para rolagem vertical, no entanto.

Conceitos chave

  • O UIScrollView deve usar apenas uma subvisualização. Este é um 'UIView' que serve como visualização de conteúdo para conter tudo o que você deseja rolar.
  • Torne a visualização de conteúdo e o pai da visualização de rolagem com alturas iguais para a rolagem horizontal. (Larguras iguais para rolagem vertical)
  • Certifique-se de que todo o conteúdo rolável tenha uma largura definida e seja fixado em todos os lados.

Comece um novo projeto

Pode ser apenas um aplicativo de visualização única.

Storyboard

Neste exemplo, faremos uma visualização de rolagem horizontal. Selecione o View Controller e escolha Freeform no Size Inspector. Faça a largura 1,000 e a altura 300 . Isso nos dá espaço no storyboard para adicionar conteúdo que irá rolar.

Adicionar uma vista de rolagem

Adicione um UIScrollView e fixe todos os quatro lados na visualização raiz do controlador de visualização.

Adicionar uma visualização de conteúdo

Adicione um UIView como uma subvisualização à vista de rolagem. Isso é fundamental. Não tente adicionar muitas subvisualizações à exibição de rolagem. Basta adicionar um único UIView . Esta será sua visualização de conteúdo para as outras visualizações que você deseja rolar. Fixar a exibição de conteúdo na visualização de rolagem nos quatro lados.

Alturas iguais

Agora, no comando Document Outline, Command , clique na visualização de conteúdo e na visualização pai da visualização de rolagem para selecioná-las. Em seguida, defina as alturas como iguais (arraste Control da Exibição de Conteúdo para a Visualização de Rolagem). Isso também é fundamental. Como estamos rolando horizontalmente, a visualização de conteúdo da visualização de rolagem não saberá o quão alto ela deve ser, a menos que seja configurada dessa maneira.

Nota:

  • Se estivéssemos fazendo o conteúdo rolar verticalmente, então definiríamos a largura da visão de conteúdo como igual à largura do pai da visão de rolagem.

Adicionar conteúdo

Adicione três UIView e forneça todas as restrições. Eu usei margens de 8 pontos para tudo.

Restrições:

  • Visualização verde: fixe as bordas superior, esquerda e inferior. Faça a largura 400.
  • Vista vermelha: fixe as margens superior, esquerda e inferior. Faça a largura 300.
  • Visualização roxa: fixe todas as quatro arestas das arestas. Faça a largura, qualquer que seja o espaço restante (268 neste caso).

Definir as restrições de largura também é essencial para que a visualização de rolagem saiba o quão ampla será sua visualização de conteúdo.

Acabado

Isso é tudo. Você pode executar seu projeto agora. Ele deve se comportar como a imagem de rolagem no topo desta resposta.

Para rolagem vertical, basta trocar todas as direções de largura e altura neste exemplo (testado e funcionando).

Um estudo mais aprofundado


É difícil ver os valores exatos e a configuração de suas restrições conforme você as colou aqui. Por isso, não tenho certeza de ver suas capturas de tela onde você errou.

Em vez de uma explicação do que está errado na sua configuração, criei um projeto de amostra básico com uma hierarquia de visualizações e configuração de restrição muito semelhantes àquelas que você descreve. A rolagem horizontal funciona como esperado no projeto de amostra, que usa a abordagem "Pure AutoLayout" descrita pela Apple na Nota Técnica .

Eu também tive muitos problemas originalmente com o Auto Layout para trabalhar com o UIScrollView . A chave para fazê-lo funcionar é garantir que todos os itens na visualização de rolagem, em conjunto, tenham restrições que eventualmente vinculem a todos os lados da visualização de rolagem e contribuam para que o sistema de AutoLayout possa determinar um contentSize para o visualização de rolagem que será maior que seu quadro. Parece que você estava tentando fazer isso em seu código, mas talvez você tivesse algumas restrições supérfluas que estavam tornando o contentSize muito pequeno.

Também digno de nota, como outros mencionaram, com AutoLayout e UIScrollview, você não define mais o contentSize explicitamente. O sistema AutoLayout calcula o contentSize com base em suas restrições.

Eu também achei este capítulo ebook muito útil para me fazer entender como tudo isso funciona. Espero que tudo isso ajude.


A solução a seguir funcionou para mim para scrollView com autolayout e sem contentSize :

  1. Arraste e solte um scrollView para visualizar o Controlador e aplique as restrições para cobrir o espaço desejado.
  2. Arraste e solte um UIView dentro do scrollView e faça com que ele cubra todo o espaço do scrollView e aplique restrições no espaço superior, esquerdo, direito e inferior do scrollView.
  3. Defina a altura (e largura, se a rolagem horizontal é necessária) da visão interna, conforme a necessidade de rolagem. Esta parte também pode ser feita a partir do código, se necessário.
  4. Crítico Depois de definir a altura para algum valor grande no ponto (3), volte ao ponto (2) e certifique-se de definir os valores top, left, right e bottom de volta para zero, já que o Xcode pode tê-los alterado quando você forçar mudou a altura em (3).

E você está feito. Agora, você pode adicionar qualquer número de controles nessa visualização e aplicar as restrições relevantes entre si (que não parecem funcionar sem essa visualização). Se você não quiser usar esta visão, então você terá que aplicar restrições para cada controle relacionado ao scrollView (não relacionado entre si).

A esmagadora dica ..............

Crítico Digamos, por clareza, que o UIScrollView tenha 1.000 de largura e 100 de altura. (Na verdade, normalmente, esses valores seriam dinâmicos, é claro, dependendo da largura do dispositivo, etc. Mas, por enquanto, digamos 1000 de largura e 100 de altura.) Digamos que você esteja fazendo um deslocamento horizontal. Então coloque um UIView dentro do UIScrollView. (Essa é a "exibição de conteúdo".) Defina todas as quatro restrições da visualização de conteúdo, superior, inferior, à esquerda, à direita, para a exibição de rolagem. Faça-os todos zero, mesmo que isso pareça errado. Defina a altura do conteúdo do UIView para 100 e esqueça isso. Agora: você deseja rolar horizontalmente, portanto, defina a largura da visualização de conteúdo como, por exemplo, 1225.

Observe que a largura da visualização de conteúdo agora é 225 maior que a largura da exibição de rolagem pai. Tudo bem: na verdade, você DEVE fazer isso. Observe que

... você não define a largura à direita para negativo 225 ...

você pensaria que você tem que "combinar" as larguras como faria normalmente . Mas se você fizer isso, não funcionará.

Você deve definir os números iniciais e finais como ZERO , nunca negativos (mesmo que a largura seja "maior")

Curiosamente, você pode realmente definir os números iniciais / finais para qualquer valor positivo (tente dizer "50") e isso lhe dá uma espécie de margem de rejeição. (Muitas vezes parece ótimo: tente.) Qualquer valor negativo em cada extremidade irá "quebrar silenciosamente".

Note que, irritantemente, muitas vezes Xcode (a partir de 7.3.1 de qualquer maneira),

será 'útil' definir esses valores para você com números negativos!

porque ele tenta contá-las automaticamente para você. Em caso afirmativo, quebrará silenciosamente. Defina todos os quatro valores para zero na primeira instância. E defina a largura da exibição de conteúdo muito mais ampla que o "1000" no exemplo.

Editado: Acabei usando UITableView em vez de UIScrollView para a maioria das minhas necessidades. Como o tableView me parece muito mais flexível e dinâmico.


Depois de algum tempo lidando com esse problema, finalmente encontrei uma solução. Estou trabalhando com storyboards de tamanho de classe universal (600x600). Eu criei um UIView (contentView) do tamanho do scrollView e criei as restrições para Top, Bottom, Leading e Trailing para o scrollView. Em seguida, recortei o tamanho manualmente do contentView para 600x600. O storyboard parou de tentar redimensionar tudo e eu pude trabalhar, mas a visão parecia horrível no dispositivo real ou no simulador. Eu fiz 2 tomadas de restrição deste tamanho recortado.

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *contentViewWidthConstraint; 
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *contentViewHeightConstraint;

Então, em viewDidLoad

CGSize viewSize = self.view.frame.size;
self.contentViewWidthConstraint.constant = viewSize.width;
self.contentViewHeightConstraint.constant = viewSize.height;

Funciona bem.


Eu enfrentei um problema semelhante. Eu estabeleci todos os constrangidos e sempre estava me perguntando por que ainda redimensiona algumas subvisualizações. Minha solução foi definir clipsToBounds como YES.


Eu sei que esta é uma solução leiga e não o que a Apple sugere no docu, mas funcionou para mim duas vezes, com conteúdo diferente e pode ser configurado muito rapidamente: No visualizador de storyboard, insira o UIView. No UIView, insira um Table View, Dynamic, 0 Prototype cells, Style Plain ou Grouped. Na vista de tabela, insira uma vista de deslocamento, na vista de deslocamento, introduza o conteúdo. Não há configurações no controlador de exibição personalizada.


Há tantas perguntas sobre como usar o AutoLayout com o UIScrollView, o ponto chave que ignoramos é que as exibições internas do UIScrollView fazem restrições contra o Content View, mas não contra o próprio UIScrollView. Consulte a Nota Técnica TN2154 , você pode encontrar:

A classe UIScrollView rola seu conteúdo alterando a origem de seus limites. Para fazer isso funcionar com Layout automático, as bordas superior, esquerda, inferior e direita dentro de uma visualização de rolagem agora significam as bordas de sua exibição de conteúdo.

A figura a seguir mostrará que:

Você pode achar que o espaço à direita é de 500 pontos, se a restrição for feita para o UIScrollView, a visualização será perdida e deverá atualizar seu quadro. No entanto, sem avisos e sem erros. Porque todas as restrições são contra a exibição de conteúdo.

O UIScrollView calculará o tamanho da visualização de conteúdo de acordo com as restrições das visualizações internas. (Por exemplo, o tamanho do conteúdo: width = 100 (espaço à esquerda) + 200 (largura da vista) + 500 (espaço à direita), altura = 131 (espaçamento superior) + 200 (altura) + 269 (espaçamento inferior)

Como adicionar restrições para visualizações no UIScrollView:

  1. Imaginando as posições de visualizações na exibição de conteúdo.
  2. Adicione o espaçamento superior, direito, inferior e esquerdo às bordas da visualização de conteúdo, além da largura e altura dessas visualizações.

E tudo está feito.

Uma maneira fácil de lidar com o AutoLayout com scrollview é adicionar uma visualização de contêiner contendo todas as subvisualizações na exibição de rolagem.

Conclusão: o ponto chave para entender o AutoLayout com o UIScrollView é que as exibições internas fazem restrições contra a visualização de conteúdo, mas não com o próprio UIScrollView.

código de exemplo anexado


Há uma peça nas notas técnicas que você pode ter olhado. Você pode definir implicitamente o tamanho do conteúdo de uma exibição de rolagem usando restrições fixadas nas bordas da exibição de rolagem.

Aqui está um exemplo simples. Crie um storyboard com uma visualização, que tenha uma visualização de rolagem. Defina as restrições de visualizações de rolagem para ajustá-las ao tamanho da visualização na qual você as colocou.

Dentro dessa vista de rolagem, adicione uma única vista. Defina explicitamente o tamanho dessa visualização usando restrições (e certifique-se de que o tamanho seja maior que a exibição de rolagem).

Agora, adicione mais quatro restrições a essa visão interna, bloqueando as quatro bordas da visão interna para sua exibição de rolagem pai. Essas quatro restrições farão com que o tamanho do conteúdo seja expandido para acomodar a visão interna.

Se você tiver várias exibições que deseja adicionar a uma exibição de rolagem, por exemplo, organizada horizontalmente, bloquearia o lado esquerdo da primeira subvisualização à esquerda da visualização de rolagem, bloquearia as subvisualizações uma na outra horizontalmente e a direita lado da última sub vista para o lado direito da vista de rolagem. Essas restrições forçariam o tamanho do conteúdo da exibição de rolagem a se expandir para acomodar todas as subvisualizações e suas restrições.


O contentSize é implicitamente definido aplicando as restrições dentro do UIScrollView.

Por exemplo, se você tem um UIScrollView dentro de um UIView, ele ficará assim (como eu tenho certeza que você está ciente):

    UIView *containerView               = [[UIView alloc] init];
    UIScrollView *scrollView            = [[UIScrollView alloc] init];
    [containerView addSubview:scrollView];
    containerView.translatesAutoresizingMaskIntoConstraints = NO;
    scrollView.translatesAutoresizingMaskIntoConstraints    = NO;
    NSDictionary *viewsDictionary       = NSDictionaryOfVariableBindings(containerView, scrollView);

    [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|"
                                                                          options:kNilOptions
                                                                          metrics:nil
                                                                            views:viewsDictionary]];
    [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|"
                                                                          options:kNilOptions
                                                                          metrics:nil

Isso irá definir o scrollView para preencher o tamanho do containerView (assim o containerView terá que ser de um determinado tamanho).

Você pode então ajustar o contentSize do UIScrollView definindo implicitamente que ele seja grande o suficiente para manter os botões assim:

    UIButton *buttonA                   = [[UIButton alloc] init];
    UIButton *buttonB                   = [[UIButton alloc] init];
    UIButton *buttonC                   = [[UIButton alloc] init];
    [scrollView addSubview:buttonA];
    [scrollView addSubview:buttonB];
    [scrollView addSubview:buttonC];
    buttonA.translatesAutoresizingMaskIntoConstraints       = NO;
    buttonB.translatesAutoresizingMaskIntoConstraints       = NO;
    buttonC.translatesAutoresizingMaskIntoConstraints       = NO;

    viewsDictionary                     = NSDictionaryOfVariableBindings(scrollView, buttonA, buttonB, buttonC);

    [scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[buttonA]-|"
                                                                       options:kNilOptions
                                                                       metrics:nil
                                                                         views:viewsDictionary]];
    [scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[buttonA]-[buttonB]-[buttonC]-|"
                                                                       options:NSLayoutFormatAlignAllBaseline
                                                                       metrics:nil
                                                                         views:viewsDictionary]];

Passei dias tentando encontrar uma solução de como usar a visualização AutoLayout de uma rolagem incorporada, para centralizar a rolagem na tela visível, que funciona em todas as dimensões de dispositivos / tela, bem como na rotação da tela.

Passei dias tentando fazer isso apenas com o Autolayout e cheguei perto, mas nunca perto o suficiente. Então, no final, eu tive que adicionar 3 linhas de código por tela, no viewDidLoad.

Veja a solução abaixo:

  1. Crie o scrollview e preencha com qualquer objeto que você quiser
  2. Ativar layout automático
  3. Em seguida, centralize o ScrollView vertical e horizontalmente
  4. Selecione a visualização e, em seguida, "Adicionar restrições ausentes" - isso, então, faz a sua coisa
  5. O resultado é que muitas restrições são geradas. Existem 2 novos criados para a exibição: 'Horiz space scrollview to View' e 'Vert space scrollview to view' ou vice-versa.
  6. Elimine a 'vista de rolagem do espaço Horiz para Ver' para que agora você tenha 3 restrições na Visualização. O 2 para inserir a visualização de rolagem na exibição e aquela para definir um espaço vertical entre a visualização de rolagem e a exibição
  7. Agora, vincule a restrição Vert ao seu código clicando e arrastando-a para o arquivo de cabeçalho e criando um IBOutlet NSLayoutConstraint (eu chamei meu constraintVertVtoSV)
  8. Agora vá para o arquivo .m e adicione essas linhas de código em viewDidLoad (toque com a quantidade de preenchimento para obter a centralização vertical correta)

    if (IPAD)

    {self.constraintVertVtoSV.constant = 150,0; }

  9. Agora, isso deve ser executado em todos os dispositivos e estar corretamente centralizado e continuar rolando corretamente.


Se a sua pergunta for "Como faço para colocar um monte de UITextFields em um UIScrollView de rolagem vertical, de modo que eles saiam do caminho do teclado quando tiverem foco", a melhor resposta é:

Não.

Use um UITableViewController com células estáticas em vez disso.

Você obtém esse comportamento de rolagem para fora do caminho de graça, E todos os insets de conteúdo funcionam apenas se o seu controlador de exibição for exibido dentro de um UINavigationController.


Se você gosta de mim, usa apenas conteúdo estático sem restrições dentro da subvisualização, como você pode fazer assim:

override func viewDidLayoutSubviews() {
    scrollView.contentSize = CGSizeMake(320, 800)
}






autolayout