地图绘图应用中如何从其他类访问ViewController视图的bounds?
Hey there! Let's tackle your problem step by step—first for your specific map drawing app, then cover the general patterns that work for any iOS scenario where you need to access a view's bounds from another class.
Since you need the map view's bounds to determine drawing layers, the key here is to get real-time access to the bounds (since they can change on rotation, layout updates, etc.) while avoiding tight coupling or memory leaks.
One straightforward approach is to pass a weak reference of your map view to your drawing class when you initialize it. Here's how that might look in Swift:
// Your drawing class (e.g., MapDrawer.swift) class MapDrawer { // Use weak to prevent retain cycles weak var mapView: UIView? init(mapView: UIView) { self.mapView = mapView } func updateDrawingLayers() { guard let mapBounds = mapView?.bounds else { return } // Use mapBounds to calculate your drawing layers here print("Current map bounds: \(mapBounds)") } } // In your ViewController class MapViewController: UIViewController { @IBOutlet weak var mapView: UIView! var mapDrawer: MapDrawer? override func viewDidLoad() { super.viewDidLoad() // Initialize the drawer with a reference to your map view mapDrawer = MapDrawer(mapView: mapView) } // Call this when you need to update drawing (e.g., after layout changes) override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() mapDrawer?.updateDrawingLayers() } }
This way, every time the map view's bounds change (like when the screen rotates), you can trigger an update to your drawing layers using the latest bounds.
Here are the most common, maintainable patterns for this scenario:
1. Pass a Weak Reference or Bounds Value During Initialization
If the other class is created directly in your ViewController, you can either:
- Pass a weak reference to the target view (as shown above) to get real-time bounds
- Pass the initial bounds value if you only need a one-time measurement
Just remember to use weak for references to avoid retain cycles!
2. Use a Delegate Pattern
If you need the other class to be notified when the bounds change, a delegate is a clean, decoupled way to do this. Example:
// Define a delegate protocol protocol BoundsUpdateDelegate: AnyObject { func didUpdateBounds() -> CGRect } // Your helper class class DrawingManager { weak var delegate: BoundsUpdateDelegate? func processDrawing() { // If you need to fetch bounds on demand guard let bounds = delegate?.didUpdateBounds() else { return } // Do drawing logic } } // Your ViewController conforms to the delegate class MainViewController: UIViewController, BoundsUpdateDelegate { @IBOutlet weak var targetView: UIView! let drawingManager = DrawingManager() override func viewDidLoad() { super.viewDidLoad() drawingManager.delegate = self } func didUpdateBounds() -> CGRect { return targetView.bounds } }
3. Use a Closure Callback
For simpler scenarios, a closure can be lighter than a delegate. You can pass a closure to the other class that lets it fetch the current bounds whenever needed:
class LayerCalculator { var fetchBounds: (() -> CGRect)? func calculateLayers() { guard let bounds = fetchBounds?() else { return } // Calculate layers using bounds } } // In your ViewController class MapViewController: UIViewController { @IBOutlet weak var mapView: UIView! let layerCalculator = LayerCalculator() override func viewDidLoad() { super.viewDidLoad() // Use [weak self] to avoid retain cycles layerCalculator.fetchBounds = { [weak self] in self?.mapView.bounds ?? .zero } } }
4. Notification Center (Use Sparingly)
If you need to broadcast bounds changes to multiple unrelated classes, you can use NotificationCenter. But be careful—it's easy to create hard-to-debug code with this approach, so use it only when other patterns don't fit:
// Define a custom notification name extension Notification.Name { static let viewBoundsUpdated = Notification.Name("ViewBoundsUpdated") } // In your ViewController class MainViewController: UIViewController { @IBOutlet weak var targetView: UIView! override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() // Post the notification with the bounds as the object NotificationCenter.default.post( name: .viewBoundsUpdated, object: targetView.bounds ) } } // In your other class class DrawingClass { init() { // Listen for the notification NotificationCenter.default.addObserver( self, selector: #selector(handleBoundsUpdate(_:)), name: .viewBoundsUpdated, object: nil ) } @objc func handleBoundsUpdate(_ notification: Notification) { if let bounds = notification.object as? CGRect { // Update your logic with the new bounds } } // Don't forget to remove the observer in deinit! deinit { NotificationCenter.default.removeObserver(self) } }
Key Notes to Remember
- Avoid strong references: Always use
weak selforweakreferences to views/ViewControllers to prevent memory leaks. - Timing matters: A view's bounds aren't finalized until
viewDidLayoutSubviewsruns—don't try to fetch them inviewDidLoadunless you're using auto layout and have fixed dimensions. - Minimize coupling: Prefer initialization parameters, delegates, or closures over Notification Center whenever possible—they make your code easier to test and maintain.
内容的提问来源于stack exchange,提问作者asapkot2

