You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

纯代码创建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 of self.view, so the dismissVC method 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 06:53:12