Files
skills/ios-application-dev/references/navigation-patterns.md
shihao 6487becf60 Initial commit: add all skills files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 16:52:49 +08:00

4.9 KiB

Navigation Patterns

iOS navigation patterns guide covering Tab navigation, Navigation Controller, and modal presentation.

Tab-Based Navigation

For apps with 3-5 main sections:

class AppTabBarController: UITabBarController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let homeNav = UINavigationController(rootViewController: HomeVC())
        homeNav.tabBarItem = UITabBarItem(
            title: "Home",
            image: UIImage(systemName: "house"),
            selectedImage: UIImage(systemName: "house.fill")
        )
        
        let searchNav = UINavigationController(rootViewController: SearchVC())
        searchNav.tabBarItem = UITabBarItem(
            title: "Search",
            image: UIImage(systemName: "magnifyingglass"),
            tag: 1
        )
        
        let profileNav = UINavigationController(rootViewController: ProfileVC())
        profileNav.tabBarItem = UITabBarItem(
            title: "Profile",
            image: UIImage(systemName: "person"),
            selectedImage: UIImage(systemName: "person.fill")
        )
        
        viewControllers = [homeNav, searchNav, profileNav]
    }
}

Tab Bar Best Practices

Principle Description
Limit count Maximum 5 tabs, use More for additional
Always visible Tab bar stays visible at all navigation levels
State preservation Preserve navigation state when switching tabs
Icon choice Use SF Symbols, provide selected/unselected states

Navigation Controller

Use large titles for root views:

class ListViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "Items"
        navigationController?.navigationBar.prefersLargeTitles = true
        navigationItem.largeTitleDisplayMode = .always
    }
    
    func pushDetail(_ item: Item) {
        let detail = DetailViewController(item: item)
        detail.navigationItem.largeTitleDisplayMode = .never
        navigationController?.pushViewController(detail, animated: true)
    }
}

Navigation Bar Configuration

class CustomNavigationController: UINavigationController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let appearance = UINavigationBarAppearance()
        appearance.configureWithDefaultBackground()
        
        navigationBar.standardAppearance = appearance
        navigationBar.scrollEdgeAppearance = appearance
        navigationBar.compactAppearance = appearance
    }
}

Navigation Bar Buttons

override func viewDidLoad() {
    super.viewDidLoad()
    
    navigationItem.rightBarButtonItem = UIBarButtonItem(
        image: UIImage(systemName: "plus"),
        style: .plain,
        target: self,
        action: #selector(addItem)
    )
    
    navigationItem.rightBarButtonItems = [
        UIBarButtonItem(systemItem: .add, primaryAction: UIAction { _ in }),
        UIBarButtonItem(systemItem: .edit, primaryAction: UIAction { _ in })
    ]
}

Modal Presentation

Sheet Presentation

func presentEditor() {
    let editorVC = EditorViewController()
    let nav = UINavigationController(rootViewController: editorVC)
    
    editorVC.navigationItem.leftBarButtonItem = UIBarButtonItem(
        systemItem: .cancel, target: self, action: #selector(dismissEditor)
    )
    editorVC.navigationItem.rightBarButtonItem = UIBarButtonItem(
        systemItem: .done, target: self, action: #selector(saveAndDismiss)
    )
    
    if let sheet = nav.sheetPresentationController {
        sheet.detents = [.medium(), .large()]
        sheet.prefersGrabberVisible = true
        sheet.prefersScrollingExpandsWhenScrolledToEdge = false
    }
    
    present(nav, animated: true)
}

Custom Detent (iOS 16+)

if let sheet = nav.sheetPresentationController {
    let customDetent = UISheetPresentationController.Detent.custom { context in
        return context.maximumDetentValue * 0.4
    }
    sheet.detents = [customDetent, .large()]
}

Full Screen Presentation

func presentFullScreen() {
    let vc = FullScreenViewController()
    vc.modalPresentationStyle = .fullScreen
    vc.modalTransitionStyle = .coverVertical
    present(vc, animated: true)
}

Presentation Styles

Style Usage
.automatic System default (usually sheet)
.pageSheet Card-style, parent view visible
.fullScreen Full screen cover
.overFullScreen Full screen with transparent background
.popover iPad popover

Navigation Best Practices

  1. Back gesture - Ensure edge swipe back always works
  2. State restoration - Use UIStateRestoring to save navigation stack
  3. Depth limit - Avoid more than 4-5 navigation levels
  4. Cancel button - Modal views must provide a cancel option
  5. Save confirmation - Show confirmation dialog for unsaved changes

UIKit, SF Symbols, and Apple are trademarks of Apple Inc.