TN3123: Refactoring your storyboard
Learn strategies and techniques for refactoring a single storyboard into multiple storyboards.
Overview
A storyboard is used to graphically lay out your app’s user interface. You use Xcode’s Interface Builder to specify your interface using scenes, segues between scenes, and controls used to trigger the segues. At design time, you configure the content of your view controllers visually, and Xcode saves the data needed to recreate that interface in a storyboard file in your app’s bundle.
Over time, as your application’s UI evolves into a larger set of view controller scenes, it may become a challenge to manage and maintain that storyboard. This can be a further challenge when you need to design it with multiple team members. Two team members modifying the same storyboard could potentially cause source control problems. Improve the storyboard management process by separating your single storyboard file into individual storyboards. Storyboards are connected together using storyboard references.
Benefits of using multiple storyboards
When a storyboard is refactored, you gain three benefits:
Improved readability - View controller connections become more explicit and the segue connections are easier to trace.
Reuse - A separate storyboard containing a view controller scene can be referenced and re-used in multiple parts of the app.
Easier to manage - A large team may work more efficiently when each member manages their own UI. Dividing your storyboard can help avoid merge conflicts when you use a source code version control system like Git.
Connect two storyboards using a storyboard reference
A view controller is connected to another by using a segue and a storyboard reference. This involves three stages.
Create a separate storyboard containing an individual view controller and give that view controller a unique identifier.
Back in the app’s Main storyboard, create a storyboard reference and set its storyboard and identifier created in step 1.
Create a segue from the source view controller and connect it to the storyboard reference.
Automatically refactor a storyboard using Xcode
For some UI elements Xcode helps refactor a single storyboard into multiple storyboards automatically.
To separate out a UITabBarController in iOS:
Open the storyboard.
Select the
UITabBarControllerscene.Select Menu: Editor > Refactor to Storyboard…
Name and save the new storyboard file to your project.
The new storyboard file will contain the UITabBarController. The child view controllers are separated and linked through storyboard references using unique identifiers. Storyboard references link one storyboard to another.
To separate out a NSTabViewController in macOS:
Open the storyboard.
Select the
NSTabViewControllerscene.Select Menu: Editor > Refactor to Storyboard…
Name and save the new storyboard file to your project.
The new storyboard file will contain the NSTabViewController. The child view controllers are separated and linked through storyboard references using unique identifiers.
Manually refactor a storyboard
If you choose to refactor your storyboard yourself, for example if one UIViewController (VC1) presents another (VC2), do the following:
Open the app’s Main storyboard file.
Select the view controller scene you want to present, and copy it.
Create new storyboard file: File > New > File… - choose Storyboard, name and save it “VC2”.
With the VC2 storyboard open, paste the copied view controller.
Select the copied view controller and select its Identify Inspector.
Name its Storyboard ID as “VC2_identifier”.
Go back to the Main storyboard.
Remove the view controller scene you previously copied.
Select Menu > Show Library
Drag and drop a new Storyboard Reference from the Library window into the Main storyboard.
Select the dropped Storyboard Reference and select its Attributes Inspector.
Set the Storyboard to “VC2”.
Set the Referenced ID “VC2_identifier”.
Connect this Storyboard Reference via segue from VC1.
[Image] [Image]
Load storyboards programmatically
Segues connecting two view controller scenes within the same storyboard load the view controller automatically. When a single storyboard is refactored into separate storyboards, storyboard references load them automatically as well. You can, however, load view controller scenes manually using code. You use storyboard instances to do that. Storyboard instances come in two flavors: NSStoryboard for macOS, and UIStoryboard for iOS. These APIs on both platforms are similar.
Load and open an NSWindowController programmatically with macOS
In macOS, an NSViewController is loaded separately, or through its NSWindowController.
let storyboard = NSStoryboard(name: "InspectorWindow", bundle: Bundle.main)
if let windController = storyboard.instantiateInitialController() as? NSWindowController {
windController.window!.makeKeyAndOrderFront(self)
}The window controller inside the “InspectorWindow” storyboard is to be set at the initial controller via “Is Initial Controller”.
[Image]
Load and present a UIViewController programmatically with iOS
Load by identifier:
let mainStoryboard: UIStoryboard = UIStoryboard(name: "DetailViewController", bundle: nil)
let viewController = mainStoryboard.instantiateViewController(withIdentifier: "DetailViewControllerID") as! UIViewController
self.present(viewController, animated: true, completion: nil)Load by initial view controller:
let mainStoryboard: UIStoryboard = UIStoryboard(name: "DetailViewController", bundle: nil)
if let viewController = mainStoryboard.instantiateInitialViewController() {
present(viewController, animated: true, completion: nil)
}The view controller inside the “DetailViewController” storyboard is to be set at the initial controller via “Is Initial Controller”.
Load and present a UIViewController programmatically with SwiftUI
If you are developing a SwiftUI app, and you want to integrate an existing UIViewController subclass and its storyboard:
struct DetailView: View {
var body: some View {
DetailViewControllerRepresentable()
}
}
final class DetailViewControllerRepresentable: UIViewControllerRepresentable {
typealias UIViewControllerType = DetailViewController
func makeUIViewController(context: Context) -> DetailViewController {
let storyboard = UIStoryboard(name: "DetailViewController", bundle: nil)
return storyboard.instantiateInitialViewController() as! DetailViewController
}
func updateUIViewController(_ uiViewController: DetailViewController, context: Context) { }
func makeCoordinator() -> Coordinator { Coordinator(self) }
class Coordinator: NSObject {
private let parent: DetailViewControllerRepresentable
init(_ parent: DetailViewControllerRepresentable) {
self.parent = parent
}
}
}
class DetailViewController: UIViewController { }Revision History
2022-03-29 First published.
See Also
Latest
TN3205: Low-latency communication with RDMA over ThunderboltTN3206: Updating Apple Pay certificatesTN3179: Understanding local network privacyTN3190: USB audio device design considerationsTN3194: Handling account deletions and revoking tokens for Sign in with AppleTN3193: Managing the on-device foundation model’s context windowTN3115: Bluetooth State Restoration app relaunch rulesTN3192: Migrating your iPad app from the deprecated UIRequiresFullScreen keyTN3151: Choosing the right networking APITN3111: iOS Wi-Fi API overviewTN3191: IMAP extensions supported by Mail for iOS, iPadOS, and visionOSTN3134: Network Extension provider deploymentTN3189: Managing Mail background traffic loadTN3187: Migrating to the UIKit scene-based life cycleTN3188: Troubleshooting In-App Purchases availability in the App Store