【问题标题】:UISlider in not working properly in UIStackViewUISlider 在 UIStackView 中无法正常工作
【发布时间】:2021-04-08 07:39:11
【问题描述】:

让我解释一下我想要实现的目标。我想在 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)
 }}

【问题讨论】:

    标签: ios swift uiscrollview uistackview


    【解决方案1】:

    这有点困难,因为您没有展示您想要实现的目标。

    此外,您的代码缺少 imageWithImage(...) 函数,因此我们无法直接运行它来查看您得到的确切信息。

    但是,这可能会帮助您...

    你做错了很多事情——将显式框架设置与堆栈视图(使用自动布局)混合;将视图添加到错误的位置;在不应该出现的地方设置约束;等等

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

    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 做什么...水平按钮可以滚动:

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

    【讨论】:

    • 这正是我想要的,它的工作非常感谢你
    • @user2572661 - 如果这回答了您的问题,请务必将其标记为已接受,以便其他可能遇到它的用户受益。
    猜你喜欢
    • 1970-01-01
    • 2021-11-08
    • 1970-01-01
    • 1970-01-01
    • 2016-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多