A área de tecnologia se expande cada vez mais, tornando-se um mercado bastante aquecido para…
Criando onboarding com UICollectionView e Storyboard
Sabe aquelas telas de boas-vindas que vemos quando abrimos um app pela primeira vez? Este processo é feito para familiarizar o usuário com o app e suas funções. É uma oportunidade para apresentar as principais funcionalidades ou diferenciais do app, novidades de uma versão, ou tentar convencer usuários a se registrarem no serviço.
Neste tutorial irei ensinar como criar um onboarding no iOS usando UICollectionView
e Storyboard. Os controllers das views serão escritos em Swift 4. Se quiser baixar o projeto completo, siga este link para o GitHub.
Storyboard
Acho legal usar o storyboard pra desenhar as views porque economiza muito tempo e linhas de código, fora a vantagem de poder ver todo o fluxo de telas da aplicação de uma vez!
Por isso, vamos criar um novo projeto, abrir o storyboard, remover a View Controller padrão e colocar em seu lugar uma Collection View Controller. Com a ajuda do Document Outline, selecione a Collection View que está dentro da Collection View Controller. Queremos alterar algumas propriedades para que ela se comporte como um onboarding com scroll horizontal:
Além disso, vamos desmarcar as propriedades Show Horizontal Indicator e Show Vertical Indicator para que as barras de rolagem não apareçam. Desmarque também Bounce On Scroll e Bounce On Zoom, para que não ocorra o efeito de bounce ao fazer o scroll. E também marque a opção Paging Enabled, para que o scroll não pare entre uma página e outra.
Por fim, ainda no painel Utilities, na aba Size inspector, vamos alterar as propriedades Min Spacing For Cells e Min Spacing For Lines para 0. Isto fará qua não haja espaço entre as células, para que cada uma ocupe de fato a tela inteira (este tamanho vamos definir em código mais adiante).
A propriedade Items da Collection View controla quantas células ela possui. Neste onboarding faremos com que a célula ocupe a tela inteira. Assim, Items dirá quantas páginas vamos ter no onboarding.
Vamos agora desenhar o protótipo da célula da Collection View. Todas as células seguirão este protótipo, mudaremos apenas as informações de cada uma. O pequeno quadrado no canto superior esquerdo da Collection View (?) é o protótipo da célula. Dentro dele ficarão os componentes visuais que compõem o protótipo. Para conseguirmos posicioná-los, aumente o tamanho da célula (não se preocupe com o tamanho real, pois definiremos isso programaticamente para preencher a tela inteira).
O layout será uma Image View no topo, com dois Labels em baixo e um Button. Para diminuir o número de Constraints, empilharemos tudo em uma Stack View vertical:
Vamos configurar a Stack View para ter Distribution: Equal Centering e Spacing: Standard. Depois, centralizá-la e definir a largura de 260px usando constraints.
Para os Labels dentro da Stack View, defina a propriedade Lines: 0 para que textos maiores quebrem a linha automaticamente. O primeiro label será o título, então usaremos a fonte com Style: Bold e Size: 24px.
Para a Image View, vamos adicionar uma constraint de altura fixa 120px. Além disso, o Content Mode será Aspect Fit.
E o Button terá a constraint de altura e largura fixos. A largura será >= 260px, e a altura será 40px. Assim ele ocupa a largura inteira da Stack View.
Código
Vamos agora escrever as classes por trás das views que desenhamos no storyboard. Precisaremos das seguintes:
OnboardingCollectionViewController
: responsável pela Collection View – data source e delegate.OnboardingCollectionViewCell
: responsável por preencher os dados de cada célula da Collection View.OnboardingModel
: struct que define a estrutura dos dados apresentados no onboarding.OnboardingModelFactory
: para facilitar a instanciação dos modelos que serão usados no onboarding.
Seguem abaixo:
import UIKit | |
class OnboardingCollectionViewController: UICollectionViewController { | |
} | |
extension OnboardingCollectionViewController: UICollectionViewDelegateFlowLayout { | |
} |
import UIKit | |
class OnboardingCollectionViewCell: UICollectionViewCell { | |
static let reuseIdentifier = “OnboardingCell“ | |
} |
import UIKit | |
struct OnboardingModel { | |
let title: String | |
let content: String | |
let iconImage: String | |
let backgroundColor: UIColor | |
let hideButton: Bool | |
} |
import UIKit | |
class OnboardingModelFactory { | |
static func getPages() -> [OnboardingModel] { | |
return [ | |
OnboardingModel(title: “Feature 1“, | |
content: “Esta feature faz com que o aplicativo faça coisas incríveis!“, | |
iconImage: “iconOnboarding1“, | |
backgroundColor: Color.lightPurple, | |
hideButton: true), | |
OnboardingModel(title: “Feature 2“, | |
content: “Com esta feature, o aplicativo se diferencia dos concorrentes, pois faz X, Y e Z.“, | |
iconImage: “iconOnboarding2“, | |
backgroundColor: Color.lightGreen, | |
hideButton: true), | |
OnboardingModel(title: “Feature 3“, | |
content: “Concluindo o porque este aplicativo é demais, e convidando o usuário a começar.“, | |
iconImage: “iconOnboarding3“, | |
backgroundColor: Color.lightOrange, | |
hideButton: false) | |
] | |
} | |
} |
UICollectionViewController
Depois de criar os arquivos, volte para o storyboard e defina as classes da Collection View Controller e da Collection View Cell. Além disso, defina o Reuse Identifier da célula como “OnboardingCell” (painel Utilities, na aba Attributes inspector).
Vamos agora implementar os métodos da classe OnboardingCollectionViewController
. Nela definiremos a quantidade de páginas do onboarding, as informações de cada página, o tamanho (tela inteira), e também colocaremos um UIPageControl
para indicar ao usuário qual página ele está atualmente.
import UIKit | |
class OnboardingCollectionViewController: UICollectionViewController { | |
private let pages = OnboardingModelFactory.getPages() | |
private lazy var pageControl: UIPageControl = { | |
let pageControl = UIPageControl() | |
pageControl.numberOfPages = pages.count | |
return pageControl | |
}() | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
addPageControlToView() | |
} | |
private func addPageControlToView() { | |
view.addSubview(pageControl) | |
pageControl.translatesAutoresizingMaskIntoConstraints = false | |
pageControl.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -8).isActive = true | |
pageControl.heightAnchor.constraint(equalToConstant: 40).isActive = true | |
pageControl.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true | |
} | |
} | |
extension OnboardingCollectionViewController { | |
// MARK: UICollectionViewDataSource | |
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { | |
return pages.count | |
} | |
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { | |
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: OnboardingCollectionViewCell.reuseIdentifier, for: indexPath) | |
as? OnboardingCollectionViewCell else { fatalError(“Could not cast dequeued cell“) } | |
cell.fill(with: pages[indexPath.row]) | |
return cell | |
} | |
} | |
extension OnboardingCollectionViewController: UICollectionViewDelegateFlowLayout { | |
// MARK: UICollectionViewDelegateFlowLayout | |
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { | |
return CGSize(width: collectionView.bounds.size.width, height: collectionView.bounds.size.height) | |
} | |
override func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) { | |
let pageNumber = Int(targetContentOffset.pointee.x / view.frame.width) | |
pageControl.currentPage = pageNumber | |
} | |
} |
Usando a factory criada anteriormente, instanciamos os conteúdos das três páginas de onboarding e colocamos na constante pages. Ela é a fonte de dados que alimenta a Collection View. Criamos também um UIPageControl
e adicionamos programaticamente na view, definindo as constraints no método ddPageControlToView()
.
O tamanho de cada célula da Collection View é definido no método collectionView(_:layout:sizeForItemAt:)
. O tamanho será igual ao tamanho da Collection View, ou seja, a tela inteira.
O UIPageControl
é atualizado no método scrollViewWillEndDragging(_:withVelocity:targetContentOffset:)
.
UICollectionViewCell
A OnboardingCollectionViewCell
tem referência para os componentes de cada célula, e um método responsável por preencher essas referências com os dados:
Definimos também uma constante estática para o reuse identifier da célula (o mesmo definido no storyboard). Vamos usar essa constante no view controller, para referenciar a célula que será reaproveitada. A classe final ficará assim:
import UIKit | |
class OnboardingCollectionViewCell: UICollectionViewCell { | |
static let reuseIdentifier = “OnboardingCell“ | |
@IBOutlet weak var pageImage: UIImageView! | |
@IBOutlet weak var pageTitle: UILabel! | |
@IBOutlet weak var pageDescription: UILabel! | |
@IBOutlet weak var startButton: UIButton! | |
func fill(with data: OnboardingModel) { | |
// Se quiser imagem no background, descomentar as linhas abaixo | |
// e criar atributo backgroundImage no OnboardingModel | |
// let backgroundImage = UIImageView(image: UIImage(named: data.backgroundImage)) | |
// backgroundImage.contentMode = .scaleAspectFill | |
// backgroundView = backgroundImage | |
backgroundColor = data.backgroundColor | |
pageImage.image = UIImage(named: data.iconImage) | |
pageTitle.text = data.title | |
pageDescription.text = data.content | |
startButton.alpha = data.hideButton ? 0 : 1 | |
} | |
} |
Perceba também que as primeiras instruções do método fill
definem a cor de background da célula com base no OnboardingModel
recebido.
Resources
O último passo é definir os resources usados em cada página. Basta editar o arquivo Assets.xcassets
, dar um nome para cada recurso e arrastar as imagens para os respectivos slots:
Obs.: em um projeto real você vai querer definir as imagens para as resoluções 1x, 2x e 3x, para que a correta seja utilizada conforme a densidade de pixels do aparelho. Para facilitar este exemplo, estou usando uma imagem única para as três densidades.
Alguns detalhes
Fora o que está descrito aqui, melhorei alguns outros detalhes como o espaçamento entre a imagem e os textos, alguns detalhes do botão, e a organização dos arquivos do projeto. A ideia é que ele leve à view inicial do app, ou para o login. Recomendo baixar o projeto do GitHub para ver como ficou o resultado final.
Comments (0)