Articles From Wei-Meng Lee
Filter Results
Article / Updated 08-10-2022
SwiftUI makes creating your iOS applications easy and efficient. However, there are neat tricks that are not so obvious. Here, you learn some of these tips and tricks so that you can become a better SwiftUI developer. Resume SwiftUI’s live preview My number-one pet peeve about SwiftUI is that the live preview feature in Xcode doesn’t always work. Very often, changes made to your code will cause the automatic previewing feature to pause. Even though your code is perfectly correct and there is no error, the live preview just can’t seem to update automatically. Of course, you could click the Resume button to update the preview, but you waste precious time moving your mouse to click the button. A better way is to press ⌘+Option+P. This causes the live preview to resume and update itself. Now that you know this trick, there is no reason to click the Resume button anymore! You may also want to check out the list of shortcuts for working in Xcode. Combine text views in SwiftUI Here is a neat little trick that you should know if you want to display the various words in a sentence in different colors and sizes. Instead of using the HStack view to group various Text views together, you can simply use the plus (+) operator to add different Text views together, like this: struct ContentView: View { var body: some View { Text("Red ") .foregroundColor(.red) .font(.largeTitle) + Text("Green ") .foregroundColor(.green) .font(.body) + Text("Blue") .foregroundColor(.blue) .font(.title) } } How cool is that? Here’s the output. If you want all the texts to be of the same font size, group them together using a Group view and apply the font() modifier on the Group view: struct ContentView: View { var body: some View { Group { Text("Red ") .foregroundColor(.red) + Text("Green ") .foregroundColor(.green) + Text("Blue") .foregroundColor(.blue) } .font(.largeTitle) } } Create custom modifiers in SwiftUI Swift modifiers allow you to change the behaviors of views. Consider the following example: import SwiftUI struct ContentView: View { var body: some View { VStack { Text("Leonardo da Vinci") .bold() .font(.largeTitle) .foregroundColor(.blue) .shadow(radius: 2) Text("Vincent van Gogh") .bold() .font(.largeTitle) .foregroundColor(.blue) .shadow(radius: 2) } } } Here, you apply the same set of modifiers to the two Text views. You often do that when you want to ensure consistencies in your UI (for example, applying the same set of UI styles when displaying certain information in your application). Instead of repeating the same set of modifiers again and again, wouldn’t it be easier if you could just encapsulate all the modifiers into yet another modifier? What you can do it is create another struct that conforms to the ViewModifier protocol. This protocol requires you to implement a body() method that has a Content parameter. You then apply whatever modifiers you want to this Content argument and return it: import SwiftUI struct Title: ViewModifier { func body(content: Content) -> some View { content .font(.largeTitle) .foregroundColor(.blue) .shadow(radius: 2) } } To use the newly created Title struct on the Text view, apply the modifier() modifier and pass in the Title struct, like this: struct ContentView: View { var body: some View { VStack { Text("Leonardo da Vinci") .bold() .modifier(Title()) Text("Vincent van Gogh") .bold() .modifier(Title()) } } } To make the Title struct look more like a true modifier, create an extension to the View protocol and give it a name — say, titleStyle: import SwiftUI extension View { func titleStyle() -> some View { self.modifier(Title()) } } You can now apply the titleStyle() modifier to the two Text views: struct ContentView: View { var body: some View { VStack { Text("Leonardo da Vinci") .bold() .titleStyle() Text("Vincent van Gogh") .bold() .titleStyle() } } } Display multiple alerts in SwiftUI Usually, in SwiftUI you apply a single alert() modifier to a single view. For example, when the user taps a button, you can display an alert by using the alert() modifier to the button. If you have multiple buttons, you can attach an alert() modifier to each button. However, there are times when you need to display multiple different alerts for a single view. Applying multiple alert() modifiers to a single view will not work correctly, because the last modifier will override the earlier ones. To solve this problem, you can use a single alert() modifier, and use a switch statement within the modifier to decide which alert to display. The following example shows a button that, when it’s clicked, generates a random number of either 1 or 2 and uses it to decide which alert to display: struct ContentView: View { @State private var displayAlert = false @State private var alertToDisplay = 0 var body: some View { Button(action: { self.alertToDisplay = Int.random(in: 1..<3) self.displayAlert = true }) { Text("Display Alert") } .alert(isPresented: $displayAlert) { switch alertToDisplay { case 1: return Alert(title: Text("Alert 1"), message: Text("This is Alert 1")) default: return Alert(title: Text("Alert 2"), message: Text("This is Alert 2")) } } } } Enable debug preview in SwiftUI By default, the preview canvas doesn’t display outputs printed using the print() function. This isn’t useful, however, because often you want to use the print() function as a quick debugging option. The good news is, you can easily fix this. In the preview canvas, right-click the Play button and select Debug Preview. Now if you tap the button, your code will print the output in the Output window: struct ContentView: View { var body: some View { Button ("Tap Me") { print("Button was tapped...") } } } If the Output window is not shown in Xcode, press ⌘+Shift+C and it should appear. Preview your SwiftUI app using different devices You’re familiar with using the preview canvas to preview your app. By default, Xcode automatically picks an appropriate device based on your target. You can preview your app on different modes — light mode and dark mode — using the environment() modifier: struct ContentView_Previews: PreviewProvider { static var previews: some View { Group { ContentView() .environment(\.colorScheme, .light) ContentView() .environment(\.colorScheme, .dark) } } } In addition to previewing in different modes, you can alter the size of the preview window, allowing you to have a glimpse of how your UI will look under different screen dimensions. You can do so using the previewLayout() modifier: static var previews: some View { Group { ContentView() .environment(\.colorScheme, .light) .previewLayout((.fixed(width: 400, height: 600))) ContentView() .environment(\.colorScheme, .dark) } } The image below shows the top preview displaying your UI in a dimension of 400 x 600 pixels. Note that clicking the Live Preview button will revert the preview back to the default size. If you want to preview your UI on multiple devices, you can use a ForEach loop, supply a list of device names, and then use the previewDevice() modifier on the ContentView, like this: static var previews: some View { ForEach(["iPhone 11", "iPhone SE"], id: \.self) { deviceName in ContentView() .environment(\.colorScheme, .light) .previewDevice(PreviewDevice( rawValue: deviceName)) .previewDisplayName(deviceName) } } The following image shows the preview on the iPhone 11 and the iPhone SE. Notice that you can display the name of the device using the previewDisplayName() modifier. Check out the full list of devices that you can preview. Dark mode only works on NavigationView As stated, you can use the environment() modifier to set the preview to dark mode so that you can see how your UI will look like in dark mode. However, it seems like the dark preview mode only works for the NavigationView. For example, consider the following example where you have two Text views contained within a VStack view: import SwiftUI struct ContentView: View { var body: some View { VStack { Text("Leonardo da Vinci") Text("Vincent van Gogh") } } } Suppose you use the environment() modifier to set the preview mode to dark, like this: struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() .environment(\.colorScheme, .dark) } } The words in the Text views automatically change to white, but the background remains white (running on the simulator or an actual device doesn’t have this issue), as shown below. So, essentially, you get a white screen. To fix this problem, wrap the ContentView view using a ZStack and set its background to black, like this: struct ContentView_Previews: PreviewProvider { static var previews: some View { ZStack { Color(.black) ContentView() } .edgesIgnoringSafeArea(.all) .environment(\.colorScheme, .dark) } } The image below shows the text showing up on a black background. Extract subviews in SwiftUI Your UI may contain quite a large number of views. This is very common if you have a complicated UI. However, you can simplify your UI by extracting some of the views as subviews. Consider the following example: import SwiftUI struct ContentView: View { var body: some View { HStack { Image("weimenglee") .resizable() .frame(width: CGFloat(120), height: CGFloat(120)) .cornerRadius(CGFloat(15), antialiased: true) VStack { Text("Wei-Meng Lee") .font(.largeTitle) .bold() Text("Founder") Text("Developer Learning Solutions") .italic() Text("http://calendar.learn2develop.net") Text("@weimenglee") } } } } To break down the UI into smaller subviews so that your UI is more modular and manageable, follow these steps: In the preview canvas, select the Image view and press the ⌘ key. Select Extract Subview. Name the new view PhotoView. The Image view will now be extracted as a new struct named PhotoView: struct ContentView: View { var body: some View { HStack { PhotoView() VStack { Text("Wei-Meng Lee") .font(.largeTitle) .bold() Text("Founder") Text("Developer Learning Solutions") .italic() Text("http://calendar.learn2develop.net") Text("@weimenglee") } } } } struct PhotoView: View { var body: some View { Image("weimenglee") .resizable() .frame(width: CGFloat(120), height: CGFloat(120)) .cornerRadius(CGFloat(15), antialiased: true) } } You can now also extract the VStack and save it as another struct named DetailsView. Now your UI looks like the following, which is more maintainable: struct ContentView: View { var body: some View { HStack { PhotoView() DetailsView() } } } struct PhotoView: View { ... } struct DetailsView: View { var body: some View { VStack { Text("Wei-Meng Lee") .font(.largeTitle) .bold() Text("Founder") Text("Developer Learning Solutions") .italic() Text("http://calendar.learn2develop.net") Text("@weimenglee") } } } Display a context menu in SwiftUI One of the innovative features of iPhone is the support for Haptic Touch (which replaces the 3D Touch on older iPhones). Using Haptic Touch, you can long-press an item on your iPhone and a context-sensitive menu appears (if the app you’re using supports it). You can support this feature in SwiftUI as well. To attach a context menu to a view, use the contextMenu() modifier: To attach a context menu to a view, use the contextMenu() modifier: struct ContentView: View { var body: some View { Image("Mac Pro") .resizable() .frame(width: 300, height: 280) .contextMenu { Button(action: { print("Save Image button tapped...") }) { Text("Save Image") Image(systemName: "tray.and.arrow.down") } Button(action: { print("Add to Cart button tapped...") }) { Text("Add to Cart") Image(systemName: "plus") } } } } To create a context menu, you provide a list of Button views, and the content of each button is automatically wrapped using an HStack view. Now when you long-press the Image view, a context menu appears. Want to learn more? Check out our SwifUI Cheat Sheet.
View ArticleArticle / Updated 08-10-2022
SwiftUI is a declarative programming framework for developing UIs for iOS, iPadOS, watchOS, tvOS, and macOS applications. In fact, SwiftUI was invented by the watchOS group at Apple. Before SwiftUI was introduced, most developers use UIKit and Storyboard (which is still supported by Apple in the current version of Xcode to design a UI. Using UIKit and Storyboard, developers drag and drop View controls onto View Controllers and connect them to outlets and actions on the View Controller classes. This model of building UIs is known as Model View Controller (MVC), which creates a clean separation between UI and business logic. The following shows a simple implementation in UIKit and Storyboard. Here, a Button and Label view have been added to the View Controller in Storyboard; two outlets and an action have been created to connect to them: class ViewController: UIViewController { <strong> @IBOutlet weak var lbl: UILabel! @IBOutlet weak var button: UIButton! @IBAction func btnClicked(_ sender: Any) { lbl.text = "Button tapped" }</strong> For laying out the views, you use auto-layout to position the button and label in the middle of the screen (both horizontally and vertically). To customize the look and feel of the button, you can code it in the loadView() method, like this: override func loadView() { super.loadView() <strong>// background color button.backgroundColor = UIColor.yellow // button text and color button.setTitle("Submit", for: .normal) button.setTitleColor(.black, for: .normal) // padding button.contentEdgeInsets = UIEdgeInsets( top: 10, left: 10, bottom: 10, right: 10) // border button.layer.borderColor = UIColor.darkGray.cgColor button.layer.borderWidth = 3.0 // text font button.titleLabel!.font = UIFont.systemFont(ofSize: 26, weight: UIFont.Weight.regular) // rounder corners button.layer.cornerRadius = 10 // auto adjust button size button.sizeToFit() </strong> } The following image shows the button that has customized. UIKit is an event-driven framework, where you can reference each view in your view controller, update its appearance, or handle an event through delegates when some events occurred. In contrast, SwiftUI is a state-driven, declarative framework. In SwiftUI, you can implement all the above with the following statements: struct ContentView: View { <strong> @State private var label = "label"</strong> var body: some View { <strong>VStack { Button(action: { self.label = "Button tapped" }) { Text("Submit") .padding(EdgeInsets( top: 10, leading: 10, bottom: 10, trailing: 10)) .background(Color.yellow) .foregroundColor(Color.black) .border(Color.gray, width: 3) .font(Font.system(size: 26.0)) .overlay( RoundedRectangle(cornerRadius: 10) .stroke(Color.gray, lineWidth: 5) </strong> <strong> ) } Text(label) .padding() }</strong> } } Notice that all the views are now created declaratively using code — no more drag-and-drop in Storyboard. Layouts are now also specified declaratively using code (the VStack in this example stacks all the views vertically). Delegates are now replaced with closures. More important, views are now a function of state (and not a sequence of events) — the text displayed by the Text view is now bound to the state variable label. When the button is tapped, you change the value of the label state variable, which automatically updates the text displayed in the Text view. This programming paradigm is known as reactive programming. The image below shows the various views in action. Want to learn more? Check out our SwiftUI Cheat Sheet.
View ArticleArticle / Updated 08-10-2022
Are you ready to build iOS apps using an innovative and intuitive user interface? Then, SwiftUI is for you! But before you dive in, you’ll need to know about Swift functions. Here’s a quick intro. In Swift, a function is defined using the func keyword, like this: func doSomething() { print("doSomething") } The preceding code snippet defines a function called doSomething. It does not take in any inputs (known as parameters) and does not return a value (technically, it does return a Void value). To call the function, simply call its name followed by a pair of empty parentheses: doSomething() Understanding input parameters A function can also optionally define one or more named typed inputs. The following function takes in one single typed input parameter: func doSomething(num: Int) { print(num) } To call this function, call its name and pass in an integer value (known as an argument) with the parameter name, like this: doSomething(num: 5) The following function takes in two input parameters, both of type Int: func doSomething(num1: Int, num2: Int) { print(num1, num2) } To call this function, pass it two integer values as the argument: doSomething(num1: 5, num2: 6) Returning a value Functions are not required to return a value. However, if you want the function to return a value, use the -> operator after the function declaration. The following function returns an integer value: func doSomething(num1: Int, num2: Int, num3: Int) -> Int { return num1 + num2 + num3 } You use the return keyword to return a value from a function and then exit it. When the function returns a value, you can assign it to a variable or constant, like this: var sum = doSomething(num1:5, num2:6, num3: 7) Functions are not limited to returning a single value. In some cases, it’s important for functions to return multiple values (or even functions). In Swift, you can use a tuple type in a function to return multiple values. Want to learn more? Check out these SwiftUI tips and tricks.
View ArticleArticle / Updated 08-10-2022
Swift is a type-safe language, which means that the programming language makes it clear to you the types of values your code is working with. The following article discusses how to declare constants and variables and how to work with strings and comments when programming with Swift. Swift constants In Swift, you create a constant using the let keyword: let radius = 3.45 // Double let numOfColumns = 5 // Int let myName = "Wei-Meng Lee" // String Notice that there is no need to specify the data type — the data types are inferred automatically. If you want to declare the type of constant, you can do so using the colon operator (:) followed by the data type, as shown here: let diameter<strong>:Double</strong> = 8 After a constant is created, you can no longer change its value. Always use a let when you need to store values that do not change. Swift variables To declare a variable, you use the var keyword: var myAge = 25 var circumference = 2 * 3.14 * radius After a variable is created, you can change its value. In Swift, values are never implicitly converted to another type. For example, suppose you’re trying to concatenate a string and the value of a variable. In the following example, you need to explicitly use the String() function to convert the value of myAge to a string value before concatenating it with another string: var strMyAge = "My age is " + String(myAge) To get the text representation of a value (constant or variable), you can also use the description property, like this: myAge.description. Swift strings One of the common tasks in programming is inserting values of variables into a string. In Swift, you use string interpolation and it has the following format: "Your string literal <strong>\(</strong><em>variable_name</em><strong>)</strong>" The following statement shows an example: let firstName = "Wei-Meng" let lastName = "Lee" var strName = "My name is \(firstName) \(lastName)" You can also use this method to include a Double value in your string (or even perform mathematical operations or function calls): var strResult = "The circumference is \(circumference)" Swift comments In Swift, as in most programming languages, you insert comments into your code using two forward slashes (//): // this is another comment If you have several lines of comments, it’s better to use the /* and */ combination to denote a block of statements as comments: /* this is a comment this is another comment */ Want to learn more? Check out our SwiftUI Cheat Sheet.
View ArticleArticle / Updated 08-10-2022
One important feature in Swift is closure. Closures are self-contained blocks of code that can be passed to functions to be executed as independent code units. Think of a closure as a function without a name. In fact, functions are actually special cases of closures. Swift offers various ways to optimize closures so that they’re brief and succinct. The various optimizations include the following: Inferring parameter types and return types Implicit returns from single-statement closures Shorthand argument names Trailing closure syntax Operator closure Understanding Swift closures The best way to understand Swift closures is to use an example. Suppose you have the following array of integers: let numbers = [5,2,8,7,9,4,3,1] Assume you want to sort this array in ascending order. You could write your own function to perform the sorting, or you could use the sorted() function available in Swift. The sorted() function takes two arguments: An array to be sorted A closure that takes two arguments of the same type as the array and returns a true if the first value appears before the second value Using Swift functions as closures In Swift, functions are special types of closures. As mentioned, the sorted() function needs a closure that takes two arguments of the same type as the array, returning a true if the first value appears before the second value. The following Swift function fulfils that requirement: func ascending(num1:Int, num2:Int) -> Bool { return num1 The ascending() function takes two arguments of type Int and returns a Bool value. If num1 is less than num2, it returns true. You can now pass this function to the sorted() function, as shown here: var sortedNumbers = numbers.sorted(by: ascending) The sorted() function now returns the array that is sorted in ascending order. The sorted() function does not modify the original array. It returns the sorted array as a new array. Assigning Swift closures to variables As mentioned earlier, functions are special types of closures. In fact, a closure is a function without a name. However, you can assign a closure to a variable — for example, the ascending() function discussed earlier can be written as a closure assigned to a variable: var compareClosure : (Int, Int)->Bool = { (num1:Int, num2:Int) -> Bool in return num1 < num2 } To use the compareClosure closure with the sorted() function, pass in the compareClosure variable: sortedNumbers = numbers.sorted(by: <strong>compareClosure</strong>) Writing Swift closures inline You can pass a function into the sorted() function as a closure function, but a better way is to write the closure inline, which obviates the need to define a function explicitly or assign it to a variable. Rewriting the earlier example would yield the following: sortedNumbers = numbers.sorted(by: { (num1:Int, num2:Int) -> Bool in return num1 < num2 } ) As you can see, the ascending() function name is now gone; all you’ve supplied is the parameter list and the content of the function. If you want to sort the array in descending order, you can simply change the comparison operator: sortedNumbers = numbers.sorted(by: { (num1:Int, num2:Int) -> Bool in return num1 > num2 } ) Understanding type inference Because the type of the first argument of the closure function must be the same as the type of array you’re sorting, it’s actually redundant to specify the type in the closure, because the compiler can infer that from the type of array you’re using: var fruits = ["orange", "apple", "durian", "rambutan", "pineapple"] print(fruits.sorted(by: { (fruit1, fruit2) in return fruit1 If your closure has only a single statement, you can even omit the return keyword: print(fruits.sorted(by: { (fruit1, fruit2) in fruit1 Using shorthand argument names Above, names were given to arguments within a closure. In fact, this is also optional, because Swift automatically provides shorthand names to the parameters, which you can refer to as $0, $1, and so on. The previous code snippet could be rewritten as follows without using named parameters: print(fruits.sorted(by: { $0<$1 }) ) To make the closure really terse, you can write everything on one line: print(fruits.sorted(by:{ $0<$1 })) Working with Swift’s operator function You saw that the closure for the sorted() function was reduced to the following: print(fruits.sorted(by:{ $0<$1 })) One of the implementations of the lesser than (<) operator is actually a function that works with two operands of type String. Because of this, you can actually simply specify the < operator in place of the closure, and the compiler will automatically infer that you want to use the particular implementation of the < operator. The preceding statement can be reduced to the following: print(fruits.sorted(by:<strong><</strong>)) If you want to sort the array in descending order, simply use the greater than (>) operator: print(fruits.sorted(by:<strong>></strong>)) Using trailing closures in Swift Consider the closure that you saw earlier: print(fruits.sorted(by: { (fruit1, fruit2) in return fruit1 Notice that the closure is passed in as a second argument of the sorted() function. For long closures, this syntax may be a little messy. If the closure is the final argument of a function, you can rewrite this closure as a trailing closure. A trailing closure is written outside of the parentheses of the function call. The preceding code snippet, when rewritten using the trailing closure, looks like this: print(fruits.sorted() { (fruit1, fruit2) in return fruit1 Using the shorthand argument name, the closure can be shortened to the following: print(fruits.sorted()<strong>{$0<$1}</strong>) Want to learn more? Check out our SwiftUI Cheat Sheet.
View ArticleArticle / Updated 08-10-2022
To animate a view in SwiftUI, apply the animation() modifier on it. SwiftUI animates any changes made to animatable properties of a view. For example, the various properties of a view in SwiftUI — such as its color, opacity, rotation, size, and other properties — are all animatable. As usual, the best way to understand this concept is to use an example. First, create a rounded button that shows the Confirm caption: struct ContentView: View { var body: some View { Button(action: { }) { Text("Confirm") .bold() } .padding(40) .background(Color.green) .foregroundColor(.white) .clipShape(Circle()) } } Apply some scaling (zooming) to the button using the scaleEffect() modifier: struct ContentView: View { @State private var scaleFactor: CGFloat = 1 var body: some View { Button(action: { }) { Text("Confirm") .bold() } .onAppear(perform: { self.scaleFactor = 2.5 }) .padding(40) .background(Color.green) .foregroundColor(.white) .clipShape(Circle()) .scaleEffect(scaleFactor) } } What you want to do here is zoom the button to two and a half times its original size. The scaling will be performed as soon as the Button view is shown in SwiftUI. The following image shows the button zoomed in to two and a half times its original size when it first appears. What you really want is to slow down the scaling, so that users can see the zooming-in process. For this, you can use the animation() modifier on the Button view: struct ContentView: View { @State private var scaleFactor: CGFloat = 1 var body: some View { Button(action: { }) { Text("Confirm") .bold() } .onAppear(perform: { self.scaleFactor = 2.5 }) .padding(40) .background(Color.green) .foregroundColor(.white) .clipShape(Circle()) .scaleEffect(scaleFactor) .animation(.default) } } The .default property actually belongs to the Animation struct, so you can rewrite the above statement as follows: .animation(<strong>Animation</strong>.default) When you now load the Button view again, the button zooms in two and a half times. Specifying the type of animation in SwiftUI By default, the button will zoom in at a linear speed. You can also use the easeInOur() modifier if you want the animation to start slow, pick up speed, and then slow down again: .animation( .easeInOut(duration: 2) ) The duration parameter indicates how much time is given for the animation to complete in SwiftUI. In this example, the zoom animation must complete in two seconds. If you want to start fast and then slow down, use the easeOut() modifier: .animation( .easeOut(duration: 2) ) Both the easeInOut() and easeOut() modifiers are type methods of the Animation struct. Repeating the animation in SwiftUI Many times, you want the animation to repeat a number of times. For this you can apply the repeatCount() modifier: .animation( Animation.easeInOut(duration: 2) .repeatCount(2, autoreverses: true) ) The easeInOut() is a type method of the Animation struct, and it returns an Animation struct. So, in this case, you call the repeatCount() modifier of the Animation struct to repeat the animation a number of times (twice, in this case). The autoreverses parameter allows you to reverse the animation, so for this particular case the size of the button changes from small to big, and then reverses and changes from big to small. The image below shows the animation that is repeated twice. Notice that at the end of the second animation, the button reverts back to the larger size as specified in the scaleFactor state variable: .scaleEffect(scaleFactor) // changed to 2.5 in onAppear() If you want the animation to repeat forever, use the repeatForever() modifier: .animation( Animation.easeInOut(duration: 2) .repeatForever(autoreverses: true) ) Stopping the animation in SwiftUI Although you can animate nonstop in SwiftUI, there are times where you need to stop the animation. Here’s another example: struct ContentView: View { @State private var opacity:Double = 1.0 var body: some View { Button(action: { }) { Text("Click Me") .fontWeight(.bold) .font(.title) .foregroundColor(.blue) .padding() .background(Color.yellow) .overlay( Rectangle() .stroke(Color.blue, lineWidth: 5) ) .opacity(opacity) .onAppear() { let baseAnimation = Animation.linear(duration: 1) withAnimation ( baseAnimation.repeatForever( autoreverses: true)) { self.opacity = 0.2 } } } } } The preceding code snippet shows a Button view with its opacity initially set to 1.0. When it appears, you perform a linear animation (animating with constant speed) to change the opacity of the button down to 0.2, all within a duration of 1 second. In the next 1 second, it then changes to fully opaque again. Unlike the earlier example, this example does not use the animation() modifier for animation. Instead, you use the withAnimation block. The withAnimation block lets you explicitly tell SwiftUI what to animate. The image below shows the button fully opaque when it’s loaded and then gradually changes its opacity to 0.2. The animation is perpetual, so to stop it, you need to do some work in SwiftUI. For this, you can use a Boolean state variable (let’s call it animate) and use it to determine if the animation should continue: withAnimation (self.animate ? baseAnimation.repeatForever( autoreverses: true) : Animation.default) { self.opacity = 0.2 } In the preceding Swift code snippet, if the animate state variable is true, you’ll perform the animation perpetually, or you can set the animation to default (which will only perform the animation once). The following code snippet stops the animation when the button is tapped and sets the opacity of the button back to 1: struct ContentView: View { @State private var opacity:Double = 1.0 @State private var animate = true var body: some View { Button(action: { self.animate = false self.opacity = 1.0 }) { Text("Click Me") .fontWeight(.bold) .font(.title) .foregroundColor(.blue) .padding() .background(Color.yellow) .overlay( Rectangle() .stroke(Color.blue, lineWidth: 5) ) .opacity(opacity) .onAppear() { let baseAnimation = Animation.linear(duration: 1) withAnimation (self.animate ? baseAnimation.repeatForever( autoreverses: true) : Animation.default) { self.opacity = 0.2 } } } } } Remember to follow the Apple Human Interface Guidelines (HIG) when it comes to animating your UI. This also applies to custom animations. Want to learn more? Check out these SwiftUI resources.
View ArticleCheat Sheet / Updated 11-24-2021
Go is a popular open-source programming language (designed at Google in 2007) used for a variety of different applications, including cloud-based or server-side applications, DevOps and site reliability automation, command-line tools, artificial intelligence, and data science. You can try out Go (sometimes referred to as Golang) programming language online — you just need to know where to go. You also may want to know how to convert JSON to Go or use Go in Docker. This Cheat Sheet tells you how to do all of these things.
View Cheat SheetArticle / Updated 08-26-2021
Did you know you can use UIKit in SwiftUI? It’s true! When you develop iOS apps using Storyboard in Xcode, you use the UIKit for all UI matters. UIKit is the framework that forms the core components of all iOS applications. Among all the classes in the UIKit, view and view controllers are the most commonly used classes. View controllers (UIViewController) play a crucial role in your apps — they act as the skeleton of your application. A view controller is basically a class that manages a set of views (to display to the user), while coordinating with model (data) objects. Because the view controller is connected to your Storyboard (an external file), it has very little control of what’s happening on the view side of things (and vice versa). You may be familiar with this — connect an event in your Interface Builder to a function in your code in Swift and then later on you delete the function. However, the Interface Builder doesn’t know that your function is now gone, and all hell will break loose when that event is invoked during runtime. This problem is precisely what SwiftUI tries to resolve. The various widgets — such as Button, Label, TextField, and Switch — are represented by the subclasses of UIView. This article discusses how to use UIKit within a SwiftUI project, but the converse is also true — you would also want to use SwiftUI in an existing UIKit project. Note that after you add SwiftUI to your UIKit project, your apps will only run on iOS 13.0 and later, macOS 10.15 and later, tvOS 13.0 and later, and watchOS 6.0 and later. The image below shows a UIViewController instances containing a number of UIView instances. Understanding the UIKit View Controller life cycle In order to handle the various states of a view controller, a view controller has a set of events, known as the life cycle of a view controller. The life cycle of a view controller has a variety of events, including (but not limited to) the following: viewDidLoad: Called after the controller’s view is loaded into memory loadView: Creates the view that the controller manages viewWillAppear: Notifies the view controller that its view is about to be added to a view hierarchy viewDidAppear: Notifies the view controller that its view was added to a view hierarchy viewWillDisappear: Notifies the view controller that its view is about to be removed from a view hierarchy viewDidDisappear: Notifies the view controller that its view was removed from a view hierarchy didReceiveMemoryWarning: Sent to the view controller when the app receives a memory warning The following Swift code shows how to handle the various events of a view controller. import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() } override func loadView() { super.loadView() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } } The various events are fired during the lifetime of the view controller. For example, you often initialize your variables during the viewDidLoad event (it fires only once when the view controller is first loaded). If you needed to update the view whenever the view controller appears, you would perform the updates in the viewDidAppear event (fired every time the view comes to the foreground). Or if you need to build the UI dynamically during loading time, you can do it in the loadView event. Understanding the SwiftUI view life cycle Compared to the UIViewController life cycles, the life cycle of a view in SwiftUI is much simpler and more straightforward. In SwiftUI, there is no concept of a view controller. Instead, everything is a View. In SwiftUI, View has only two events: onAppear onDisappear You can attach these two events to any views, and SwiftUI will execute them when they occur. Working with the onAppear and onDisappear events in SwiftUI When a view appears, the onAppear event will be fired. Likewise, when the view disappears, the onDisappear event will be fired. Let’s illustrate the use of the onAppear event: func fetchData() { ... } var body: some View { List(articles, id: \.url) { item in VStack(alignment: .leading) { Text(item.title) .font(.headline) Text(item.description ?? "") .font(.footnote) } }.onAppear(perform: fetchData) } When the List view first appears, it will fire the onAppear event, which you then use to call the fetchData() function to load data from the web. For that example, it’s quite straightforward to understand when the onAppear event fires. A more involved example would be one comprising a NavigationView and a TabView. The following image shows an example where the ContentView contains a TabView, which in turn also contains a NavigationView. The following Swift code snippet shows the implementation of the three views that you’ve just seen. To understand when the onAppear and onDisappear events are fired when the user moves from one screen to another, you insert print statements, as shown in the following code: import SwiftUI struct View1: View { var body: some View { Text("View1") .onAppear{ print("onAppear in View1") } .onDisappear{ print("onDisappear in View1") } } } struct View2: View { var body: some View { Text("View2") .onAppear{ print("onAppear in View2") } .onDisappear{ print("onDisappear in View2") } } } struct ContentView: View { var body: some View { TabView { NavigationView{ NavigationLink(destination: View1()) { Text("Next") } }.onAppear{ print("onAppear in ContentView") } .onDisappear{ print("onDisappear in ContentView") } .tabItem { Image(systemName: "viewfinder.circle.fill") Text("ContentView") } View2() .tabItem { Image(systemName: "camera.viewfinder") Text("View2") } } .onAppear{ print("onAppear in TabView") } .onDisappear{ print("onDisappear in TabView") } } } When the app is first loaded, you see the following outputs: onAppear in TabView onAppear in ContentView You can see that TabView first appeared, followed by ContentView. When you click the Next button, it navigates to View1. The following statement in bold shows the additional statement printed: onAppear in TabView onAppear in ContentView <strong>onAppear in View1</strong> Even though ContentView is now hidden, it hasn’t disappeared yet (because there is no output indicating its disappearance). Now, click the Back button to go back to ContentView. This time, you see the following additional output: onAppear in TabView onAppear in ContentView onAppear in View1 <strong>onDisappear in View1</strong> So View1 has disappeared. Click the View2 item on the tab bar. View2 is now loaded. Here’s the output: onAppear in TabView onAppear in ContentView onAppear in View1 onDisappear in View1 <strong>onDisappear in ContentView</strong> <strong>onAppear in View2</strong> Now ContentView has disappeared and View2 appears. Finally, click ContentView item on the tab bar and notice that View2 has disappeared and ContentView has appeared again: onAppear in TabView onAppear in ContentView onAppear in View1 onDisappear in View1 onDisappear in ContentView onAppear in View2 <strong>onDisappear in View2</strong> <strong>onAppear in ContentView</strong> As you can see from the example, the onAppear event allows you to know when a view comes onscreen. This is useful in cases where you want to load data on demand. Suppose you have a tabbed application with five tab items and each of these tab pages separately loads data from the web. It would be useful to load the data only when the user views a particular page. Otherwise, the entire iOS app would feel sluggish when it tries to load all the data at the same time when the app starts up. Instantiating properties of a view in SwiftUI Besides understanding when the two events are fired, it’s also important to understand how SwiftUI processes a View when it’s created and the sequences in which properties and binding objects are processed. You may already know that the View is a struct. Very often, views have properties, which allows you to pass in additional information to customize the behaviors of views. Consider the following simple example of a View named MyView: struct MyView: View { var greetings: String var body: some View { Text(greetings) } } In the MyView struct, you have a property named greetings. It is of type String, but it hasn’t been initialized yet. When you try to use MyView, as in the following code snippet, struct ContentView: View { var body: some View { VStack{ MyView("Hello, World!") } } } You get the error: Missing argument label 'greetings:' in call. This is because you need to initialize the value of the greetings property in MyView, which is the first thing the compiler tries to do when instantiating the struct. To fix this, pass in the argument for the greetings property when calling MyView, like this: MyView(greetings: "Hello, World!") Using initializers in a SwiftUI view Structs can also have initializers (constructors). If MyView has an explicit initializer, like the following: struct MyView: View { var greetings: String init(_ str:String) { self.greetings = str } var body: some View { Text(greetings) } } Then greetings must be initialized via the initializer for MyView: MyView("Hello, World!") Using binding variables in a SwiftUI view If MyView has a @Binding variable, like this: struct MyView: View { @Binding var text: String var greetings: String var body: some View { Text(greetings) } }</pre Then you have to initialize it through the parameters: struct ContentView: View { @State var text: String = "" var body: some View { MyView(greetings: "Hello, World!", text: $text) } } Note that the binding variable argument must come after the greetings argument. If MyView has an explicit initializer, you have to specify the type of the Binding object in your initializer, like this: struct MyView: View { @Binding var text: String var greetings: String init(_ str:String, _ text: Binding) { self.greetings = str self._text = text } var body: some View { Text(greetings) } } Observe that the text @Binding variable has an underscore (_) in the initializer: self._text = text And now when you create an instance of the MyView struct, you can bind the state variable through the initializer, like this: MyView("Hello, World!", <strong>$text</strong>) Want to learn more? Check out these SwiftUI resources.
View ArticleArticle / Updated 10-30-2020
In addition to animating changes made to animatable properties of views, SwiftUI also allows you to specify your own animation, such as moving views or rotating views. Here, you learn how to rotate views in two dimensions in SwiftUI. Rotating in 2D To perform rotation in two dimensions in SwiftUI, use the rotationEffect() modifier. The following code snippet displays a “wheel of fortune” image on the screen, together with a Button view: struct ContentView: View { @State private var degrees = 0.0 var body: some View { VStack{ Image("wheel") .resizable() .frame(width: 400.0, height: 400.0) .rotationEffect(.degrees(degrees)) Button("Spin") { let d = Double.random(in: 720...7200) withAnimation () { self.degrees += d } } } } } When the button is tapped, a random number between 720 and 7,200 is generated and assigned to the degrees state variable. This state variable is bound to the rotationEffect() modifier, so when the button is tapped, the image will rotate. To rotate the image one complete turn takes 360 degrees. So, when you generate a value from 720 to 7,200, you’re essentially making the image turn from 2 to 20 complete turns. The image on the left shows the image before rotation. The image on the right shows the image rotated using the random number generator. For this example, you really need to try it out to see the effects of the rotation. If you try this code snippet, you’ll find that the animation is very abrupt — it starts and ends with the same speed. A more natural way of spinning could be achieve using the easeInOut() modifier: var body: some View { VStack{ Image("wheel") .resizable() .frame(width: 400.0, height: 400.0) .rotationEffect(.degrees(degrees)) Button("Spin") { let d = Double.random(in: 720...7200) let baseAnimation = Animation.easeInOut(duration: d / 360) withAnimation (baseAnimation) { self.degrees += d } } } } In the preceding addition, you use the easeInOut() modifier to perform the animation based on the number of complete rotations you need to perform (each turn is allocated 1 second). When the wheel is rotated now, it’s more realistic, and at the same time each spin of the wheel takes a random amount of times to complete (more spins take more time). Rotating in 3D In addition to performing animations in 2D, you can also perform 3D animations using the rotation3DEffect() modifier. To understand how this modifier works, it’s best to look at an example: struct ContentView: View { var body: some View { Text("SwiftUI for Dummies") .font(.largeTitle) } } The preceding code shows a Text view displayed in large title format. Let’s now apply a rotation3DEffect() modifier to the Text view: struct ContentView: View { @State private var degrees = 45.0 var body: some View { Text("SwiftUI for Dummies") .font(.largeTitle) .rotation3DEffect(.degrees(degrees), axis: (x: 1, y: 0, z: 0)) } } In the preceding addition, you converted the value of the degrees state variable using the degrees() function (which returns an Angle struct), pass the result to the rotation3DEffect() modifier, and specify the axis to apply the rotation to. In this example, you specified the x-axis to apply the rotation. The result of this rotation is shown below. If you change the degrees to 75, then the result would look like the following image. @State private var degrees = <strong>75.0</strong> Now change the axis to y and the result is as shown below: struct ContentView: View { @State private var degrees = 75.0 var body: some View { Text("SwiftUI for Dummies") .font(.largeTitle) .rotation3DEffect(.degrees(degrees), axis: (x: 0, y: 1, z: 0)) } } How about z-axis? The following image shows the output: struct ContentView: View { @State private var degrees = 45.0 var body: some View { Text("SwiftUI for Dummies") .font(.largeTitle) .rotation3DEffect(.degrees(degrees), axis: (x: 0, y: 0, z: 1)) } } Finally, how about all the three axes? The image you see below shows the output: struct ContentView: View { @State private var degrees = 45.0 var body: some View { Text("SwiftUI for Dummies") .font(.largeTitle) .rotation3DEffect(.degrees(degrees), axis: (x: 1, y: 1, z: 1)) } } By now, you should have a pretty good grasp of how the rotation is applied to the three axes. The next image shows a summary of the rotation made to the three axes: Now apply some 3D animation to the rotation: struct ContentView: View { @State private var degrees = 0.0 var body: some View { Text("SwiftUI for Dummies") .font(.largeTitle) .rotation3DEffect(.degrees(degrees), axis: (x: 1, y: 1, z: 1)) .onAppear() { let baseAnimation = Animation.easeInOut(duration: 3) withAnimation (baseAnimation) { self.degrees += 360 } } } } Now you’re able to see the text rotate in 3D. Want to learn more? Check out these SwiftUI resources.
View ArticleArticle / Updated 10-28-2020
You may already know what you need to succeed in SwiftUI development, but most Swift developers need multiple resources to stay ahead. Here, you learn about ten great SwiftUI resources that will be useful to you when you’re ready to venture beyond the basics. Apple You may as well go straight to the source! Apple has a set of resources for learning SwiftUI, including tutorials, documentation, sample code, videos, and a forum. Of particular interest is the Tutorial section. As you scroll through each topic, you can follow the various steps in each section. Some of the figures are animated and interactive, which makes following the tutorials fun and effective. You can also jump directly to a specific topic that you’re interested in and select the relevant section to start learning. SwiftUI by Example SwiftUI by Example has a vast collection of tutorials on everything related to SwiftUI. If you search online for anything related to SwiftUI, chances are, you’ll find an answer on Swift by Example. The code examples are very well written — straight to the point, without all the fluff. 100 Days of SwiftUI Created by Paul Hudson, the same person behind SwiftUI by Example 100 Days of SwiftUI is a collection of videos, tutorials, tests, and more, designed to help beginners learn SwiftUI effectively. What’s more, the course is entirely free! All you need is your motivation and focus to complete the 100 days of learning and coding. Gosh Darn SwiftUI Gosh Darn SwiftUI is a curated list of questions and answers about SwiftUI. It also contains a quick reference to some of the commonly used views and controls in SwiftUI. I find it useful as a quick code reference when I need to refresh myself on how to use a particular view. There is an alternative URL for this same website. If you’re curious, just replace goshdarn with f___ing (we can’t spell out the whole word, but you can probably guess what the word is). You didn’t hear it from me. SwiftUI Hub SwiftUI Hub is a collection of links to tutorials on SwiftUI. This site is useful if you’re trying to learn a new topic in SwiftUI. Awesome SwiftUI Awesome SwiftUI is another curated list of awesome SwiftUI tutorials, libraries, videos, and articles. It contains tutorials and videos from the Apple Worldwide Developers Conference (WWDC), as well as related articles on SwiftUI. It also contains a list of GitHub code repositories of SwiftUI projects. raywenderlich.com raywenderlich.com has established itself in the iOS development world. It has a list of tutorials on SwiftUI that are worth a read. In addition, if you’re already familiar with the Swift programming language, SwiftUI by Tutorials is a great book to get you started using SwiftUI to build an iOS app user interface (UI) declaratively. Swift Talk Swift Talk is a weekly video series on Swift programming. Although Swift is in its name, it contains a lot of interesting videos on SwiftUI, including ones that allow you to learn a particular topic in depth. About SwiftUI This site is another repository for all things SwiftUI. About SwiftUI contains a listing of documentations by Apple, WWDC videos, tutorials, books, courses, and articles, as well as code repositories created using SwiftUI. Stack Overflow No resource list would be complete if it didn’t mention Stack Overflow, a question-and-answer site for professional and enthusiast programmers. In fact, if you don’t know Stack Overflow, you aren’t a programmer yet! One of the top places to get your questions on SwiftUI answered is Stack Overflow. In fact, if you search the web for the answer to your question, chances are, Stack Overflow already has what you’re looking for! And if these don’t do the trick, you can always check out our SwiftUI Cheat Sheet. And if you’re still not sated, you can always learn a few new SwiftUI tips and tricks.
View Article