UISlider 在 UIStackView 中无法正常工作

UISlider in not working properly in UIStackView

让我解释一下我想要实现的目标。我想在 UIStackView 和 UIScrollView 中创建一个滑块和水平按钮列表,以便按钮可以滚动,然后将 UISlider 和 UIScrollview 放置在垂直 UIStackView 中。但问题是我可以滚动 UISlider 但按钮水平方向似乎卡住或与 UIScrolView 水平方向重叠并且它不起作用我尝试了一切但无法修复它。我想以编程方式进行。任何帮助都非常有用

class ViewController: UIViewController {
    private var stackView: UIStackView!
    private var stackViewNew: UIStackView!
    let x: CGFloat = 10
    let width: CGFloat = UIScreen.main.bounds.width - 20
    var y: CGFloat =  10
    var i = 0
    let step:Float=10 
    let scrollView: UIScrollView = {
       let v = UIScrollView()
       v.translatesAutoresizingMaskIntoConstraints = true
       v.frame =  CGRect(x: 0.0, y: 0.0, width: UIScreen.main.bounds.width, height: 200)
      return v
     }()
     private var stackViewFilter: UIStackView = {
        let v = UIStackView()
        v.translatesAutoresizingMaskIntoConstraints = true
        v.axis = .vertical
        v.backgroundColor = .black
        v.alpha = 0.8
        v.frame =  CGRect(x: 0.0, y: 0.0, width: UIScreen.main.bounds.width, height: 330)
        v.frame.origin = CGPoint(x:0 , y: UIScreen.main.bounds.height - 330)
        v.distribution = .equalSpacing
        v.spacing = 10.0
        return v
     }()

     let horizontalStackView : UIStackView = {
        let v = UIStackView()
        v.translatesAutoresizingMaskIntoConstraints = true
        v.axis = .horizontal
        v.backgroundColor = .systemPink
        v.frame = CGRect(x: 0.0, y: 0.0, width: UIScreen.main.bounds.width, height: 200)
        v.distribution = .equalSpacing
        v.spacing = 10.0
        return v
    }()
    
    override func viewDidLoad() {
       super.viewDidLoad()
       createBottomFilter()
    }
    
