SwiftUI is amazing
It is a declarative way to build up user interfaces for iOS apps. So that developers don’t need to decide if they want to build user interfaces using code or storyboard/xib. In combination with the great SwiftUI tools supported in XCode 11, the process to develope apps for Apple’s platforms has completely changed.
You can find a lot more information about SwiftUI in many WWDC 2019 sessions dedicated to SwiftUI
You can also take a look here for list of SwiftUI Views corresponding to their UIKit counterparts.
As a new framework and the documentation is not fully completed, it is sometimes hard to do some simple tasks. One of such common tasks is showing a loading indicator while the app is doing some heavy asynchronous operations. If you follow all WWDC Videos about SwiftUI, you can’t find any sample code for showing loading indicators while for example loading images from remote sources/internet.
Wrapping UIControls
Since SwiftUI doesn’t have all equivalent Views
for all UIControls
from UIKit
, the SwiftUI team has created great APIs so that developers can wrap any UIKit components in SwiftUI’s views. Basically the wrapper must only adopt UIViewRepesentable
protocol.
The following code will wrap UIActivityIndicator
into a ActivityIndicator
View which can be use as a SwiftUI View.
struct ActivityIndicator: UIViewRepresentable {
typealias UIViewType = UIActivityIndicatorView
let style: UIActivityIndicatorView.Style
func makeUIView(context: UIViewRepresentableContext<ActivityIndicator>) -> ActivityIndicator.UIViewType {
return UIActivityIndicatorView(style: style)
}
func updateUIView(_ uiView: ActivityIndicator.UIViewType, context: UIViewRepresentableContext<ActivityIndicator>) {
uiView.startAnimating()
}
}
Normally, developers don’t use UIActivityIndicator
alone but also adding an UILabel
to describe the context of the asynchronous operation such as: Loading, Calculating, Processing …
Since we now have ActivityIndicator
as a SwiftUI View, we can easily use it and combine with other views to create a full-blown HUD (Head up Display) View
struct ActivityIndicatorView<Content>: View where Content: View {
@Binding var isShowing: Bool
var content: () -> Content
var body: some View {
GeometryReader { geometry in
ZStack(alignment: .center) {
if (!self.isShowing) {
self.content()
} else {
self.content()
.disabled(true)
.blur(radius: 3)
VStack {
Text("Loading ...")
ActivityIndicator(style: .large)
}
.frame(width: geometry.size.width / 2.0, height: 200.0)
.background(Color.secondary.colorInvert())
.foregroundColor(Color.primary)
.cornerRadius(20)
}
}
}
}
}
To preview it, you can use this example code:
#if DEBUG
struct ActivityIndicatorView_Previews: PreviewProvider {
static var previews: some View {
ActivityIndicatorView(isShowing: .constant(true)) {
NavigationView {
Text("Hello World")
.navigationBarTitle(Text("List"), displayMode: .large)
}
}
}
}
#endif
The result is quite promising: