如何通过代码统计Storyboard中UIViewController关联的Segue数量
Hey there! I've got a couple of solid approaches to count the number of segues linked to a specific UIViewController in a Storyboard. Let's break them down based on your use case:
Storyboards are just XML files under the hood, so we can parse them directly using standard XML tools. Here's a Swift implementation that works on macOS or iOS (if you're building an internal tool):
import Foundation func countSeguesForViewController(in storyboardPath: String, viewControllerID: String) -> Int { // Load the storyboard XML data guard let xmlData = try? Data(contentsOf: URL(fileURLWithPath: storyboardPath)), let xmlDoc = try? XMLDocument(data: xmlData, options: [.nodePreserveWhitespace]) else { print("Failed to load or parse storyboard file") return 0 } // Locate the target view controller's parent scene node using its storyboard ID let sceneXPathQuery = "//scene[@sceneID]/objects/viewController[@storyboardIdentifier='\(viewControllerID)']/../.." guard let targetScene = xmlDoc.nodes(forXPath: sceneXPathQuery).first else { print("No view controller found with ID: \(viewControllerID)") return 0 } // Extract the unique scene ID associated with the view controller guard let sceneID = targetScene.attribute(forName: "sceneID")?.stringValue else { print("Could not retrieve scene ID for target view controller") return 0 } // Count all segues where the source matches our target scene ID let segueXPathQuery = "//segue[@source='\(sceneID)']" guard let segues = try? xmlDoc.nodes(forXPath: segueXPathQuery) else { return 0 } return segues.count } // Example usage let storyboardPath = "/Users/you/Projects/YourApp/Base.lproj/Main.storyboard" let targetVCID = "ProfileViewController" let segueCount = countSeguesForViewController(in: storyboardPath, viewControllerID: targetVCID) print("Total segues linked to \(targetVCID): \(segueCount)")
How this works:
- Each view controller in a Storyboard lives inside a
<scene>node with a uniquesceneID - Segues have a
sourceattribute that references thesceneIDof their originating view controller - We use XPath queries to first find the target scene, then count all segues pointing back to it
xmllint(快速验证) If you just need a one-off count without writing a full program, use xmllint (preinstalled on macOS) to parse the XML directly from terminal:
# Replace these values with your own STORYBOARD_PATH="/Users/you/Projects/YourApp/Base.lproj/Main.storyboard" TARGET_VC_ID="SettingsViewController" # Step 1: Get the scene ID of the target view controller SCENE_ID=$(xmllint --xpath "//scene[@sceneID]/objects/viewController[@storyboardIdentifier='$TARGET_VC_ID']/../@sceneID" "$STORYBOARD_PATH" | grep -o 'sceneID="[^"]*"' | cut -d'"' -f2) # Step 2: Count all segues originating from that scene SEGUE_COUNT=$(xmllint --xpath "count(//segue[@source='$SCENE_ID'])" "$STORYBOARD_PATH") echo "Segue count for $TARGET_VC_ID: $SEGUE_COUNT"
Pro Tip:
If your view controller doesn't have a storyboard ID, you can use its custom class name instead. Replace the first XPath query with:
SCENE_ID=$(xmllint --xpath "//scene[@sceneID]/objects/viewController[@customClass='$TARGET_VC_CLASS']/../@sceneID" "$STORYBOARD_PATH" | grep -o 'sceneID="[^"]*"' | cut -d'"' -f2)
If you're building a tool for your team (not for the App Store), you can use Xcode's private frameworks to directly load and inspect the Storyboard. Note: This API is unsupported and may break with Xcode updates.
Here's an Objective-C example:
#import <Foundation/Foundation.h> #import <IBFoundation/IBFoundation.h> int main(int argc, const char * argv[]) { @autoreleasepool { NSString *storyboardPath = @"/Users/you/Projects/YourApp/Base.lproj/Main.storyboard"; NSString *targetVCID = @"HomeViewController"; // Load the storyboard using Xcode's private IBStoryboard class IBStoryboard *storyboard = [[IBStoryboard alloc] initWithContentsOfFile:storyboardPath error:nil]; if (!storyboard) { NSLog(@"Failed to load storyboard"); return 1; } // Find the target view controller by its ID IBObjectContainer *container = storyboard.objectContainer; IBUIViewController *targetVC = nil; for (IBUIViewController *vc in [container objectsOfClass:[IBUIViewController class]]) { if ([vc.storyboardIdentifier isEqualToString:targetVCID]) { targetVC = vc; break; } } // Count outgoing segues if (targetVC) { NSLog(@"Segue count: %lu", (unsigned long)[targetVC.outgoingSegues count]); } else { NSLog(@"View controller not found"); } } return 0; }
To compile this, you need to link against Xcode's private framework:
clang -o segueCounter segueCounter.m -framework Foundation -framework IBFoundation -F/Applications/Xcode.app/Contents/SharedFrameworks/
Key Notes:
- For nested view controllers (e.g., inside a UINavigationController), make sure your XPath or API query targets the correct child view controller node
- Always prefer the XML parsing approach for production tools since it's stable and doesn't rely on private APIs
内容的提问来源于stack exchange,提问作者Reimond Hill