    @objc func createBottomFilter(){
        
 
        /*---------- Slider Section ----------*/
        let mySlider = UISlider(frame:CGRect(x: 40, y: 10, width: 200, height: 60))
        mySlider.minimumValue = 0
        mySlider.maximumValue = 100
        mySlider.isContinuous = true
        mySlider.tintColor = UIColor.green
        mySlider.addTarget(self, action: #selector(self.sliderValueDidChange(_:)), for: .valueChanged)
        mySlider.translatesAutoresizingMaskIntoConstraints = true
        
        UIView.animate(withDuration: 0.8) {
            mySlider.setValue(80.0, animated: true)
        }
    
        self.view.addSubview(stackViewFilter)
       
        stackViewFilter.addArrangedSubview(mySlider)
        stackViewFilter.addArrangedSubview(scrollView)
        
        self.view.addSubview(scrollView)

        mySlider.leadingAnchor.constraint(equalTo: stackViewFilter.leadingAnchor, constant: 8).isActive = true
        mySlider.trailingAnchor.constraint(equalTo: stackViewFilter.trailingAnchor, constant: 8).isActive = true
        mySlider.topAnchor.constraint(equalTo: stackViewFilter.bottomAnchor, constant: 10).isActive = true
      constraintBottom = mySlider.bottomAnchor.constraint(equalTo: scrollView.topAnchor, constant: 40)
        constraintBottom?.isActive = true
        
        scrollView.leftAnchor.constraint(equalTo: stackViewFilter.leftAnchor, constant: 0.0).isActive = true
        scrollView.topAnchor.constraint(equalTo: mySlider.bottomAnchor, constant: 8.0).isActive = true
        scrollView.rightAnchor.constraint(equalTo: stackViewFilter.rightAnchor, constant: 80.0).isActive = true
        scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 8.0).isActive = true
        
        // add the stack view to the scroll view
        scrollView.addSubview(horizontalStackView)
       
        horizontalStackView.leftAnchor.constraint(equalTo: scrollView.leftAnchor, constant: 0.0).isActive = true
        horizontalStackView.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: 0.0).isActive = true
        horizontalStackView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -30.0).isActive = true
                
        let b = generateButton(title: "Btn 1", selectedTitle: "Filter \(i)", iconName: "hellen", scaledToSize:CGSize(width: 90.0, height: 90.0))
        b.translatesAutoresizingMaskIntoConstraints = true

        let b1 = generateButton(title: "Btn 2", selectedTitle: "Filter \(i)", iconName: "hellen", scaledToSize:CGSize(width: 90.0, height: 90.0))
        b1.translatesAutoresizingMaskIntoConstraints = true

        let b2 = generateButton(title: "Btn 3", selectedTitle: "Filter \(i)", iconName: "hellen", scaledToSize:CGSize(width: 90.0, height: 90.0))
      
        b2.translatesAutoresizingMaskIntoConstraints = true

        let b3 = generateButton(title: "Btn 4", selectedTitle: "Filter \(i)", iconName: "hellen", scaledToSize:CGSize(width: 90.0, height: 90.0))
        b3.translatesAutoresizingMaskIntoConstraints = true

        let b4 = generateButton(title: "Btn 5", selectedTitle: "Filter \(i)", iconName: "hellen", scaledToSize:CGSize(width: 90.0, height: 90.0))
        b4.translatesAutoresizingMaskIntoConstraints = true

        let b5 = generateButton(title: "Btn 6", selectedTitle: "Filter \(i)", iconName: "hellen", scaledToSize:CGSize(width: 90.0, height: 90.0))
        b5.translatesAutoresizingMaskIntoConstraints = true

       
        horizontalStackView.addArrangedSubview(b)
        horizontalStackView.addArrangedSubview(b1)
        horizontalStackView.addArrangedSubview(b2)
        horizontalStackView.addArrangedSubview(b3)
        horizontalStackView.addArrangedSubview(b4)
        horizontalStackView.addArrangedSubview(b5)
        horizontalStackView.alignment = .center
        
    }
    
    @objc func generateButton(title: String, selectedTitle: String? = nil, iconName: String, scaledToSize newSize: CGSize) -> UIButton {
        let iconName: UIImage? = imageWithImage(UIImage(named: iconName), scaledToSize:CGSize(width: newSize.width, height: newSize.height))
    iconName?.withTintColor(.white)

        let button = UIButton.vertical(padding: 3)
        button.frame = CGRect(x: x, y: y, width: width, height: 80)
        button.setImage(iconName, for: .normal)
        button.layer.zPosition = 1
        button.setTitle(title, for: .normal)
        button.setTitle(selectedTitle, for: .selected)
        self.view.addSubview(button)
        i += 1
        y += button.frame.height
        return button
    }
    
 
}
class VerticalButton: UIButton {
    override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
    let titleRect = super.titleRect(forContentRect: contentRect)
    let imageRect = super.imageRect(forContentRect: contentRect)

     return CGRect(x: 0,
              y: contentRect.height - (contentRect.height - padding - imageRect.size.height - titleRect.size.height) / 2 - titleRect.size.height,
              width: contentRect.width,
              height: titleRect.height)
    }

    override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
       let imageRect = super.imageRect(forContentRect: contentRect)
       let titleRect = self.titleRect(forContentRect: contentRect)

       return CGRect(x: contentRect.width/2.0 - imageRect.width/2.0,
               y: (contentRect.height - padding - imageRect.size.height - titleRect.size.height) / 2,
              width: imageRect.width,
              height: imageRect.height )
    }

    private let padding: CGFloat
    init(padding: CGFloat) {
       self.padding = padding
       super.init(frame: .zero)
       self.titleLabel?.textAlignment = .center
    }

    required init?(coder aDecoder: NSCoder) { fatalError() }
}

extension UIButton {
    static func vertical(padding: CGFloat) -> UIButton {
   return VerticalButton(padding: padding)
 }}

有点难,因为你没有展示你想要实现的目标的图像。

此外,您的代码缺少 imageWithImage(...) 函数,因此我们无法 运行 直接查看您得到的结果。

但是,这可能对您有所帮助...

您做错了很多事 -- 将显式框架设置与堆栈视图(使用自动布局)混在一起;在错误的地方添加视图;在不应该的地方设置约束;等等

她的代码是您希望接近您所追求的代码。我添加了注释来解释不应该存在的内容,并注释掉了您现有的代码,以便您可以看到差异:

class ViewController: UIViewController {

    var i = 0

    // these will not be used
    //  private var stackView: UIStackView!
    //  private var stackViewNew: UIStackView!
    //  let x: CGFloat = 10
    //  let width: CGFloat = UIScreen.main.bounds.width - 20
    //  var y: CGFloat =  10
    //  let step:Float=10
    
