Digamos que adicionei mais visualizações no UIStackView que podem ser exibidas, como posso fazer a UIStackView
rolagem?
Digamos que adicionei mais visualizações no UIStackView que podem ser exibidas, como posso fazer a UIStackView
rolagem?
Respostas:
Caso alguém esteja procurando uma solução sem código , criei um exemplo para fazer isso completamente no storyboard, usando o Layout automático.
Você pode obtê-lo no github .
Basicamente, para recriar o exemplo (para rolagem vertical):
UIScrollView
defina suas restrições.UIStackView
aoUIScrollView
Leading
, Trailing
, Top
e Bottom
deve ser igual para os deUIScrollView
Width
restrição igual entre UIStackView
e UIScrollView
.UIStackView
UIViews
aoUIStackView
Troque Width
pela Height
etapa 4 e defina Axis
= Horizontal
na etapa 5, para obter um UIStackView horizontal.
O Guia de layout automático da Apple inclui uma seção inteira sobre Trabalho com vistas de rolagem . Alguns trechos relevantes:
- Fixar as bordas superior, inferior, dianteira e posterior da visualização de conteúdo nas bordas correspondentes da visualização de rolagem. A visualização de conteúdo agora define a área de conteúdo da visualização de rolagem.
- (Opcional) Para desativar a rolagem horizontal, defina a largura da visualização de conteúdo igual à largura da visualização de rolagem. A visualização de conteúdo agora preenche a visualização de rolagem horizontalmente.
- (Opcional) Para desativar a rolagem vertical, defina a altura da exibição de conteúdo igual à altura da exibição de rolagem. A visualização de conteúdo agora preenche a visualização de rolagem horizontalmente.
Além disso:
Seu layout deve definir completamente o tamanho da exibição do conteúdo (exceto onde definido nas etapas 5 e 6). … Quando a exibição de conteúdo é mais alta que a exibição de rolagem, a exibição de rolagem ativa a rolagem vertical. Quando a exibição do conteúdo é maior que a exibição de rolagem, a exibição de rolagem ativa a rolagem horizontal.
Para resumir, a exibição de conteúdo da exibição de rolagem (nesse caso, uma exibição de pilha) deve ser fixada nas bordas e ter sua largura e / ou altura restritas. Isso significa que o conteúdo da visualização de pilha deve ser restrito (direta ou indiretamente) nas direções em que a rolagem é desejada, o que pode significar adicionar uma restrição de altura a cada visualização dentro de uma visualização de pilha de rolagem vertical, por exemplo. A seguir, é apresentado um exemplo de como permitir a rolagem vertical de uma exibição de rolagem que contém uma exibição de pilha:
// Pin the edges of the stack view to the edges of the scroll view that contains it
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
// Set the width of the stack view to the width of the scroll view for vertical scrolling
stackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
Need constraints for: Y position or height.
Esse erro desaparece se você definir uma restrição de largura e altura para a exibição da pilha, que desativa a rolagem vertical. Esse código ainda funciona para você?
As restrições na resposta mais votada aqui funcionaram para mim e colei uma imagem das restrições abaixo, criada no meu storyboard.
Eu atingi dois problemas, porém, de que outros deveriam estar cientes:
Depois de adicionar restrições semelhantes às da resposta aceita, recebia o erro vermelho de execução automática Need constraints for: X position or width
. Isso foi resolvido adicionando um UILabel como uma subvisualização da visualização da pilha.
Estou adicionando as subvisões programaticamente, então, originalmente, não tinha subvisões no storyboard. Para se livrar dos erros de execução automática, adicione uma subvisão ao storyboard e remova-a quando estiver carregando antes de adicionar suas subvisões e restrições reais.
Inicialmente, tentei adicionar UIButtons
ao UIStackView. Os botões e as visualizações seriam carregados, mas a visualização de rolagem não rolaria . Isso foi resolvido adicionandoUILabels
se à exibição de pilha em vez de botões. Usando as mesmas restrições, essa hierarquia de exibição com os UILabels rola, mas os UIButtons não.
Eu estou confuso com este problema, como os UIButtons não parecem ter uma IntrinsicContentSize (usado pelo Ver Stack). Se alguém souber por que os botões não funcionam, eu adoraria saber o porquê.
Aqui está minha hierarquia e restrições de exibição, para referência:
Como Eik diz, UIStackView
e UIScrollView
jogue bem juntos, veja aqui .
A chave é que ele UIStackView
lida com a variável altura / largura para diferentes conteúdos e, em UIScrollView
seguida, faz seu trabalho bem ao rolar / saltar esse conteúdo:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.contentSize = CGSize(width: stackView.frame.width, height: stackView.frame.height)
}
Eu estava olhando para fazer a mesma coisa e me deparei com este excelente post. Se você deseja fazer isso programaticamente usando a API âncora, este é o caminho a percorrer.
Para resumir, incorpore o seu UIStackView
no seu UIScrollView
e defina as restrições de âncora do UIStackView
para coincidir com as do UIScrollView
:
stackView.leadingAnchor.constraintEqualToAnchor(scrollView.leadingAnchor).active = true
stackView.trailingAnchor.constraintEqualToAnchor(scrollView.trailingAnchor).active = true
stackView.bottomAnchor.constraintEqualToAnchor(scrollView.bottomAnchor).active = true
stackView.topAnchor.constraintEqualToAnchor(scrollView.topAnchor).active = true
stackView.widthAnchor.constraintEqualToAnchor(scrollView.widthAnchor).active = true
Tenha uma cena em tela cheia em branco
Adicione uma visualização de rolagem. Arraste com a tecla Control pressionada da visualização de rolagem para a visualização de base , adicione esquerda, direita, superior e inferior, todos os zero.
Adicione uma exibição de pilha na exibição de rolagem. Arraste com a tecla Control pressionada da exibição da pilha para a exibição de rolagem , adicione esquerda, direita, superior e inferior, todos os zero.
Coloque duas ou três etiquetas dentro da visualização da pilha.
Para maior clareza, torne a cor de fundo do rótulo vermelha. Defina a altura da etiqueta como 100.
Agora defina a largura de cada UILabel:
Surpreendentemente, arraste com o controle UILabel
da exibição de rolagem, não da pilha e selecione larguras iguais .
Repetir:
É simples assim. Esse é o segredo.
Dica secreta - bug da Apple:
Não funcionará com apenas um item! Adicione alguns rótulos para fazer a demonstração funcionar.
Você Terminou.
Dica: você deve adicionar uma altura a cada novo item . Cada item em qualquer exibição de pilha de rolagem deve ter um tamanho intrínseco (como um rótulo) ou adicionar uma restrição de altura explícita.
A abordagem alternativa:
Acima: surpreendentemente, defina as larguras dos UILabels com a largura da exibição de rolagem (não da exibição de pilha).
Como alternativa ...
Então você tem duas opções:
ou
Para ser claro, execute um desses métodos, NÃO faça os dois.
Para rolagem horizontal. Primeiro, crie ae UIStackView
a UIScrollView
e adicione-os à sua exibição da seguinte maneira:
let scrollView = UIScrollView()
let stackView = UIStackView()
scrollView.addSubview(stackView)
view.addSubview(scrollView)
Lembre-se de definir o translatesAutoresizingMaskIntoConstraints
para false
no UIStackView
e o UIScrollView
:
stackView.translatesAutoresizingMaskIntoConstraints = false
scrollView.translatesAutoresizingMaskIntoConstraints = false
Para que tudo funcione, as âncoras à direita, à esquerda e à direita UIStackView
devem ser iguais às UIScrollView
âncoras:
stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
Mas a âncora de largura do UIStackView
mosto deve ser igual ou maior que a largura da UIScrollView
âncora:
stackView.widthAnchor.constraint(greaterThanOrEqualTo: scrollView.widthAnchor).isActive = true
Agora ancore seu UIScrollView
, por exemplo:
scrollView.heightAnchor.constraint(equalToConstant: 80).isActive = true
scrollView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor).isActive = true
scrollView.leadingAnchor.constraint(equalTo:view.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo:view.trailingAnchor).isActive = true
Em seguida, sugiro tentar as seguintes configurações para o UIStackView
alinhamento e distribuição:
topicStackView.axis = .horizontal
topicStackView.distribution = .equalCentering
topicStackView.alignment = .center
topicStackView.spacing = 10
Finalmente, você precisará usar o addArrangedSubview:
método para adicionar subviews ao seu UIStackView.
Um recurso adicional que você pode achar útil é que, como o conteúdo UIStackView
é mantido em um, UIScrollView
você agora tem acesso a inserções de texto para tornar as coisas um pouco mais bonitas.
let inset:CGFloat = 20
scrollView.contentInset.left = inset
scrollView.contentInset.right = inset
// remember if you're using insets then reduce the width of your stack view to match
stackView.widthAnchor.constraint(greaterThanOrEqualTo: topicScrollView.widthAnchor, constant: -inset*2).isActive = true
Basta adicionar isso a viewdidload
:
let insets = UIEdgeInsetsMake(20.0, 0.0, 0.0, 0.0)
scrollVIew.contentInset = insets
scrollVIew.scrollIndicatorInsets = insets
Você pode tentar o ScrollableStackView : https://github.com/gurhub/ScrollableStackView
É uma biblioteca compatível com Objective-C e Swift. Está disponível através do CocoaPods .
Código de exemplo (Swift)
import ScrollableStackView
var scrollable = ScrollableStackView(frame: view.frame)
view.addSubview(scrollable)
// add your views with
let rectangle = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 55))
rectangle.backgroundColor = UIColor.blue
scrollable.stackView.addArrangedSubview(rectangle)
// ...
Código de exemplo (Objective-C)
@import ScrollableStackView
ScrollableStackView *scrollable = [[ScrollableStackView alloc] initWithFrame:self.view.frame];
scrollable.stackView.distribution = UIStackViewDistributionFillProportionally;
scrollable.stackView.alignment = UIStackViewAlignmentCenter;
scrollable.stackView.axis = UILayoutConstraintAxisVertical;
[self.view addSubview:scrollable];
UIView *rectangle = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 55)];
[rectangle setBackgroundColor:[UIColor blueColor]];
// add your views with
[scrollable.stackView addArrangedSubview:rectangle];
// ...
Se você tiver uma restrição para centralizar a Vista de pilha verticalmente dentro da vista de rolagem, basta removê-la.
Exemplo para um stackview / scrollview vertical (usando o EasyPeasy
for autolayout):
let scrollView = UIScrollView()
self.view.addSubview(scrollView)
scrollView <- [
Edges(),
Width().like(self.view)
]
let stackView = UIStackView(arrangedSubviews: yourSubviews)
stackView.axis = .vertical
stackView.distribution = .fill
stackView.spacing = 10
scrollView.addSubview(stackView)
stackView <- [
Edges(),
Width().like(self.view)
]
Apenas certifique-se de que a altura de cada subvisão esteja definida!
Antes de tudo, projete sua visualização, de preferência em algo como Sketch ou tenha uma idéia do que você deseja como conteúdo rolável.
Depois disso, crie o controlador de exibição de forma livre (escolha no inspetor de atributos) e defina altura e largura de acordo com o tamanho do conteúdo intrínseco da sua exibição (a ser escolhido no inspetor de tamanho).
Depois disso, no controlador de exibição, coloque uma exibição de rolagem e isso é uma lógica, que eu descobri que funciona quase o tempo todo no iOS (pode ser necessário passar pela documentação dessa classe de exibição que pode ser obtida através do comando + clique em nessa aula ou no Google)
Se você estiver trabalhando com duas ou mais visualizações, inicie primeiro com uma visualização que foi introduzida anteriormente ou seja mais primitiva e, em seguida, vá para a visualização que foi introduzida posteriormente ou que é mais moderna. Então, aqui, desde que a exibição de rolagem foi introduzida primeiro, comece com a exibição de rolagem primeiro e depois vá para a exibição de pilha. Aqui, coloque as restrições da visualização de rolagem em zero em todas as direções em relação à sua super visualização. Coloque todas as suas visualizações nessa exibição de rolagem e, em seguida, coloque-as na exibição de pilha.
Ao trabalhar com a exibição de pilha
Primeiro, inicie com motivos de cima para baixo (abordagem de baixo para cima), ou seja, se você tiver rótulos, campos de texto e imagens em sua exibição, expanda essas exibições primeiro (dentro da exibição de rolagem) e depois coloque-as na exibição de pilha.
Depois disso, ajuste a propriedade da exibição de pilha. Se a visualização desejada ainda não for alcançada, use outra visualização da pilha.
Depois de fazer sua exibição, coloque uma restrição entre a exibição da pilha mãe e a exibição de rolagem, enquanto os filhos da restrição empilham a exibição com a exibição da pilha mãe. Esperançosamente, a essa altura, ele deve funcionar bem ou você pode receber um aviso do Xcode dando sugestões, leia o que diz e implemente-as. Espero que agora você tenha uma visão de trabalho de acordo com suas expectativas :).
Para a visualização de pilha aninhada ou única, a visualização de rolagem deve ser definida com uma largura fixa com a visualização raiz. A exibição da pilha principal que está dentro da exibição de rolagem deve definir a mesma largura. [Minha visualização de rolagem está abaixo de uma Visualização, ignore-a]
Configure uma restrição de largura igual entre o UIStackView e o UIScrollView.
Se alguém procurar horizontalmente scrollview
func createHorizontalStackViewsWithScroll() {
self.view.addSubview(stackScrollView)
stackScrollView.translatesAutoresizingMaskIntoConstraints = false
stackScrollView.heightAnchor.constraint(equalToConstant: 85).isActive = true
stackScrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
stackScrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
stackScrollView.bottomAnchor.constraint(equalTo: visualEffectViews.topAnchor).isActive = true
stackScrollView.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.topAnchor.constraint(equalTo: stackScrollView.topAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: stackScrollView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: stackScrollView.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: stackScrollView.bottomAnchor).isActive = true
stackView.heightAnchor.constraint(equalTo: stackScrollView.heightAnchor).isActive = true
stackView.distribution = .equalSpacing
stackView.spacing = 5
stackView.axis = .horizontal
stackView.alignment = .fill
for i in 0 ..< images.count {
let photoView = UIButton.init(frame: CGRect(x: 0, y: 0, width: 85, height: 85))
// set button image
photoView.translatesAutoresizingMaskIntoConstraints = false
photoView.heightAnchor.constraint(equalToConstant: photoView.frame.height).isActive = true
photoView.widthAnchor.constraint(equalToConstant: photoView.frame.width).isActive = true
stackView.addArrangedSubview(photoView)
}
stackView.setNeedsLayout()
}
Coloque uma exibição de rolagem em sua cena e dimensione-a para que ela preencha a cena. Em seguida, coloque uma vista de pilha dentro da vista de rolagem e coloque o botão adicionar item dentro da vista de pilha. Assim que tudo estiver no lugar, defina as seguintes restrições:
Scroll View.Leading = Superview.LeadingMargin
Scroll View.Trailing = Superview.TrailingMargin
Scroll View.Top = Superview.TopMargin
Bottom Layout Guide.Top = Scroll View.Bottom + 20.0
Stack View.Leading = Scroll View.Leading
Stack View.Trailing = Scroll View.Trailing
Stack View.Top = Scroll View.Top
Stack View.Bottom = Scroll View.Bottom
Stack View.Width = Scroll View.Width
código: Stack View.Width = Scroll View.Width
é a chave.
Adicionando uma nova perspectiva para o macOS Catalyst. Como os aplicativos macOS suportam o redimensionamento de janelas, é possível que você UIStackView
faça a transição de um status não rolável para um status rolável ou vice-versa. Há duas coisas sutis aqui:
UIStackView
foi projetado para caber em todas as áreas possíveis.UIScrollView
tentará redimensionar seus limites para dar conta da área recém-adquirida / perdida sob a barra de navegação (ou barra de ferramentas no caso de aplicativos macOS).Infelizmente, isso criará um loop infinito. Eu não estou extremamente familiarizado com isso UIScrollView
e adjustedContentInset
, mas do meu log no layoutSubviews
método, estou vendo o seguinte comportamento:
UIScrollView
tenta reduzir seus limites (já que não há necessidade da área abaixo da barra de ferramentas).UIStackView
segue.UIScrollView
está insatisfeito e decide restaurar os limites maiores. Isso me parece muito estranho, pois o que estou vendo no log é isso UIScrollView.bounds.height == UIStackView.bounds.height
.UIStackView
segue.Parece-me que duas etapas resolveriam o problema:
UIStackView.top
a UIScrollView.topMargin
.contentInsetAdjustmentBehavior
como .never
.Aqui estou preocupado com uma exibição verticalmente rolável com um crescimento vertical UIStackView
. Para um par horizontal, altere o código adequadamente.
Espero que ajude alguém no futuro. Não foi possível encontrar alguém mencionando isso na Internet e me custou muito tempo para descobrir o que aconteceu.