0 Comments
Update note: This tutorial has been updated for Xcode 9, iOS 11, and Swift 4 by Nicholas Sakaimbo. The original tutorial was written by Matthijs Hollemans. If you want to learn about storyboards, you’ve come to the right place! In the first part of this series, you covered the basics of using Interface Builder to create and connect various view controllers, along with how to make custom table view cells directly from the storyboard editor. In this second and final part of this storyboards tutorial series, we’ll cover segues, static table view cells, the Add Player scene, and game picker scene! We’ll start where we left off last tutorial, so open your project from last time, or download the example code from the previous tutorial here. OK, now you’ll dive into some of the other cool features in storyboards! Introducing SeguesIt’s time to add more view controllers to the storyboard. You’re going to create a scene to add new players to the app. Open Main.storyboard and drag a Bar Button Item into the right slot of the navigation bar on the Players scene. In the Attributes inspector change System Item to Add, and set its Style to Bordered. When the user taps this button, you want the app to pop up a new modal scene for entering details of a new player. Drag a new Table View Controller into the canvas to the right of the Players scene. Remember you can double-click the canvas to zoom out so you have more room to work. With the new Table View Controller selected, choose Editor\Embed in\Navigation Controller. Here’s the trick: Select the + button you just added on the Players scene and ctrl-drag to the new Navigation Controller. Release the mouse button and select Present Modally from the popup menu: This places a new arrow between the Players scene and the Navigation Controller: This type of connection is known as a segue (pronounce: seg-way) and represents a transition from one scene to another. The storyboard connections you’ve seen so far were relationships and they described one view controller containing another. A segue, on the other hand, changes what’s on the scene. Segues are triggered by taps on buttons, table view cells, gestures, and so on. The cool thing about using segues is you don’t have to write any code to present the new scene, or hook up your buttons to IBAction methods. What you just did, dragging from the Bar Button Item to the next scene, is enough to create the transition. (Note: If your control already has an IBAction connection, the segue overrides it.) Build and run the app and tap the + button. A new table view will slide up from the bottom. This is called a modal segue. The new scene completely obscures the previous one. The user cannot interact with the underlying scene until they close the modal scene first. Later you’ll see “show” segues that push a new scene onto a Navigation Controller’s navigation stack. The new scene isn’t very useful yet – you can’t even close it to go back to the main scene. That’s because segues only go one way – so while it can go from the Players scene to this new one, it can’t go back. Storyboards provide the ability to ‘go back’ with something called an unwind segue, which you’ll implement next. There’s three main steps:
First, open Main.storyboard and select the new Table View Controller scene. Change the title of the scene to Add Player (by double-clicking in the navigation bar). Next, add two Bar Button Items, one to each side of the navigation bar. In the Attributes inspector, set the System Item property of the left button to Cancel, and the right button to Done. Next, add a new file to the project using the Cocoa Touch Class template – name it PlayerDetailsViewController and make it a subclass of Now you can finally create the unwind segue. Open PlayersViewController.swift, add the following extension above your // MARK: - IBActions extension PlayersViewController { @IBAction func cancelToPlayersViewController(_ segue: UIStoryboardSegue) { } @IBAction func savePlayerDetail(_ segue: UIStoryboardSegue) { } }
Finally, open Main.storyboard and hook up the Cancel and Done buttons to their respective action methods. Ctrl-drag from the bar button to the exit object above the view controller then pick the correct action name from the popup menu: Note the name you gave the cancel method. When you create an unwind segue, the list will show all unwind methods (i.e. ones with the signature Build and run the app, tap the + button, and test the Cancel and Done buttons. A lot of functionality for very little code! Storyboards Static CellsWhen you’re finished with this section, the Add Player scene will look like this: That’s a grouped table view, but you don’t have to create a data source for this table. You can design it directly in the storyboard — no need to write Open Main.storyboard and select the table view in the Add Player scene. In the Attributes inspector change Content to Static Cells. Change Style from Plain to Grouped and give the table view 2 sections. Note: When you change the value of the Sections attribute, the editor will clone the existing section. (You can also select a specific section in the Document Outline on the left and duplicate it.) The finished scene will have only one row in each section, so select two cells in each of the sections and delete them using the Document Outline. Next, select the top table view section (from the Document Outline) and set the header value to Player Name. Drag a new Text Field into the cell for this section. Stretch out its width and remove its border so you can’t see where the text field begins or ends. Set the Font to System 17.0 and uncheck Adjust to Fit. You’re going to make an outlet for this text field on the Select the new text field and ctrl-drag to the top of Creating outlets for views on your table cells is exactly the kind of thing I said you shouldn’t try with prototype cells, but for static cells it’s OK. There will be only one instance of each static cell so it’s perfectly acceptable to connect their subviews to outlets on the view controller. Select the second section of the table view in the Document Outline and delete the placeholder “Section-2” text in the Header field in the Attributes Inspector. Set the Style of the static cell in the second section to Right Detail. This gives you a standard cell style to work with. Change the label on the left to read Game by double clicking it and give the cell a Disclosure Indicator accessory. Just as you did for The final design of the Add Player scene looks like this: Note: The scenes you’ve designed so far in this storyboard all have the width and height of the 4.7-inch screen of the iPhone 7, which is 667 points tall. Obviously, your app should work properly with different screen sizes, and you can preview these sizes within your Storyboard. Open the Assistant Editor from the toolbar, and use the jump bar to select Preview. At the bottom left of the assistant editor, click the + symbol to add new screen sizes to preview. To remove a screen size, select it and hit the Delete key. For the Ratings app, you don’t have to do anything fancy. It only uses table view controllers and they automatically resize to fit the screen space. When you do need to support different layouts for different sized devices, you’ll use Auto Layout and Size Classes. Build and run the app. You’ll notice the Add Player scene is still blank! When you use static cells, your table view controller doesn’t need a data source. Since you used an Xcode template to create the Open PlayerDetailsViewController.swift and delete everything from the following line down (except for the class closing bracket): override func viewDidLoad() { super.viewDidLoad() } Build and run the app. Now the new scene displays the static cells, and all without writing a line of code. One more thing about static cells: they only work in Note: If you’re building a scene with a lot of static cells — more than can fit in the visible frame — you can scroll through them in Interface Builder with the scroll gesture on the mouse or trackpad (2 finger swipe). You can’t always avoid writing code altogether though, even for a table view of static cells. When you dragged the text field into the first cell, you probably noticed it didn’t fit completely. There’s a small margin of space around the text field. The user can’t see where the text field begins or ends, so if they tap in the margin and the keyboard doesn’t appear, they’ll be confused. To avoid this, let a tap anywhere inside the row bring up the keyboard. Open PlayerDetailsViewController.swift and add the following extension to the end of the file: // MARK: - UITableViewDelegate extension PlayerDetailsViewController { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if indexPath.section == 0 { nameTextField.becomeFirstResponder() } } } If the user taps the first cell, the app should activate the text field. There’s only one cell in the section so you only need to test for the section index. Making the text field the first responder will automatically bring up the keyboard. Note: when adding a delegate method, or overriding a view controller method, just start typing the method name (without preceding it with “func”), and you’ll be able to select the correct method from the available list. You should also set the Selection for the cell to None in the storyboard Attributes Inspector, otherwise the row appears highlighted when the user taps in the margin around the text field. All right, that’s the design of the Add Player scene. Now to actually make it work! The Add Player Scene at WorkFor now you’ll ignore the Game row and just let users enter the name of the player. When the user taps the Cancel button the scene should close and the data entered should be lost. This already works with the unwind segue. When the user taps Done, you should create a new
Note: Never call Open PlayerDetailsViewController.swift, and add the following property at the top of the class: // MARK: - Properties var player: Player? Next, add the following method below your IBOutlets definitions: // MARK: - Navigation override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "SavePlayerDetail", let playerName = nameTextField.text { player = Player(name: playerName, game: "Chess", rating: 1) } }
Open Main.storyboard, find the Add Player scene in the Document Outline and select the unwind segue tied to Next, open PlayersViewController and replace the unwind segue method @IBAction func savePlayerDetail(_ segue: UIStoryboardSegue) { guard let playerDetailsViewController = segue.source as? PlayerDetailsViewController, let player = playerDetailsViewController.player else { return } // add the new player to the players array players.append(player) // update the tableView let indexPath = IndexPath(row: players.count - 1, section: 0) tableView.insertRows(at: [indexPath], with: .automatic) } This obtains a reference to the You could have invoked Build and run the app, you should now be able to add new players to the list! PerformanceSince you have several view controllers in the storyboard, you might be wondering about performance. Loading a whole storyboard at once isn’t a big deal. The storyboard doesn’t instantiate all the view controllers right away – only the initial view controller is immediately loaded. Since your initial view controller is a Tab Bar Controller, the two view controllers it contains are also loaded (the Players scene from the first tab and the scene from the second tab). The other view controllers are not instantiated until you segue to them. When you close these view controllers they’re immediately deallocated, so only the actively used view controllers are in memory. To see this in practice, open PlayerDetailsViewController.swift and add the following below your // MARK: - Initializers required init?(coder aDecoder: NSCoder) { print("init PlayerDetailsViewController") super.init(coder: aDecoder) } deinit { print("deinit PlayerDetailsViewController") } You’re overriding Build and run the app. Open the Add Player scene. You should see the When you close the Add Player scene, either by tapping Cancel or Done, you should see the The Game Picker SceneTapping the Game row in the Add Player scene should open a new scene to let the user pick a game from a list. This means you’ll add another table view controller, although this time you’re going to push it on the navigation stack rather than show it modally. Open Main.storyboard and drag a new Table View Controller into the canvas. Next, select the Game table view cell in the Add Player scene (be sure to select the entire cell, not one of the labels) and ctrl-drag to the new table view controller to create a segue between them. Select Show under Selection Segue in the popup, not Accessory Action. Select this new segue and give it the identifier PickGame in the Attributes Inspector. Select the new table view controller in the Document Outline and in the Attributes Inspector, name this scene Choose Game. Next, select the prototype table view cell and set the Style of the prototype cell to Basic, and give it the reuse identifier GameCell. That’s all you need to do for the design of this scene: Add a new Swift file to the project, using the Cocoa Touch Class template and name it GamePickerViewController, subclass of UITableViewController. Next, open Main.storyboard and select the Choose Game Scene. In the Identity Inspector, set its Custom Class to Now you’ll give this new scene some data to display. Open GamePickerViewController.swift, and add replace everything in the class definition with the following: // MARK: - Properties var games = [ "Angry Birds", "Chess", "Russian Roulette", "Spin the Bottle", "Texas Hold'em Poker", "Tic-Tac-Toe" ] Next, add the following extension to the end of the file: // MARK: - UITableViewDataSource extension GamePickerViewController { override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return games.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "GameCell", for: indexPath) cell.textLabel?.text = games[indexPath.row] return cell } } Here you’re setting up the data source to use the Build and run the app and tap the Game row. The new Choose Game scene will slide into view. Tapping the rows won’t do anything yet, but because this scene is presented on the navigation stack, you can always tap the back button to return to the Add Player scene. This is pretty cool, huh? You didn’t have to write any code to invoke this new scene. You just ctrl-dragged from the static table view cell to the new scene and that’s it. The only code you wrote was to populate the contents of the table view, which is typically something more dynamic rather than a hardcoded list. Currently, this new scene isn’t very useful since it doesn’t send any data back. You’ll have to add a new unwind segue. In var selectedGame: String? { didSet { if let selectedGame = selectedGame, let index = games.index(of: selectedGame) { selectedGameIndex = index } } } var selectedGameIndex: Int? Whenever Next, replace override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "GameCell", for: indexPath) cell.textLabel?.text = games[indexPath.row] if indexPath.row == selectedGameIndex { cell.accessoryType = .checkmark } else { cell.accessoryType = .none } return cell } This sets a checkmark on the cell containing the name of the currently selected game. Small gestures such as these will be appreciated by users of the app. Next, add the following extension below the // MARK: - UITableViewDelegate extension GamePickerViewController { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) // Other row is selected - need to deselect it if let index = selectedGameIndex { let cell = tableView.cellForRow(at: IndexPath(row: index, section: 0)) cell?.accessoryType = .none } selectedGame = games[indexPath.row] // update the checkmark for the current row let cell = tableView.cellForRow(at: indexPath) cell?.accessoryType = .checkmark } } This method is called whenever the user taps a row. First deselect the row after it was tapped (makes it fade from the gray highlight color back to white). Finally, remove the checkmark from the previously selected cell, and puts it on the just tapped cell. Build and run the app. Tap the name of a game and its row will get a checkmark. Tap the name of another game and the checkmark moves to that row. The scene should close when the user taps a row but that doesn’t happen yet because you haven’t hooked up an unwind segue. Sounds like a great next step! Open PlayerDetailsViewController.swift, and add the following below the var game: String = "Chess" { didSet { detailLabel.text = game } } This property will hold the selected game so it can be stored in the Still in PlayerDetailsViewController.swift, add the following extension above your // MARK: - IBActions extension PlayerDetailsViewController { @IBAction func unwindWithSelectedGame(segue: UIStoryboardSegue) { if let gamePickerViewController = segue.source as? GamePickerViewController, let selectedGame = gamePickerViewController.selectedGame { game = selectedGame } } } This method is executed once the user selects a game from the Choose Game Scene and updates both the label on screen and the game property based on the game selected. The unwind segue also pops Open Main.storyboard, ctrl-drag from the tableview cell to the Exit as you did before, and choose unwindWithSelectedGame: from the popup list: In the Attributes Inspector give the new unwind segue the Identifier SaveSelectedGame. Build and run the app. Create a new player, select the player’s game row and choose a game. The game is not updated on the Add Player scene! Unfortunately, the unwind segue method is performed before Open GamePickerViewController, and add the following method below your property definitions: override func prepare(for segue: UIStoryboardSegue, sender: Any?) { guard segue.identifier == "SaveSelectedGame", let cell = sender as? UITableViewCell, let indexPath = tableView.indexPath(for: cell) else { return } let index = indexPath.row selectedGame = games[index] } The sender parameter of Build and run the app and select the game, it’ll update the player’s game details! Next, you need to change Open PlayerDetailsViewController.swift, replace override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "SavePlayerDetail", let playerName = nameTextField.text { player = Player(name: playerName, game: game, rating: 1) } } When you complete the Add Player scene and tap done, the list of players will now update with the correct game. One more thing – when you choose a game, return to the Add Player scene, then try to choose a game again, the game you chose before should have a checkmark by it. The solution is to pass the selected game stored in Still in PlayerDetailsViewController.swift, add the following to the end of if segue.identifier == "PickGame", let gamePickerViewController = segue.destination as? GamePickerViewController { gamePickerViewController.selectedGame = game } Note you now have two Awesome. You now have a functioning Choose Game scene! One More Thing: Storyboard ReferencesOpen Main.storyboard and zoom out. You’ll see the complete project has several scenes. This bird’s-eye-view of your project is nice, but you can imagine a large number of scenes could become unwieldy to navigate. In addition, multiple people working in the same storyboard file can lead to nasty merge conflicts in version control. To mitigate these issues, you can use Storyboard References to split up an existing storyboard into one or more smaller storyboard files, separated by logical areas of functionality. Let’s see how this works by refactoring all view controllers from the To do this, click + drag to select all view controllers starting from Next, select Editor\Refactor to Storyboard to consolidate the selected scenes into their own storyboard. When prompted for a filename, type “Players” and hit “Save”. You’ll see a new Players.storyboard file in your project containing all the scenes you just built. In addition, back in Main.storyboard, you’ll see the tab bar controller now points to the Build and run the project to confirm everything works as before. Voila! Refactoring storyboards is easy and could save you down the road – it’s a great tool to have in your toolbelt. Where To Go From Here?Here is the final Ratings example project with all of the code from the above tutorial. Congratulations, you now know the basics of using the Storyboard Editor, and can create apps with multiple view controllers transitioning between each other with segues! Editing multiple view controllers and their connections to each other in one place makes it easy to visualize your app as a whole. One item you didn’t add as part of this tutorial is the ability to change the player rating, but you now know enough about storyboards to implement this yourself for practice. :] You’ve also seen how easy it is to customize table views and table view cells. Static cells make it easy to set up an interface without implementing all the data source methods. If you want to learn more about storyboards, check out our book the iOS Apprentice. If you have any questions or comments on this tutorial or on storyboards in general, please join the forum discussion below! The post Storyboards Tutorial for iOS: Part 2 appeared first on Ray Wenderlich. Storyboards Tutorial for iOS: Part 2 syndicated from http://ift.tt/2uHrXAJ via Tumblr Storyboards Tutorial for iOS: Part 2 Update note: This tutorial has been updated for Xcode 9, iOS 11, and Swift 4 by Nicholas Sakaimbo. The original tutorial was written by Matthijs Hollemans. Storyboards are an exciting feature first introduced in iOS 5 that save time building user interfaces for your apps. Storyboards allow you to prototype and design multiple view controller views within one file. Before Storyboards you had to use XIB files and you could only use one XIB file per view ( The following image shows you what a storyboard looks like, and it’s similar to the storyboard you’ll build during this tutorial: You may not know what the app does but you can see its scenes and how they’re related. Storyboards have a number of advantages:
In this tutorial you’re going to build a sample app to create a list of players and show you the games they play and their skill rating. In the process, you’ll learn common tasks which can be accomplished using in storyboards. Getting StartedOpen Xcode and create a new project. Use the Single View Application template as the starting point. Fill in the template options as follows, click Next and then Create:
Once created, the main Xcode window should look like the following: The new project consists of three files, AppDelegate.swift, ViewController.swift, and the star of this tutorial: Main.storyboard. Under Deployment Info > Device Orientation in the General project settings, set Devices to iPhone. Since this is a portrait-only app, uncheck the Landscape Left and Landscape Right options. Open Main.storyboard in the project navigator to view it in the Interface Builder editor: The official storyboard terminology for a view controller is “scene”, but you can use the terms interchangeably. A scene represents a view controller in the storyboard. Here you see a single view controller containing an empty view. The arrow pointing to the view controller from the left indicates it’s the initial view controller to be displayed for this storyboard. Designing a layout in the storyboard editor is done by dragging controls from the Object Library (see bottom-right corner) into your view controller. You’ll notice the default scene size is for a 4.7-inch screen. Xcode enables Auto Layout and Size Classes by default for storyboards. Auto Layout and Size Classes allow you to make flexible user interfaces that can easily resize, which is useful for supporting the various sizes of iPhones and iPads. To change the scene size to another device, click the button at the bottom left of the storyboard. You’ll then be able to select from the full range of supported device sizes, ranging from the iPad Pro (12.9-inch) to the iPhone 4S (3.5-inch), in both portrait and landscape orientations. For this tutorial, we’ll leave the default scene size – iPhone 7 – unchanged, so make sure to switch it back if you’ve toggled through a couple of different device sizes. Xcode will automatically re-size existing and new scenes added to the storyboard for the currently-selected device size. To get a feel for how the storyboard editor works, drag some controls from the Object Library into the blank view controller: As you drag controls in, they should show up in the Document Outline on the left: The storyboard shows the contents of all your scenes. Currently there’s only one scene in your storyboard, but over the course of this tutorial you’ll add several others. There’s a miniature version of this Document Outline above the scene called the Dock: The Dock shows the top-level objects in the scene. Each scene has at least a View Controller object, a First Responder object, and an Exit object. It can potentially have other top-level objects as well. The Dock is convenient for making connections to outlets and actions. If you need to connect something to the scene, you can simply drag to its icon in the Dock.
Note: You probably won’t use the First Responder very much. This is a proxy object referring to whatever object has first responder status at any given time. As an example, you can hook up the Touch Up Inside event from a button to First Responder’s
cut: selector. If at some point a text field has input focus then you can press that button to make the text field, which is now the first responder, cut its text to the pasteboard.
Build and run the app, it should look exactly like what you designed in the editor (yours may look different than the screenshot below): The single view controller you defined was set as the Initial View Controller – but how did the app load it? Open AppDelegate.swift to find the answer: import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } The The secret’s in the Info.plist file. Open Info.plist in the Project Navigator and you’ll see the following: Storyboard apps use the You can also see this in the Project Settings under the General tab and Deployment Info section: Now to create the real Ratings app with several view controllers. Just Add It To My TabThe Ratings app you’re about to build has a tabbed interface with two scenes. With a storyboard it’s easy to create tabs. Open Main.storyboard and delete the scene you worked with earlier. This can be done by clicking on View Controller in the Document Outline and pressing the delete key. Drag a Tab Bar Controller from the Object Library into the canvas. You may want to maximize your Xcode window first, because the Tab Bar Controller comes with two view controllers attached and you’ll need some room to maneuver. You can zoom in and out by double-clicking the canvas, or set the zoom scale by ctrl-clicking the canvas and selecting the zoom level. The new Tab Bar Controller comes pre-configured with two additional view controllers – one for each tab. The container Relationship is represented by the arrows between the Tab Bar Controller and the view controllers it contains. An embed Relationship in particular is signified by the icon seen below in the middle of the arrow body.
Note: If you want to move the Tab Bar Controller and its attached view controllers as a group, zoom out, then
⌘ -click or click and drag to select multiple scenes. This makes it possible to move them around together. (Selected scenes have a thin blue outline.)
Drag a label into the first view controller (currently titled “Item 1”), double click it, and give it the text “First Tab”. Next, drag a label into the second view controller (“Item 2”) and give it the text “Second Tab”. This allows you to see something happen when you switch between the tabs. Build and run the app. You’ll see something similar to this in the console:
Fortunately, the error is pretty clear here – you never set an entry point, meaning you didn’t set the Initial View Controller after you deleted the previous scene. To fix this, select the Tab Bar Controller, go to the Attributes Inspector and check Is Initial View Controller. In the canvas, an arrow now points at the Tab Bar Controller: Now when you run the app,
Note: To change the initial view controller, you can also drag the arrow between view controllers.
Xcode comes with a template for building a tabbed app (called the Tabbed Application template). You could have used it, but it’s good to know how this works so you can create a Tab Bar Controller by hand if you have to.
Note: If you connect more than five scenes to the Tab Bar Controller, it automatically gets a More… tab when you run the app. Pretty neat!
Adding a Table View ControllerThe two scenes currently attached to the Tab Bar Controller are both Click on the first view controller in the Document Outline to select it, then delete it. Drag a new Table View Controller into the canvas where the previous scene used to be: Next, you want to place the Table View Controller inside a navigation controller. First, select the Table View Controller. Next, choose Editor\Embed In\Navigation Controller from Xcode’s menubar. This adds another controller to the canvas: You could have dragged in a Navigation Controller from the Object Library and embedded the table view, but this Embed In command is a nice time saver for a common action. Since the Navigation Controller is also a container view controller (just like the Tab Bar Controller), it has a relationship arrow pointing to the Table View Controller. You can also see these relationships in the Document Outline: Notice embedding the Table View Controller gave it a navigation bar. Interface Builder automatically put it there because this scene will now be displayed inside the Navigation Controller’s frame. It’s not a real To connect these two new scenes to the Tab Bar Controller, ctrl-drag from the Tab Bar Controller to the Navigation Controller. When you let go, a small popup menu appears. Choose the Relationship Segue – view controllers option: This creates a new relationship arrow between the two scenes. This is also an embed Relationship as you saw with the other controllers contained by the Tab Bar Controller. The Tab Bar Controller has two embed relationships, one for each tab. The Navigation Controller itself has an embed Relationship with the Table View Controller. When you made this new connection, a new tab was added to the Tab Bar Controller, simply named “Item”. For this app, you want this new scene to be the first tab, so drag the tabs around to change their order: Build and run the app to try it out. The first tab now contains a table view inside a navigation controller. Before you put some actual functionality into this app, you need to clean up the storyboard a little. You’ll name the first tab “Players” and the second “Gestures”. You don’t change this on the Tab Bar Controller itself, but in the view controllers connected to these tabs. As soon as you connect a view controller to a Tab Bar Controller, it’s given a Tab Bar Item object which you can see in the Document Outline or the bottom of the scene. Use this Tab Bar Item to configure the tab’s title and image seen on the Tab Bar Controller. Select the Tab Bar Item inside the Navigation Controller, and in the Attributes inspector set its Title to Players: Next, rename the Tab Bar Item for the second tab to Gestures the same way you did above. A well-designed app should also put icons on these tabs. The resources for this tutorial contains a subfolder named Images. Drag that folder into the Assets.xcassets subfolder in the project. Open Main.storyboard, in the Attributes inspector for the Players Tab Bar Item, choose the Players image. Next, give the Gestures Tab Bar Item the image Gestures. A view controller embedded inside a Navigation Controller has a Navigation Item used to configure the navigation bar. Select the Navigation Item for the Table View Controller in the Document Outline and change its title in the Attributes inspector to Players. . Notice the Scene title in the Document Outline now changes to Players
Note: Alternatively, you can double-click the navigation bar and change the title there. You should double-click the simulated navigation bar in the Table View Controller, not the actual Navigation Bar object in the Navigation Controller.
Build and run the app. Now marvel at your pretty tab bar, created without writing a single line of code! Prototype CellsPrototype cells allow you to easily design a custom layout for your table view cells directly within the storyboard editor. The Table View Controller comes with a blank prototype cell. Click the cell to select it and in the Attributes inspector set the Style option to Subtitle. This immediately changes the appearance of the cell to include two labels.
Note: With so much stackable content on a storyboard, it can sometimes be difficult to click on exactly what you want. If you have trouble, there’s several options. One is you can select the item in the Document Outline to the left of the canvas. The second is a handy hotkey: hold control + shift and click on the area you’re interested in. A popup will appear allowing you to select any element directly under your cursor.
If you’ve used table views before and created your own cells by hand, you may recognize this as the Set the Accessory attribute to Disclosure Indicator and the Identifier to PlayerCell. All prototype cells must have a reuse identifier so you can refer to them in code. In addition, set the cell’s Selection to None.
Note: The cell’s Selection attribute is set to None to prevent the user from editing an existing item by tapping the PlayerCell. Although you won’t be adding this functionality in this tutorial, you will have enough knowledge by the end of Part 2 to implement this feature into the sample project for additional practice.
Build and run the app, nothing has changed. That’s not so strange: you still have to make a data source for the table so it knows which rows to display. You’re going to do that next. Add a new file to the project. Choose the Cocoa Touch Class template under iOS/Source. Name the class PlayersViewController and make it a subclass of UITableViewController. Uncheck Also create XIB file. Choose the Swift language and hit Next followed by Create. Open Main.storyboard and select the Table View Controller (make sure you select the actual view controller and not one of its views). In the Identity inspector, set its Class to PlayersViewController. This is an essential step for hooking up a scene from the storyboard with your custom view controller subclass. Don’t forget this or your class won’t be used! Now when you run the app the table view controller from the storyboard is an instance of the The table view should display a list of players, so now you’ll create the main data model for the app – an array containing Replace the code in Player.swift with the following: import Foundation struct Player { // MARK: - Properties var name: String? var game: String? var rating: Int } There’s nothing special going on here. Next, create a new file using the Swift File template named SampleData. Replace the contents of SampleData.swift with the following: import Foundation final class SampleData { static func generatePlayersData() -> [Player] { return [ Player(name: "Bill Evans", game: "Tic-Tac-Toe", rating: 4), Player(name: "Oscar Peterson", game: "Spin the Bottle", rating: 5), Player(name: "Dave Brubeck", game: "Texas Hold 'em Poker", rating: 2) ] } } Here you’ve defined a static method on Next, open PlayersViewController.swift and replace the contents of the file with the following: import UIKit class PlayersViewController: UITableViewController { // MARK: - Properties var players = SampleData.generatePlayersData() } You could have set up the sample data in Now you’ve an array full of // MARK: - UITableViewDataSource extension PlayersViewController { override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return players.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "PlayerCell", for: indexPath) let player = players[indexPath.row] cell.textLabel?.text = player.name cell.detailTextLabel?.text = player.game return cell } } The method Build and run the app, the table view has players in it! It takes just a few lines of code to use these prototype cells. I think that’s just great!
Note: In this app you’re using only one prototype cell. If your table needs to display different kinds of cells you can add additional prototype cells to the storyboard. Make sure to give each cell its own re-use identifier!
Designing Your Own Prototype CellsUsing a standard cell style is fine for most apps, but for this app you want to add an image on the right-hand side of the cell showing the player’s rating. Having an image view in that spot is not supported by the standard cell styles, so you’ll have to make a custom design. Open Main.storyboard, select the prototype cell in the table view, and in Attributes inspector, set its Style attribute to Custom. The default labels now disappear. First make the cell a little taller. Either change the Row Height value in the Size inspector (after checking Custom) or drag the handle at the bottom of the cell. Make the cell 60 points high. Drag two Label objects from the Objects Library into the cell and place them roughly where the standard labels were previously. Just play with the font and colors in the Attributes Inspector and pick something you like. Set the text of the top label to Name and the bottom label to Game. Select both the Name and Game labels in the Document Outline using Command+click, and choose Editor\Embed In\Stack View.
Note: Stack views were introduced in in iOS 9 and are brilliant for easily laying out collections of views. You can find out more about stack views in our UIStackView Tutorial.
Drag an Image View into the cell and place it on the right, next to the disclosure indicator. In the Size Inspector, make it 81 points wide and 35 points high. Set its Content Mode to Center (under View in the Attributes inspector) so whatever image you put into this view is not stretched. Command + click the Stack View and Image View in the Document Outline to select both of them. Choose Editor\Embed in\Stack View. Xcode will create a new horizontal stack view containing these two controls. Select this new horizontal stack view, and in the Attributes Inspector, change the Alignment to Center and the Distribution to Equal Spacing. Now for some simple auto layout for this control. At the bottom right of the storyboard, click the Pin icon: Change the top constraints to Top: 0, Right: 20, Bottom: 0 and Left: 20. Make sure the four red pointers to the values are highlighted as in the picture. Click Add 4 Constraints at the bottom of the popover window. If your stack view has orange constraints, it is misplaced. To fix this, select the horizontal stack view and choose Editor\Resolve Auto Layout Issues\Update Frames (in the Selected Views section of the menu). The stack view should position itself correctly and the orange constraint errors go away. To position the image view within the stack view, select the image view in the Document Outline and choose Editor\Resolve Auto Layout Issues\Add Missing Constraints (in the Selected Views section of the menu). You may see a small arrow highlighted in red in the Document Outline indicating unresolved layout issues with the stack view. Click on this arrow and click on the red circle to view Xcode’s suggested auto-fix. Selecting Change Priority for either the Name or Game labels’ hugging priority should silence this warning. The final design for the prototype cell looks something like this: Because this is a custom designed cell, you can no longer use
Note: You could use
Tags in this situation, however tags do not provide the type of object which the compiler can check at runtime hence you have to use type casting and checking during runtime which you should avoid if at all possible. For this reason, tags were no longer considered prudent to teach in this situation.
Using a Subclass for the CellAdd a new file to the project, with the Cocoa Touch Class template. Name it PlayerCell and make it a subclass of UITableViewCell. Don’t check the option to create a XIB, as you already have the cell in your storyboard. Next, add the following to the // MARK: - IBOutlets @IBOutlet weak var gameLabel: UILabel! @IBOutlet weak var nameLabel: UILabel! @IBOutlet weak var ratingImageView: UIImageView! These Next, add the following property below the // MARK: - Properties var player: Player? { didSet { guard let player = player else { return } gameLabel.text = player.game nameLabel.text = player.name ratingImageView.image = image(forRating: player.rating) } } Whenever the Next, add the following method below func image(forRating rating: Int) -> UIImage? { let imageName = "\(rating)Stars" return UIImage(named: imageName) } This returns a different star image depending on the provided rating. Next, open Main.storyboard, select the prototype cell
Note: You gave this class the same name as the reuse identifier – they’re both called
PlayerCell – but that’s only because I like to keep things consistent. The class name and reuse identifier have nothing to do with each other, so you can name them differently if you wish.
Finally, connect the labels and the image view to these outlets. Navigate to the Connections Inspector in the storyboard and then select the Player Cell from either the canvas or Document Outline. Drag from the nameLabel Outlet in the Connections inspector to the Name label object in either the Document Outline, or the canvas. Repeat for gameLabel and ratingImageView. Note: You should hook up the controls to the table view cell, not to the view controller! You see, whenever your data source asks the table view for a new cell with This means there will be more than one instance of Now you’ve hooked up the properties, you can simplify the data source code a bit. Open PlayersViewController.swift, and change override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "PlayerCell", for: indexPath) as! PlayerCell let player = players[indexPath.row] cell.player = player return cell } That’s more like it. You now cast the object you receive from Build and run the app. Hmm, that doesn’t look quite right – the cells appear to be squished. You did change the height of the prototype cell, but the table view doesn’t take that into consideration. There’s two ways to fix it: you can change the table view’s Row Height attribute, or implement the Note: You would use Open Main.storyboard, in the Size inspector of the Table View, set Row Height to 60: Build an run the app. That’s much better isn’t it!
Note: If you changed the cell height by dragging its handle rather than typing in the value, the table view’s Row Height property was automatically changed too. So it may have worked correctly for you the first time around.
Where To Go From Here?Click here to download the full source code for the project up to this point. Check out part two of this tutorial, where we’ll cover segues, static table view cells, the Add Player scene, a game picker scene, and the full downloadable example project for this tutorial! If you felt lost at any point during this tutorial, you also might want to brush up on the basics with our iOS Apprentice series. In that series, you’ll learn the foundational knowledge you need as an iOS developer from the ground up — perfect for complete beginners, or those looking to fill in some gaps. If you have any questions or comments on this tutorial or on storyboards, please join the forum discussion below! The post Storyboards Tutorial for iOS: Part 1 appeared first on Ray Wenderlich. Storyboards Tutorial for iOS: Part 1 syndicated from http://ift.tt/2uHrXAJ via Tumblr Storyboards Tutorial for iOS: Part 1 |
About UsMobilith aim is to become mobile industry's top news source for mobile trends, smartphones, android, apple, apps, and technology. In this digital age, everything from cooking to saving money have been made easier thanks to apps for smartphones. We allow everyone to learn and take advantage of the best available app. |