    let scrollView: UIScrollView = {
        let v = UIScrollView()
        // we will want to use auto-layout
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()
    private var stackViewFilter: UIStackView = {
        let v = UIStackView()
        // we will want to use auto-layout
        v.translatesAutoresizingMaskIntoConstraints = false
        v.axis = .vertical
        v.backgroundColor = .black
        v.alpha = 0.8
        // use .fill instead of .equalSpacing
        v.distribution = .fill
        v.spacing = 10.0
        return v
    }()
    
    let horizontalStackView : UIStackView = {
        let v = UIStackView()
        // we will want to use auto-layout
        v.translatesAutoresizingMaskIntoConstraints = false
        v.axis = .horizontal
        v.backgroundColor = .systemPink
        // you want the buttons to be equal sizes,
        // so use .fillEqually instead of .equalSpacing
        v.distribution = .fill
        v.spacing = 10.0
        return v
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        createBottomFilter()
    }
    
    @objc func createBottomFilter(){
        
        /*---------- Slider Section ----------*/
        // we will want to use auto-layout
        // so no need to set a frame here
        //let mySlider = UISlider(frame:CGRect(x: 40, y: 10, width: 200, height: 60))
        let mySlider = UISlider()
        mySlider.translatesAutoresizingMaskIntoConstraints = false

        mySlider.minimumValue = 0
        mySlider.maximumValue = 100
        mySlider.isContinuous = true
        mySlider.tintColor = UIColor.green
        mySlider.addTarget(self, action: #selector(self.sliderValueDidChange(_:)), for: .valueChanged)
        
        UIView.animate(withDuration: 0.8) {
            mySlider.setValue(80.0, animated: true)
        }
        
        // respect safe-area
        let g = view.safeAreaLayoutGuide
        
        self.view.addSubview(stackViewFilter)

        // constrain stackViewFilter
        NSLayoutConstraint.activate([
            stackViewFilter.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            stackViewFilter.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            stackViewFilter.bottomAnchor.constraint(equalTo: g.bottomAnchor),
            stackViewFilter.heightAnchor.constraint(equalToConstant: 330.0),
        ])

        stackViewFilter.addArrangedSubview(mySlider)
        stackViewFilter.addArrangedSubview(scrollView)
        
        // just added scrollView as an arrangedSubview of stackViewFilter
        // so don't add it to the view
        //self.view.addSubview(scrollView)
        
        // slider is in a stack view, so don't set any positioning constraints
        
        //mySlider.leadingAnchor.constraint(equalTo: stackViewFilter.leadingAnchor, constant: 8).isActive = true
        //mySlider.trailingAnchor.constraint(equalTo: stackViewFilter.trailingAnchor, constant: 8).isActive = true
        //mySlider.topAnchor.constraint(equalTo: stackViewFilter.bottomAnchor, constant: 10).isActive = true
        
        //constraintBottom = mySlider.bottomAnchor.constraint(equalTo: scrollView.topAnchor, constant: 40)
        //constraintBottom?.isActive = true
        
        // scrollView is in a stack view, so don't set any positioning constraints
        //scrollView.leftAnchor.constraint(equalTo: stackViewFilter.leftAnchor, constant: 0.0).isActive = true
        //scrollView.topAnchor.constraint(equalTo: mySlider.bottomAnchor, constant: 8.0).isActive = true
        //scrollView.rightAnchor.constraint(equalTo: stackViewFilter.rightAnchor, constant: 80.0).isActive = true
        //scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 8.0).isActive = true
        
        // but we can set the scrollView's height constraint here
        scrollView.heightAnchor.constraint(equalToConstant: 200.0).isActive = true
        
        // add the stack view to the scroll view
        scrollView.addSubview(horizontalStackView)
        
        // constrain scrollView contents to the scrollView's contentLayoutGuide
        //horizontalStackView.leftAnchor.constraint(equalTo: scrollView.leftAnchor, constant: 0.0).isActive = true
        //horizontalStackView.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: 0.0).isActive = true
        //horizontalStackView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -30.0).isActive = true
        NSLayoutConstraint.activate([
            horizontalStackView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
            horizontalStackView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
            horizontalStackView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
            horizontalStackView.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor, constant: 30.0),
        ])
        
        // views added to stackView as arrangedSubview automatically use auto-layout
        // so no sense setting .translatesAutoresizingMaskIntoConstraints = true
        
        let b = generateButton(title: "Btn 1", selectedTitle: "Filter \(i)", iconName: "hellen", scaledToSize:CGSize(width: 90.0, height: 90.0))
        //b.translatesAutoresizingMaskIntoConstraints = true
        
        let b1 = generateButton(title: "Btn 2", selectedTitle: "Filter \(i)", iconName: "hellen", scaledToSize:CGSize(width: 90.0, height: 90.0))
        //b1.translatesAutoresizingMaskIntoConstraints = true
        
        let b2 = generateButton(title: "Btn 3", selectedTitle: "Filter \(i)", iconName: "hellen", scaledToSize:CGSize(width: 90.0, height: 90.0))
        //b2.translatesAutoresizingMaskIntoConstraints = true
        
        let b3 = generateButton(title: "Btn 4", selectedTitle: "Filter \(i)", iconName: "hellen", scaledToSize:CGSize(width: 90.0, height: 90.0))
        //b3.translatesAutoresizingMaskIntoConstraints = true
        
        let b4 = generateButton(title: "Btn 5", selectedTitle: "Filter \(i)", iconName: "hellen", scaledToSize:CGSize(width: 90.0, height: 90.0))
        //b4.translatesAutoresizingMaskIntoConstraints = true
        
        let b5 = generateButton(title: "Btn 6", selectedTitle: "Filter \(i)", iconName: "hellen", scaledToSize:CGSize(width: 90.0, height: 90.0))
        //b5.translatesAutoresizingMaskIntoConstraints = true
        
        
        horizontalStackView.addArrangedSubview(b)
        horizontalStackView.addArrangedSubview(b1)
        horizontalStackView.addArrangedSubview(b2)
        horizontalStackView.addArrangedSubview(b3)
        horizontalStackView.addArrangedSubview(b4)
        horizontalStackView.addArrangedSubview(b5)
        
        // alignment should be .fill, not .center
        horizontalStackView.alignment = .fill
        
        // because we set horizontalStackView.distribution = .fillEqually
        // we only need to set a width constraint on the first button
        b.widthAnchor.constraint(equalToConstant: 90.0).isActive = true
        
        // buttons should all be 90x90 ?
        [b, b1, b2, b3, b4, b5].forEach { btn in
            btn.heightAnchor.constraint(equalTo: btn.widthAnchor).isActive = true
        }
        
    }
    
    @objc func generateButton(title: String, selectedTitle: String? = nil, iconName: String, scaledToSize newSize: CGSize) -> UIButton {
        let iconName: UIImage? = imageWithImage(UIImage(named: iconName), scaledToSize:CGSize(width: newSize.width, height: newSize.height))
        iconName?.withTintColor(.white)
        
        let button = UIButton.vertical(padding: 3)
        // buttons in stack view will use auto-layout,
        // so no need to set frames here
        //button.frame = CGRect(x: x, y: y, width: width, height: 80)
        button.setImage(iconName, for: .normal)
        button.layer.zPosition = 1
        button.setTitle(title, for: .normal)
        button.setTitle(selectedTitle, for: .selected)
        
        // button will be added to stack view
        //self.view.addSubview(button)
        
        i += 1
        
        // not sure why this was here to begin with...
        // you want a horizontal row of buttons, so changing the
        // y position makes no sense
        //y += button.frame.height
        
        return button
    }
    
    
}

class VerticalButton: UIButton {
    override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
        let titleRect = super.titleRect(forContentRect: contentRect)
        let imageRect = super.imageRect(forContentRect: contentRect)
        
        return CGRect(x: 0,
                      y: contentRect.height - (contentRect.height - padding - imageRect.size.height - titleRect.size.height) / 2 - titleRect.size.height,
                      width: contentRect.width,
                      height: titleRect.height)
    }
    
    override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
        let imageRect = super.imageRect(forContentRect: contentRect)
        let titleRect = self.titleRect(forContentRect: contentRect)
        
        return CGRect(x: contentRect.width/2.0 - imageRect.width/2.0,
                      y: (contentRect.height - padding - imageRect.size.height - titleRect.size.height) / 2,
                      width: imageRect.width,
                      height: imageRect.height )
    }
    
    private let padding: CGFloat
    init(padding: CGFloat) {
        self.padding = padding
        super.init(frame: .zero)
        self.titleLabel?.textAlignment = .center
    }
    
    required init?(coder aDecoder: NSCoder) { fatalError() }
}

extension UIButton {
    static func vertical(padding: CGFloat) -> UIButton {
        return VerticalButton(padding: padding)
    }
}

这是它的外观,使用系统“文档”图像作为按钮,因为我不知道你用 iconName 做什么...水平按钮 可以 滚动:

如果这接近您的要求,您应该能够在查看代码后调整值。