Training and Instructional Design
Sometimes you need a control that doesn’t yet exist. For example, the UISwitch
control is far from customizable. I’d like a toggle switch that looks more like an old-fashioned switch or pushbutton. Let’s learn the basics of making your own controls by making a UIButton
into a toggle button.
Open the example file. You’ll find an empty class UIToggleButton
. in the UIToggleButton.swift file. To make our toggle button I’m going to subclass UIButton
, since much of what I want is there.
class UIToggleButton:UIButton{ }
I can’t use an extension because I’m going to add more properties to the button. The most important of those will be isOn
.
var isOn:Bool = false
UIButton
subclasses UIControl
. There is a series of tracking methods in UIControl
that handles touches. For controls, you don’t use UIResponder
for touch events, but these methods instead. The control will handle any target-action pairs in the class hierarchy. I’ll change the button when I stop tracking a touch using the endTracking
method.
override func endTracking(_ touch: UITouch?, with event: UIEvent?) { }
Always call super
when you use these methods.
super.endTracking(touch, with: event)
And I’ll change the state of the isOn
button.
isOn = !isOn
Head to the ViewController
code. You’ll see I made a button configuration method configureToggleButton
. One problem with custom controls like this is you will have to add them programmatically without more work, which I’ll cover in a future tip. I added two methods to configure the button and to lay out the button, which you can find in the example file or the listing below. I’ll use the button in the action I set up, where I’ll change the title on the toggle. This way you can see the action and the touch event can run independently, but see the effect of the touch event on the toggle.
if sender.isOn { sender.setTitle("On", for: .normal) } else { sender.setTitle("Off", for: .normal) }
Build and run. You’ll have a button in the center of the screen.
You can add more features. I’ll add images for the buttons.
I may not use an image. If I don’t I’ll want the image to be nil
, and use the superclass properties, so I’ll use optionals for the image in the UIToggleButtonClass
public var onImage:UIImage! = nil public var offImage:UIImage! = nil
In my UIToggleButton
method, II’ll make a method updateDisplay
which will change the button’s background image.
func updateDisplay() { if isOn { if let onImage = onImage{ setBackgroundImage(onImage, for: .normal) } } else { if let offImage = offImage{ setBackgroundImage(offImage, for: .normal) } } }
I’ll update the display once I set the image. I’ll use a property setter to do that.
var isOn:Bool = false{ didSet{ updateDisplay() } }
I’ll do the same for the off image, so I’ll copy and paste that
var offImage:UIImage! = nil { didSet{ updateDisplay() } }
And the same for the on Image
var onImage:UIImage! = nil{ didSet{ updateDisplay() } }
I added to the assets folder a toggle off and toggle on button image. Head to the ViewController
. In the cofigureToggleButton
, add two more lines of code.
toggleButton.offImage = UIImage(named: "Toggle off") toggleButton.onImage = UIImage(named:"Toggle on")
Build and run. Tap the button. You’ll get a cool looking button that glows when on, and dims when off.
You’ll find a few more examples of switches and toggles you can use in the downloads file. Give them a try like this switch
This is only scratching the surface of this subclass. You can add background colors the same way as images. You could add haptics to make the user feel the click of the button. You might also want to play with making some properties and methods public and private. Play around and see what else you can do.
// // UIToggleButton.swift // Toggle buttons and switches // // // An exercise file for iOS Development Tips Weekly // by Steven Lipton (C)2018, All rights reserved // For videos go to http://bit.ly/TipsLinkedInLearning // For code go to http://bit.ly/AppPieGithub // import UIKit class UIToggleButton:UIButton{ var isOn:Bool = false{ didSet{ updateDisplay() } } var onImage:UIImage! = nil{ didSet{ updateDisplay() } } var offImage:UIImage! = nil{ didSet{ updateDisplay() } } func updateDisplay(){ if isOn { if let onImage = onImage{ setBackgroundImage(onImage, for: .normal) } } else { if let offImage = offImage{ setBackgroundImage(offImage, for: .normal) } } } override func endTracking(_ touch: UITouch?, with event: UIEvent?) { super.endTracking(touch, with: event) isOn = !isOn } }
// // ViewController.swift // Toggle buttons and switches // // // An exercise file for iOS Development Tips Weekly // by Steven Lipton (C)2018, All rights reserved // For videos go to http://bit.ly/TipsLinkedInLearning // For code go to http://bit.ly/AppPieGithub // import UIKit class ViewController: UIViewController { @IBOutlet weak var basicSwitch: UISwitch! var toggleButton: UIToggleButton! //Action when button recieves touchUpInside @IBAction func didToggleButton(_ sender: UIToggleButton) { if sender.isOn { sender.setTitle("ON", for: .normal) } else { sender.setTitle("OFF", for: .normal) } } //Configure the toggle button for this app. func configureToggleButton()-> UIToggleButton{ let toggleButton = UIToggleButton() toggleButton.offImage = UIImage(named: "Toggle off") toggleButton.onImage = UIImage(named: "Toggle on") toggleButton.setTitleColor(.black, for: .normal) toggleButton.titleLabel?.font = UIFont(name: "Gill Sans", size: 36) toggleButton.setTitle("OFF", for: .normal) toggleButton.addTarget(self, action: #selector(didToggleButton(_:)), for: .touchUpInside) return toggleButton } //Add the button to the layout centered on the view. func addToggleButton(to view:UIView) { toggleButton = configureToggleButton() view.addSubview(toggleButton) toggleButton.translatesAutoresizingMaskIntoConstraints = false var constraints = [NSLayoutConstraint]() constraints += [NSLayoutConstraint.init(item: toggleButton, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerXWithinMargins, multiplier: 1.0, constant: 0)] constraints += [NSLayoutConstraint.init(item: toggleButton, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerYWithinMargins, multiplier: 1.0, constant: 0)] constraints += [NSLayoutConstraint.init(item: toggleButton, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 128)] constraints += [NSLayoutConstraint.init(item: toggleButton, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 96)] NSLayoutConstraint.activate(constraints) } override func viewDidLoad() { super.viewDidLoad() addToggleButton(to: view) } }
Note:You can find a video of this tip here on LinkedIn Learning
If there’s one thing in auto layout that drives me nuts it is laying out for landscape different than portrait on an iPad. Phones are easy since the class sizes are different for portrait and landscape. For all phones, if I have a compact height, I know I’m in landscape. But iPads are regular width, regular height, and so I have no clue to programmatically change the layout. Let’s look at one way to handle this.
Download the example file. Take a look at the storyboard.
I have a large number of buttons and an image. I want in landscape buttons to the side, while in portrait buttons below. I set this up with nested stack views, so by switching between vertical and horizontal axis I can set my layout appropriately.
Head to the ViewController
code. What I’m going to do is look at the device rotation and decide what orientation I should be in. I need to generate orientation notifications. At the bottom of the code, in viewWillAppear
, add a start method from UIDevice
‘s singleton current
:
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
As s good developer I’ll shut this down when not on this page in viewWillDisappear
.
UIDevice.current.endGeneratingDeviceOrientationNotifications()
Before I layout out the subview, I’ll change the orientation of the stack view and use a different image. I made methods for the different layouts. I’ll tell the system which layout I want in viewWillLayoutSubviews
:
First I’ll get the orientation from UIDevice
.
let orientation = UIDevice.current.orientation
The orientation is an enum
. There’s two cases for landscape, and I’ll put both in an if
statement, going to landscape if true and portrait if false:
if (orientation == .landscapeLeft)||(orientation == .landscapeRight) { landscapeLayout() } else { //portrait portraitLayout() }
This works for both iPhones and iPads. I got an iPad Pro 11″ and an iPhone XR warmed up and ready to go on the simulator. I’ll choose to run the iPad Pro in the simulator.and run the app.
Once it comes up , I’ll click simulator and drag the app into the iPhone XR then run it
Rotating them with Command-Right Arrow changes the layout.
But there’s a problem on an iPad. Add an extra pane from Safari. Multitasking lets me add another pane, and those don’t layout the way I’d like.
The key here is class sizes. With one exception, all the multitasking panes on an iPad act like portrait on the phone as compact width, regular height. The exception, the 2/3 pane in landscape, acts like an iPad with regular width, regular height. If you want this behavior, you don’t have to do anything, But in portrait I always want my portrait layout.
I’ll first check if this is a regular height view. In landscape, I’m on an iPad if that is true, so I’lll add some code there. I’ll then check for a compact width. I have a smaller multipane if that is true. If false, we have landscape. I’ll add this:
if isRegularHeight && isCompactWidth{ portraitLayout() } else { landscapeLayout() }
Run this. You get the portrait layout in 2/3.
Change the pane size to 1/2 or 1/3, and it goes back to portrait.
Rotate to landscape and it will stay portrait
.
Try with the phone and it works too. With a small bit of code, you can get rotation and multipane layout for your iPad that reflects your iPhone layout.
You can find the download of the completed project here. You can find a video of this tip here on LinkedIn Learning
// // ViewController.swift // Multipane rotation - Demonstrates landscape layout on iPad // // // A exercise file for iOS Development Tips Weekly // by Steven Lipton (C)2018, All rights reserved // For videos go to http://bit.ly/TipsLinkedInLearning // For code go to http://bit.ly/AppPieGithub // import UIKit class ViewController: UIViewController { //Outlets @IBOutlet weak var pizzaImageView: UIImageView! @IBOutlet weak var rootStackView: UIStackView! // Computed properties for convenience var isRegularHeight:Bool{ return view.traitCollection.verticalSizeClass == .regular } var isCompactWidth:Bool{ return view.traitCollection.horizontalSizeClass == .compact } // The layout methods func landscapeLayout(){ rootStackView.axis = .horizontal pizzaImageView.image = UIImage(named:"Pizza Landscape") } func portraitLayout(){ rootStackView.axis = .vertical pizzaImageView.image = UIImage(named: "Pizza Portrait") } override func viewWillLayoutSubviews() { let orientation = UIDevice.current.orientation if (orientation == .landscapeLeft)||(orientation == .landscapeRight) { if isRegularHeight && isCompactWidth{ portraitLayout() } else { landscapeLayout() } } else { //portrait portraitLayout() } } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) UIDevice.current.beginGeneratingDeviceOrientationNotifications() } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) UIDevice.current.endGeneratingDeviceOrientationNotifications() } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } }
One powerful, yet sometimes rightfully feared programming technique is recursion. Many do not even use it, some don’t know what it is, but when working with hierarchies, you really have to use it. Let’s take a look at recursion and apply it to a common UI case where you need it.
Download and open the exercise file. I put a playground inside the project to show you some simple recursion. The classic definition of recursion is
recursion: See Recursion
Recursion is calling yourself. However that’s not quite accurate. The most basic recursion is this.
func factorial(_ n:Int)->Int{ return factorial(n-1) * n }
This calls itself with one less for n
every time. You’ll notice that Xcode has problems with this, and does warn you when you do not have a well-formed recursion. Something like this will not run truly forever, but will run until you run out of memory or out of range of your type. The definition of a well formed recursion is better written
Recursion: See Recursion until something happens.
There is some condition where we stop the recursion. For example, I can add a condition that if n
is greater or equal to 1, the function returns a value of 1.
func factorial(_ n:Int)->Int{ if n >= 1{ return factorial(n - 1) * n } else{ return 1 } }
The error disappears. We can run the function for 5 and get 120.
This is great, but where it is the most practical is traveling hierarchy or tree structures. Recursion is basically making a smaller identical problem and solving that, until there’s no more problems. One place you can see this is in a really messy storyboard I wrote.
I’ve got 20 buttons and I decide they would look better with a drop shadow and rounded corners, like I’ve shown you in other tips. I have them in three stack views and in two of the stack views I have stack views of one to three buttons.
I’m not going to make 20 outlets and change all of them. Instead, I’ll use the view hierarchy and some recursion.
Got to the view controller, and you’ll see I stubbed out code for us. There a’s function findButton
which takes two parameters: a view and a level, which I defaulted to 0
func findButton(view:UIView,level:Int = 0){ print("Subview count: \(view.subviews.count) Level: \(level)") }
Right now all it does is print out the number of subviews and level. I’ll change this into a recursive function to search for Buttons to change.
I’ll start with the limiting factor. This time if a view has subviews, this is the subview count is greater than 0, I’ll look at each subview for more subviews.
if view.subviews.count >= 0{ }
I could do a recursive look at that array of subviews, but for ordered items I tend to use a for. Iteration simplifies tracing the recursion, and is a bit less intense on memory in large cases. So I’ll add this
for subview in view.subviews{ }
However within this loop, I will check each subview for subviews recursively, indicating I’ve dropped a level in the tree by incrementing the level
findButton(view: subview,level:level + 1)
At some point, I’ll hit a view without subviews. The if
will fail, and that’s a single view. For this I’ll check for a button
if let button = view as? UIButton{ }
And I’ll set the button to have the level for title, as a rounded button with a shadow.
button.setTitle("Level \(level)", for: .normal) button.setTitleColor(.white, for: .normal) viewFormat(view: button)
We’ll call this in view did layout subviews so rounded corners work right.
override func viewDidLayoutSubviews() { findButton(view: self.view) }
Change your simulator to an iPad pro 9.7″. Run this and you get rounded buttons with shadows.
While I changed everything, This technique can find a specific view if you have some identifying information. Of course it is not limited to views, but nested arrays or dictionaries can be traversed easily using a recursive method like this.
// // ViewController.swift // RecursionDemo // // // A exercise file for iOS Development Tips Weekly // by Steven Lipton (C)2018, All rights reserved // For videos go to http://bit.ly/TipsLinkedInLearning // For code go to http://bit.ly/AppPieGithub // import UIKit class ViewController: UIViewController { func viewFormat(view:UIView){ view.layer.shadowOpacity = 0.9 view.layer.shadowRadius = 5.0 view.layer.shadowColor = UIColor.black.cgColor view.layer.shadowOffset = CGSize(width: 3, height: 3) if view.frame.width > view.frame.height{ view.layer.cornerRadius = view.frame.height / 2.0 } else { view.layer.cornerRadius = view.frame.width / 2.0 } } func findButton(view:UIView,level:Int = 0){ print("Subview count: \(view.subviews.count) Level: \(level)") if view.subviews.count >= 0{ for subview in view.subviews{ findButton(view: subview,level: level + 1) } } if let button = view as? UIButton{ button.setTitle("Level \(level)", for: .normal) button.setTitleColor(.white, for: .normal) viewFormat(view: button) } } override func viewDidLayoutSubviews() { findButton(view: self.view) } override func viewDidLoad() { super.viewDidLoad() } }
It’s sometimes nice to add a little sound to your applications, and even better to add a voice. Outside Siri, you can use a cool and simple AVFoundation
API to make iOS devices talk. Let’s learn how to use the Speech synthesizer.
Download the starter file. I’ve made an app for you that has a text field, a button and a segue to a table view we’ll discuss a little later.
Open the ViewController,swift
file. Find the speak
action. We’ll need to get a string from the text field. So add the following code to get the text if the field has a value:
if let speechText = speakingText.text{ }
You’ll need two objects: a synthesizer object and a speech utterance. But before you do, make sure you have imported AVFoundation
.
import AVFoundation
I’ll add the synthesizer with the AVSpeechSynthesizer
class
let synth = AVSpeechSynthesizer() let speech = AVSpeechUtterance(string: speechText) synth.speak(speech)
That’s it. Set your simulator to iPhone 8 Plus. Build and run the app. Type in some phrase, like
I could use a good pizza right now.
Tap the Speak! Button.
Your simulator speaks!!
Of course that’s not the only method you have at your disposal. You can easily change the voice as well. Voices are a localization in pronunciation. Some languages will have more than one available pronunciation. I set up the app to list all the voices and their language combinations on a table view and then the user can select the voice and accent.
You’ll use the AVSpeechSynthesisVoice
class for this. There is a class method that returns an array of voices. I’ll use this for my model for the table
let voices = [AVSpeechSynthesisVoice.speechVoices()] //list of voices
Head down the code to the tableview.dequeue
method. Remove what I have their now. All the voices have, names, Identifiers and languages. Names are only indication if they are male or female voices until you hear them. You use the name property. Since these properties are strings. we can assign the name directly to the text label and the language to the detail text label of the table cell.
cell.textLabel?.text = voice.name cell.detailTextLabel?.text = voice.language
When a language is selected I’ll send that voice back from the table and dismiss the table. I set up the delegation for you. In didselect
, uncomment out the two lines. If you don’t understand what I’m doing here take a look at the delegates and data sources course in the library.
Back in ViewController
, you’ll see I have the delegate method defined to transfer the voice to a variable. All you need to do is assign the voice with the action to the variable.
Speech.voice = speechVoice
Now run. Tap voice and then pick another voice. Try the voice.
Play around with this. Of course, voices are more for localization than entertainment. There’s more to the API, but with these basics you can make your app talk.
// // ViewController.swift // SpeechSynthesizer // // A exercise file for iOS Development Tips Weekly // by Steven Lipton (C)2018, All rights reserved // For videos go to http://bit.ly/TipsLinkedInLearning // For code go to http://bit.ly/AppPieGithub // import UIKit import AVFoundation class ViewController: UIViewController, VoiceTableViewControllerDelegate { @IBOutlet weak var speakingText: UITextField! var speechVoice = AVSpeechSynthesisVoice() @IBAction func speak(_ sender: UIButton) { //<-- Add code here if let speechText = speakingText.text{ let synth = AVSpeechSynthesizer() let speech = AVSpeechUtterance(string: speechText) speech.voice = speechVoice synth.speak(speech) } } //delegate function func didSelectVoice(voice: AVSpeechSynthesisVoice) { speechVoice = voice title = voice.name } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. title = "Default Voice" } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "voices"{ let vc = segue.destination as! VoiceTableViewController vc.delegate = self } } }
// // VoiceTableViewController.swift // SpeechSynthesizer // // A exercise file for iOS Development Tips Weekly // by Steven Lipton (C)2018, All rights reserved // For videos go to http://bit.ly/TipsLinkedInLearning // For code go to http://bit.ly/AppPieGithub // // A table view controller for selecting a language // import UIKit import AVFoundation protocol VoiceTableViewControllerDelegate{ func didSelectVoice(voice:AVSpeechSynthesisVoice) } class VoiceTableViewController:UITableViewController { let voices = AVSpeechSynthesisVoice.speechVoices() //list of voices var delegate:VoiceTableViewControllerDelegate! = nil override func numberOfSections(in tableView: UITableView) -> Int { return 1 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return voices.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) let row = indexPath.row let voice = voices[row] cell.textLabel?.text = voice.name cell.detailTextLabel?.text = voice.language return cell } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let row = indexPath.row delegate.didSelectVoice(voice: voices[row]) navigationController?.popViewController(animated: true) } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } }
You’ll often want default settings that users can change. iOS provides the settings bundle for storing this information. Let’s look at how to use the settings bundle.
Download the starter example file. I created a project which eventually will display the user’s default pizza. We’ll finish the code to add that pizza.
To add a settings menu Go to File>New>File on the menu. In the filter type sett. Under resources you’ll see the settings bundle. You’d Select it and click Next, then Save it into the project group. I’ve already set one up for you, so you can hit Cancel.
Find the settings bundle, click the arrow to find the root.plist. The third item is Preference Items. Open that.
You’ll find several items already here. There are several types of controls here. Item 3 is sliders. I find them difficult to use in settings. Delete the slider by clicking to the left of the arrow to select and hit Delete on your keyboard. Right-click on Item 1. A drop down asks you for what control here and select multi value.
Expand out all the Items. Each has different values. The Type is the type of control, the Title is the title showing in the settings app, and Identifier is your key for accessing this value. I set up the text field and the toggle switch. Let set up together the Multi-value.
For title make it Size. For Identifier, make it size_preference. I’m going to leave the default value blank, but click on default value to get the + icon. Click the icon and you’ll see two more attributes not here yet. Titles and Values. You need both for a multi value. Click on Titles first. In title, hit the plus four times. For item names add Personal, Small, Medium and Large. Go back to default value, and hit the plus again to add the values, which pair with the titles. Add one value and change the type to number. Add three more items.
You’ve now added the multi value. Head to ViewController.swift I’ve added some code here for you, but we’ll now complete it.
Userdefaults.standard
is a singleton for the user defaults settings. I’ll make a shorter identifier for UserDefaults.standard
called defaults
:
let defaults = UserDefaults.standard
User defaults has several methods with the key as the identifier from the Root.plist. These methods, based on type retrieves the data from the settings page. The switch uses a Bool
, and the text uses an optional string.
Let’s add the MutiValue. We made the value an integer, so we can use integer here, then add it to the string.
let size = defaults.integer(forKey: "size_preference") text += String(format: "%i\" ", size)
Head down to viewDidLoad
.
We need to tell the app we have a settings bundle. Add the following code to register the settings bundle with a dictionary with a string key.
UserDefaults.standard.register(defaults: [String:Any]())
While the code we have will work, if a user changes default settings while using the app, that will not show up in the app. We need a key-value observer to tell us when there is a change, and to update our favorite pizza.
NotificationCenter.default.addObserver(self,selector: #selector(defaultsChanged),name: UserDefaults.didChangeNotification,object: nil)
For the selector, I set the defaultsChanged
method to an IBAction
, so I can use that.
I have a call here for defaultsChanged
already. So I’ll set the simulator to iPhone 8 plus and run. We get a boring pizza. Head over to settings and scroll to the bottom to find ad select the favorite pizza settings. I’ll add some topping, make a pan pizza and make the pizza medium. Go back to the app and you find your pizza there.
That’s all you need to get settings into your app for defaults and other important user preferences that don’t change often. For more on the attributes, check out the pdf chart I placed in the assets folder of the exercise file. There’s a lot you can do.
There’s been some great changes to playgrounds in Xcode 10 that will make your code even easier to prototype than ever before. Let’s take a look at some of the changes.
In Xcode 10 open a new single view playground, and save it as Xcode10Playground. If you are familiar with the Xcode 9 playground single view template, you’ll notice a difference. Instead of setting up my view in viewDidLoad
, this sets up my view in loadView
. This will prevent a bug that happens in iPad playgrounds and sometime Xcode where the superview is still nil
in viewDidLoad
.
When working with view controllers and setting up your user interface, use this pattern of adding all your views to a superview, then assigning the superview to the view property of the view controller. Also make sure you set the frame of the superview, which you can get from the nativeBounds
property of UIScreen
. nativeBounds
will be in portrait. Since I use iPad playgrounds most of the time, this code in my view controller sets the frame correctly for iPad landscape, and adds a label, which I’ll have to layout and set properties elsewhere. Strangely it doesn’t always work, But I’m still trying to find a consistent solution. This works more often than others.
A big feature change to macOS is dark mode, and Xcode supports this. I can show you the really cool features a little easier in dark mode. So I’ll go to Editor, select Theme and select Presentation Dark.
If you look to the right of the line numbers, you’ll see some lines.
That’s for code folding, which is a new feature to Xcode Playgrounds. If you hover over the line you get a set of arrows giving you an idea what you are about to fold. I can fold
Loadview and MyViewController by clicking on the fold line.
I’ll click on the arrow and they unfold again. If you don’t have code folding or want to turn it off. There is a setting in Xcode preferences. Go to text editing and check on or off Code folding ribbon according to your preference.
You’ll also notice that circle with a play button next to playground page on the line numbers. The biggest change is a selective run mode.
I can still run using the run on the bottom toolbar, but you can also run using this button. I can click on the new play button, open the live view and my playground runs.
I’ll stop the playground and close the live view. Notice what happens when I drag the play button up and down the code. It skips over the class.
This will only let you run code that makes sense. Nested statements or classes, essentially anything you can fold, will not run.
Fold up the view controller and give your self some space above it to play with this. Add a variable a with a random number using my new favorite API. iOS 12 has a simple to use random function, which you use as as class method of the type your are working with.
var a = Int.random(in: 1...10)</pre>
Then make a simple for loop from one to five. I'll skip a variable for it with a underscore.
for _ in 1...5{ }
There a beautiful new random number generator in iOS 12. random(in:)
is a class function you give a range to, working with several types of numbers. I’ll add a random number of 1 to 10 to the sum of a
:
a += Int.random(in: 1...10)
Then print this
print(a)
I’ll then move my mouse over the line numbers, and you can see that assignment, the print, and the end of the for loop can run. I’ll run the assignment and I get a random number on the right.
The line number is no longer highlighted.
Run at the end of the loop and you get five iterations of the summation, which you can see if I open up a viewpoint.
I also have more lines not highlighted, telling me I ran those already. Change the assignment line by adding 1 to it, and the run resets. with a blue number
I’ll run to the print this time. I get my result in the panel and the code runs. Note the random number is frozen so you can step through this code. That’s different than pressing the run button on the bottom which changes your random numbers. If you are testing with data that might change every execution, selective execution make life much easier.
While it does not run within a class, this selective run can help you prototype your code, letting you change and run as you step through an algorithm. Write the code like I did here and then make it a function or class. You can trace and build your code easily when you need to see every statement run.