Introduction
StackKit harnesses the power of UIStackView's and Swift's ResultBuilders together. This allows you to easily build declarative UI while only using UIKit. StackKit is a great solution for anyone looking to level up their programmatic UI without having to convert an existing project to SwiftUI. Its API design is closely matched to SwiftUI making it effortless to pick up without having to accommodate any reactive programming patterns.
Examples
Feed | Account |
---|---|
Why StackKit?
The main advantage StackKit has over a standard programmatic constraints-based approach is maintenance and flexibility. You can quickly change your layout by simply moving the ordering of a couple of views. No more painful rearranging of anchors e.c.t. You can also evaluate conditionals within the UI code, adding boolean properties, loops maps, and more.
We've made some Example screens and code samples would definitely recommend starting there to grasp how powerful and simplistic StackKit is. We hope to see it in your projects in the near future
StackKit is built and maintained with love by the Gymshark Engineering Team
Why would you use StackKit instead of SwiftUI
- You still want to use UIKit throughout your app
- Harness the power of building UI like SwiftUI does but your team/project isn’t ready for reactive programming.
- It’s easier to integrate into existing code bases. With SwiftUI you can’t swap a View for a UIView. This makes it hard to integrate old UI components into SwiftUI.
- SwiftUI hides the underlying view rendering, therefore making it hard to see what is actually going on and refine issues. With StackKit you are still within UIKit so don't lose any abilities.
⚠️
Note If you are trying to use StackKit to handle large data sets to replace a UITableView or UICollection view you will run into performance issues. For this usecase you should use ListKit
Installation
Swift Package Manager (SPM)
To install StackKit using Swift Package Manager you can follow the tutorial published by Apple using the URL for the StackKit repo with the current version:
\1. In Xcode, select “File” → “Swift Packages” → “Add Package Dependency”
\2. Enter https://github.com/gymshark/ios-stack-kit.git
Getting Started
StackKit works by extending off of any UIView
, adding a UIStackView
to the parents' subview, setting the constraints, and then appending the views to the stack provided to it via the ResultBuilder This process makes laying out you UI programmatically effortless and because it is simply using UIStackView underneath, all of the same layout logic still applies.
If you haven't used UIStackView's before or need a refresher, I highly recommend checking out this article. Result builders are also key to the power of StackKit as well as SwiftUI. To get a better understanding of them check out this article. They are awesome
With StackKit there are 4 main types of Stacks that you can use. VStack, HStack, VScroll and HScroll. Here are some common code examples below to help you get started with StackKit by extending off your UIView's.
UIViewController
func viewDidLoad(){
view.VStack {
avatar
displayName
friendStatus
Spacer() // Fills rest of space
}
}
UIView
class ProfileView: UIView {
init(){
HStack {
avatar
displayName
friendStatus
}
}
}
Stackception 🤯 )
Stack's within stack's... (Above you can see how extending off a UIView and placing UIView items within a Stack is a relatively straightforward process. But what about putting a stack within a stack? Because our result builder is expecting type is a UIView, we can simply pass in another Stack which inturn also excepts UIViews. But, for this, we need to use a Class variant of the Stack instead of extending off of a UIView. Thankfully this is all taken care of and you don't need to do anything differently. Here is a nice example of stacks within stacks.
class ProductsView: UIView {
init(){
HStack {
VStack {
title1
product1
HStack {
statusIcon1
statusLabel1
}
orderButton1
}
VStack {
title2
product2
HStack {
statusIcon2
statusLabel2
}
orderButton2
}
}
}
}
Layout and customisation
You might be asking but how do I control the layout, what about sizing constraints and setting spacing and padding. Don't worry we have you covered.
Advanced stack layout
To have greater control over how a particular Stack handles the layout of its views you can access its alignment
and distribution
properties by chaining off the Stack. You can find out more about how to best utilize these properties for your specific needs in these articles.
• https://www.hackingwithswift.com/example-code/uikit/what-are-the-different-uistackview-distribution-types • https://www.raywenderlich.com/2198310-uistackview-tutorial-for-ios-introducing-stack-views
VStack {
displayName
VStack {
bioLabel
HStack {
skills.compactMap({ SkillView(skill: $0) })
}.distribution(.fillEqually)
.alignment(.top)
}
prices.compactMap({ SkillView(skill: $0) })
Spacer()
}.distribution(.fill)
.alignment(.center)
Spacing
At some point, you will want to provide some spacing between your items. Thankfully with UIStackViews this is simple. We can chain off the specific stack using the .spacing
function. As well as this we can also add some unique spacing in places where required using the Spacing
class. It's worth noting that you will want to use this class when you would like the remaining area in a Stack to be taken up by an empty view.
VStack {
displayName
tagline
favouriteColor
Spacer(h: 10)
HStack {
skills.compactMap({ SkillView(skill: $0) })
}.spacing(150)
Spacer(h: 20)
seporator
Spacer()
}.spacing(5)
Margin
Applying padding around a stack is made a lot easier with UIStackViews layoutMargins
which takes a UIEdgeInset property. To use this with your stack you can just chain off the stack again using the .margin()
function. This provides a great way of giving spacing around views without lots of complex layout code.
VStack {
displayName
tagline
favouriteColor
Spacer(h: 10)
HStack {
skills.compactMap({ SkillView(skill: $0) })
}.spacing(5)
.margin(.horizontal(10))
Spacer()
}.margin(.init(top: 20, left: 10, bottom: 20, right: 10)
Sizing
Sizing of views can still be achieved using standard NSLayout anchors width and height. Although we prefer to use some handy extensions to make this a lot easier from ConstraintsKit. Simply chain off any view you want to apply an explicit size to using .with()
notation. You will notice we have width, height, and square extensions available. Because we are using stacks you don't need to worry about your other views. They will expand and contract where needed. You can even apply sizing to your stacks if you wish to.
VStack {
displayName.withHeight(120)
profilePicture.withSquare(150)
footer.withSize(w: 300, h: 150)
}.alignment(.center)
.margin(.all(20)
Use of result builders
With the power of result builders, we can also add conditional parameters and add things such as compact maps and for loops easily. This makes writing coding features a little bit easier. Some use cases might be, Feature Toggling, Hiding Views, easily applying arrays to the stacks.
VStack {
if premiumUser {
PremiumBadge(name: "Russell")
}
userInfo.map({
UserTag(text: $0)
)}
HStack {
(0..10).map { (id: Int) in
NumberView(number: id)
}
}
}.alignment(.center)