纯代码创建UIViewController:子视图不显示、手势无响应问题
Hey there! Let's break down the issues you're facing one by one and fix them up—pure code UI can be tricky at first, but these are all common gotchas when moving away from Storyboards.
1. Your StackView isn't showing up (and causing BAD_ACCESS when enabling constraints)
The biggest problem here is that stkMenu is a computed property, not a stored one. Every time you access stkMenu, you're creating a brand new UIStackView instance. So when you add it to the view, then try to activate constraints later, you're referencing a different object than the one you added—leading to that BAD_ACCESS crash, and the original stack view never gets properly laid out.
Fix this by making stkMenu a lazy stored property instead. Lazy properties only get created once when first accessed, which is exactly what we want here.
2. Swipe gesture isn't working
Your swipe gesture's target is set to self.view, but the dismissVC method belongs to your MenuViewController class. The target should be self instead. Also, you might want to add a left swipe option (users often expect both directions to close menus, but that's optional if you only want right swipes).
3. Layout fixes for the StackView
Once we fix the stack view property, we can safely activate those constraints you commented out. Your original constants (50 top/bottom, 20 leading/trailing) will give you a padded layout—if you want it to truly fill the whole view, just remove those constants.
Here's the fixed version of your code with all these changes:
import UIKit class MenuViewController: UIViewController { // Changed to lazy stored property instead of computed property lazy var stkMenu: UIStackView = { let s = UIStackView() // No need to set frame here—auto layout handles positioning s.axis = .vertical s.distribution = .fillEqually s.alignment = .fill s.translatesAutoresizingMaskIntoConstraints = false return s }() var butSettings: UIButton = { let b = UIButton() b.backgroundColor = UIColor.clear b.setTitle("SETTINGS", for: .normal) b.showsTouchWhenHighlighted = true b.titleLabel?.font = UIFont(name: "Roboto-Bold", size: 20) b.setTitleColor(UIColor.netScoreGreen(), for: .normal) b.translatesAutoresizingMaskIntoConstraints = false return b }() var butAbout: UIButton = { let b = UIButton() b.backgroundColor = UIColor.clear b.setTitle("ABOUT", for: .normal) b.showsTouchWhenHighlighted = true b.titleLabel?.font = UIFont(name: "Roboto-Bold", size: 20) b.setTitleColor(UIColor.netScoreGreen(), for: .normal) b.translatesAutoresizingMaskIntoConstraints = false return b }() override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = UIColor.black // Add buttons to the stack view stkMenu.addArrangedSubview(butSettings) stkMenu.addArrangedSubview(butAbout) // Add stack view to the main view view.addSubview(stkMenu) // Activate constraints safely now NSLayoutConstraint.activate([ stkMenu.topAnchor.constraint(equalTo: view.topAnchor, constant: 50), stkMenu.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -50), stkMenu.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), stkMenu.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20) ]) // Fix swipe gesture target let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(dismissVC)) swipeRight.direction = .right view.addGestureRecognizer(swipeRight) // Optional: Add left swipe for extra flexibility let swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(dismissVC)) swipeLeft.direction = .left view.addGestureRecognizer(swipeLeft) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } @objc func dismissVC() { self.dismiss(animated: true, completion: nil) } }
Key Changes Recap:
- Lazy Stack View: Ensures only one instance is created, so we're working with the same object when adding it to the view and applying constraints. We also removed the frame initialization since auto layout handles positioning.
- Gesture Target Fix: Swipe gestures now target
self(the view controller) instead ofself.view, so thedismissVCmethod is correctly triggered. - Active Constraints: No more BAD_ACCESS crashes, and the stack view will be laid out exactly as you defined.
Once this is working, you can add tap handlers to your buttons like this:
// Add this inside the butSettings closure b.addTarget(self, action: #selector(openSettings), for: .touchUpInside) // Then add the handler method @objc func openSettings() { dismiss(animated: true) { // Add your settings navigation logic here (present or push from your Storyboard VC) } }
Also, when presenting this menu from your Storyboard view controllers, set modalPresentationStyle to .overFullScreen for a clean overlay effect, and consider adding a custom slide-in transition to match the side menu behavior!
内容的提问来源于stack exchange,提问作者Jonno

