UIScrollView与其他UIElement重叠,因此它们不再可单击

问题描述 投票:1回答:1

亲爱的StackOverflowCommunity,

im目前正在从事一个项目,我需要在其中创建一个完全动态的用户界面。为此,我以编程方式构建了所有功能,以使其尽可能简单。现在,我遇到一个问题,即我将UIViewView包裹了contentView(UIStackView)以使其可滚动时,scrollView在所有其他UIElement的前面,因此我只能滚动。我无法与按钮,滑块,开关或任何其他东西进行交互,不会触发任何事件。

我确实做了我能想到的任何事情(针对DAYS问题进行工作),但无论是直接在Google上还是在堆栈溢出或苹果论坛上都找不到任何合适的答案。

我很确定这是我无法想到的很小的变化。非常感谢您的帮助。

结构是这样的:ViewController > UIScrollView > UIStackView > Item Wrappers (for example contains a UISwitch and a Describing Label) > Single UIElement

在用户交互(例如选择其他模式)时,包装器将根据用户需要被删除和/或添加到视图中。我只发布了与该问题某种程度上相关的代码(我认为)。如果您需要更多信息,请随时询问。

我可能需要添加:一旦我删除UIScrollView并仅将StackView(在代码中命名为contentView)添加到主视图中,它就可以正常工作,但我无法滚动它,这是一个大问题,一旦我拥有超过5个元素附加到视图的包装器。

    var wrappers : Dictionary<String, UIView> = [:]
var elements : Dictionary<String, [UIView]> = [:]
var constraints : Dictionary = [String: [[NSLayoutConstraint]]]()
let contentView = UIStackView()

let states = [
    "state_operating_hours",
    "state_dim",
    "state_brightness_sensor",
    "state_operating_voltage",
    "state_circuit_voltage",
    "state_load_current_led",
    "state_output_power",
    "state_temperature",
    "state_error",
    "state_sw_version",
    "state_hw_version"
]

let checkboxes = [
    "summertime_wintertime",
    "dali",
    "error_output"
]

let sliders = [
    "immediate_sensitivity",
    "immediate_dawn",
    "immediate_dim",
    "immediate_day",
    "immediate_dusk",
    "immediate_night",
    "aging"
]

let textInputs = [
    "module_name",
    "switch_delay"
]

let dropdowns = [
    "mode",
    "bt_state",
    "config_output"
]

let timePickers = [
    "phase_0",
    "phase_1",
    "phase_2",
    "phase_3"
]

let buttons = [
    "state_trigger",
    "reset_trigger",
]

let l_kind_criteria = [
    "immediate_dawn",
    "immediate_day",
    "immediate_dusk",
    "immediate_night",
    "immediate_sensitivity"
]

let d_kind_criteria = [
    "immediate_dim"
]

let t_kind_criteria = [
    "phase_0",
    "phase_1",
    "phase_2",
    "phase_3"
]

let m_kind_criteria = [
    "immediate_dawn",
    "immediate_day",
    "immediate_dusk",
    "immediate_night",
    "immediate_sensitivity",
    "phase_0",
    "phase_1"
]

let user_criteria = [
    //"access",
    //"state_trigger",
    //"reset_trigger",
    "mode",
    "summertime_wintertime"
]

let service_criteria = [
    "module_name",
    //"access",
    "state_trigger",
    "reset_trigger",
    "mode",
    "bt_state",
    "config_output",
    "aging",
    "switch_delay",
    "summertime_wintertime",
    "error_output",
    "dali"
]

override func viewDidLoad() {
    bleService.delegate = self
    bleService.requestAuthMode()
    view.backgroundColor = .lightGray
    bleService.send(aText: "c28r:#")
    bleService.send(aText: "c05r:#")
    Toast.show(message: "Statuswerte werden abgerufen..." , controller: self)
    buildLayout()
}

// Class - Functions

func buildLayout() {
    // Building the Basic Layout

    let topView = UIView()
    topView.backgroundColor = .purple
    self.view.addSubview(topView)
    topView.translatesAutoresizingMaskIntoConstraints = false

    let logoImageView = UIImageView(image: UIImage(named: "placeholder"))
    logoImageView.translatesAutoresizingMaskIntoConstraints = false
    logoImageView.frame = CGRect(x: 0, y: 0, width: view.frame.width/1.8, height: 30)
    topView.addSubview(logoImageView)

    logoImageView.leftAnchor.constraint(greaterThanOrEqualTo: view.leftAnchor, constant: 20).isActive = true
    logoImageView.topAnchor.constraint(greaterThanOrEqualTo: view.topAnchor, constant: 30).isActive = true

    topView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
    topView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
    topView.heightAnchor.constraint(equalToConstant: view.frame.height/3).isActive = true
    topView.centerYAnchor.constraint(equalTo: view.topAnchor).isActive = true
    topView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true

    //Generate and add Scroll View to Main Window

    let scrollView = UIScrollView()
    scrollView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(scrollView)

    NSLayoutConstraint.activate([
        scrollView.topAnchor.constraint(equalTo: topView.bottomAnchor, constant: 20),
        scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
        scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
        scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
        scrollView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
    ])

    //Add Content Stack to Scroll View

    contentView.axis = .vertical
    contentView.alignment = .fill
    contentView.spacing = 150
    contentView.distribution = .fill
    contentView.backgroundColor = .blue
    contentView.translatesAutoresizingMaskIntoConstraints = false
    scrollView.addSubview(contentView)

    NSLayoutConstraint.activate([
        contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
        contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 20),
        contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -20),
        contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
        contentView.widthAnchor.constraint(equalToConstant: scrollView.frame.width),
        contentView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
    ])

    // programmatically creating layout elements without constraints
    // Elements that change a value are always last in their respective array

    for (index, dropdownName) in dropdowns.enumerated() {
        constraints[dropdownName] = [[]]
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.text = dropdownName

        let leadAnch = label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor)
        constraints[dropdownName]!.append([leadAnch])

        let textField = UITextField()
        textField.delegate = self
        textField.translatesAutoresizingMaskIntoConstraints = false
        textField.backgroundColor = .white
        textField.layer.cornerRadius = 5

        var trailAnch = textField.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
        var widthAnch = textField.widthAnchor.constraint(equalToConstant: view.frame.width / 6)
        constraints[dropdownName]!.append([trailAnch, widthAnch])

        let pickerView = UIPickerView()
        pickerView.backgroundColor = .white
        pickerView.translatesAutoresizingMaskIntoConstraints = false
        pickerView.delegate = self
        pickerView.isHidden = true
        pickerView.dataSource = self

        trailAnch = pickerView.trailingAnchor.constraint(equalTo: textField.trailingAnchor)
        widthAnch = pickerView.widthAnchor.constraint(equalTo: textField.widthAnchor)
        constraints[dropdownName]!.append([trailAnch, widthAnch])

        let dropdownWrapper = UIView()
        dropdownWrapper.translatesAutoresizingMaskIntoConstraints = false

        dropdownWrapper.addSubview(label)
        dropdownWrapper.addSubview(textField)
        dropdownWrapper.addSubview(pickerView)

        wrappers[dropdownName] = dropdownWrapper
        elements[dropdownName] = [label, textField, pickerView]

        let commandID = bleService.getCommand(commandName: dropdownName)
        bleService.send(aText: "c\(commandID)r:#")
    }

    for (index, sliderName) in sliders.enumerated() {
        constraints[sliderName] = [[]]
        let descLabel = UILabel()
        descLabel.translatesAutoresizingMaskIntoConstraints = false
        descLabel.text = sliderName

        var leadAnch = descLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor)
        constraints[sliderName]!.append([leadAnch])

        let valueLabel = UILabel()
        valueLabel.translatesAutoresizingMaskIntoConstraints = false
        valueLabel.text = "0"
        valueLabel.backgroundColor = .white

        let widthAnch = valueLabel.widthAnchor.constraint(equalToConstant: view.frame.width/6)
        var trailAnch = valueLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
        constraints[sliderName]!.append([trailAnch, widthAnch])

        let slider = UISlider()
        slider.translatesAutoresizingMaskIntoConstraints = false
        slider.isContinuous = false
        slider.addTarget(self, action: #selector(sliderValueChanged), for: .valueChanged)

        leadAnch = slider.leadingAnchor.constraint(equalTo: contentView.leadingAnchor)
        trailAnch = slider.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
        let topAnch = slider.topAnchor.constraint(equalTo: descLabel.bottomAnchor, constant: 5)
        constraints[sliderName]!.append([trailAnch, leadAnch, topAnch])

        let sliderWrapper = UIView()
        sliderWrapper.translatesAutoresizingMaskIntoConstraints = false

        sliderWrapper.addSubview(descLabel)
        sliderWrapper.addSubview(valueLabel)
        sliderWrapper.addSubview(slider)

        wrappers[sliderName] = sliderWrapper
        elements[sliderName] = [descLabel, valueLabel, slider]

        let commandID = bleService.getCommand(commandName: sliderName)
        bleService.send(aText: "c\(commandID)r:#")
    }

    for (index, checkboxName) in checkboxes.enumerated() {
        constraints[checkboxName] = [[]]
        let cbLabel = UILabel()
        cbLabel.translatesAutoresizingMaskIntoConstraints = false
        cbLabel.text = checkboxName

        let leadAnch = cbLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor)
        constraints[checkboxName]!.append([leadAnch])

        let checkbox = UISwitch()
        checkbox.translatesAutoresizingMaskIntoConstraints = false
        checkbox.addTarget(self, action: #selector(checkboxClicked), for: .valueChanged)

        let trailAnch = checkbox.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
        constraints[checkboxName]!.append([trailAnch])

        let checkboxWrapper = UIView()
        checkboxWrapper.translatesAutoresizingMaskIntoConstraints = false

        checkboxWrapper.addSubview(cbLabel)
        checkboxWrapper.addSubview(checkbox)

        wrappers[checkboxName] = checkboxWrapper
        elements[checkboxName] = [cbLabel, checkbox]

        let commandID = bleService.getCommand(commandName: checkboxName)
        bleService.send(aText: "c\(commandID)r:#")
    }

    for (index, textInputName) in textInputs.enumerated() {
        constraints[textInputName] = [[]]
        let textLabel = UILabel()
        textLabel.translatesAutoresizingMaskIntoConstraints = false
        textLabel.text = textInputName

        var leadAnch = textLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor)
        constraints[textInputName]!.append([leadAnch])

        let inputField = UITextField()
        inputField.layer.cornerRadius = 5
        inputField.translatesAutoresizingMaskIntoConstraints = false
        inputField.placeholder = textInputs[index]
        inputField.backgroundColor = .white
        inputField.addTarget(self, action: #selector(textfieldChanged), for: .valueChanged)

        let topAnch = inputField.topAnchor.constraint(equalTo: textLabel.bottomAnchor, constant: 5)
        let widthAnch = inputField.widthAnchor.constraint(equalToConstant: view.safeAreaLayoutGuide.layoutFrame.width/1.1)
        leadAnch = inputField.leadingAnchor.constraint(equalTo: contentView.leadingAnchor)
        constraints[textInputName]!.append([topAnch, widthAnch, leadAnch])

        let inputWrapper = UIView()
        inputWrapper.translatesAutoresizingMaskIntoConstraints = false

        inputWrapper.addSubview(textLabel)
        inputWrapper.addSubview(inputField)

        wrappers[textInputName] = inputWrapper
        elements[textInputName] = [textLabel, inputField]

        let commandID = bleService.getCommand(commandName: textInputName)
        bleService.send(aText: "c\(commandID)r:#")
    }

    for(index, phase) in timePickers.enumerated() {
        constraints[phase] = [[]]
        let descLabel = UILabel()
        descLabel.translatesAutoresizingMaskIntoConstraints = false
        descLabel.text = "Zeitschaltung \(index+1)"

        var leadAnch = descLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor)
        constraints[phase]!.append([leadAnch])

        let enabledSwitch = UISwitch()
        enabledSwitch.translatesAutoresizingMaskIntoConstraints = false
        enabledSwitch.addTarget(self, action: #selector(changeTimerState), for: .valueChanged)

        var topAnch = enabledSwitch.topAnchor.constraint(equalTo: descLabel.topAnchor)
        var trailAnch = enabledSwitch.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
        constraints[phase]!.append([trailAnch, topAnch])

        let showPickerButton = UIButton()
        showPickerButton.translatesAutoresizingMaskIntoConstraints = false
        showPickerButton.setTitle("Zeit auswählen", for: .normal)
        showPickerButton.backgroundColor = .darkGray
        showPickerButton.layer.cornerRadius = 5
        showPickerButton.addTarget(self, action: #selector(showTimePicker), for: .touchUpInside)

        topAnch = showPickerButton.topAnchor.constraint(equalTo: enabledSwitch.bottomAnchor, constant: 4)
        trailAnch = showPickerButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
        constraints[phase]!.append([topAnch, trailAnch])

        let timePicker = UIDatePicker()
        timePicker.backgroundColor = .white
        timePicker.isHidden = true
        timePicker.translatesAutoresizingMaskIntoConstraints = false
        timePicker.datePickerMode = .time
        timePicker.addTarget(self, action: #selector(changeTimer), for: .valueChanged)

        topAnch = timePicker.bottomAnchor.constraint(equalTo: enabledSwitch.bottomAnchor)
        trailAnch = timePicker.trailingAnchor.constraint(equalTo: enabledSwitch.trailingAnchor)
        constraints[phase]!.append([topAnch, trailAnch])

        //Brightness Slider Value Label

        let sliderValLabel = UILabel()
        sliderValLabel.translatesAutoresizingMaskIntoConstraints = false
        sliderValLabel.text = "0"
        sliderValLabel.backgroundColor = .white

        trailAnch = sliderValLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
        topAnch = sliderValLabel.topAnchor.constraint(equalTo: showPickerButton.bottomAnchor, constant: 10)
        var widthAnch = sliderValLabel.widthAnchor.constraint(equalToConstant: view.frame.width / 6)
        constraints[phase]!.append([trailAnch, topAnch, widthAnch])

        //Brightness Slider

        let valueSlider = UISlider()
        valueSlider.isContinuous = false
        valueSlider.translatesAutoresizingMaskIntoConstraints = false

        topAnch = valueSlider.topAnchor.constraint(equalTo: sliderValLabel.bottomAnchor, constant: 10)
        leadAnch = valueSlider.leadingAnchor.constraint(equalTo: contentView.leadingAnchor)
        trailAnch = valueSlider.trailingAnchor.constraint(equalTo: contentView.trailingAnchor)
        constraints[phase]!.append([topAnch, leadAnch, trailAnch])

        let timePickerWrapper = UIView()
        //timePickerWrapper.translatesAutoresizingMaskIntoConstraints = false

        timePickerWrapper.addSubview(descLabel)
        timePickerWrapper.addSubview(enabledSwitch)
        timePickerWrapper.addSubview(showPickerButton)
        timePickerWrapper.addSubview(timePicker)
        timePickerWrapper.addSubview(valueSlider)
        timePickerWrapper.addSubview(sliderValLabel)

        wrappers[phase] = timePickerWrapper
        elements[phase] = [descLabel, showPickerButton, enabledSwitch, timePicker, sliderValLabel, valueSlider]

        let commandID = bleService.getCommand(commandName: phase)
        bleService.send(aText: "c\(commandID)r:#")
    }

    for buttonName in buttons {
        constraints[buttonName] = [[]]
        let button = UIButton()
        button.translatesAutoresizingMaskIntoConstraints = false

        let widthAnch = button.widthAnchor.constraint(equalToConstant: contentView.frame.width/1.1)
        let xAnch = button.centerXAnchor.constraint(equalTo: view.centerXAnchor)
        constraints[buttonName]!.append([widthAnch, xAnch])

        let buttonWrapper = UIView()
        buttonWrapper.translatesAutoresizingMaskIntoConstraints = false

        wrappers[buttonName] = buttonWrapper
        elements[buttonName] = [button]
    }
}        

func changeContent(criteria: [String]) {
        for item in criteria {
            if(!contentView.contains(wrappers[item]!)) {
                contentView.addArrangedSubview(wrappers[item]!)
                for singleView in constraints[item]! {
                    for singleViewConstraint in singleView {
                        singleViewConstraint.isActive = true
                    }
                }
            }
        }
    }

func removeContent() {
    var criteria = [String]()
    switch(previousSetupMode) {
    case "d":
        criteria = d_kind_criteria
        break
    case "l":
        criteria = l_kind_criteria
        break
    case "m":
        criteria = m_kind_criteria
        break
    case "t":
        criteria = t_kind_criteria
        break
    default:
        break
    }
    for item in criteria {
        wrappers[item]!.removeFromSuperview()
    }
}

func changeView() {
    if(previousSetupMode != activeSetupMode) {
        removeContent()
    }
    switch(activeSetupMode) {
    case "d":
        changeContent(criteria: d_kind_criteria)
        break
    case "l":
        changeContent(criteria: l_kind_criteria)
        break
    case "t":
        changeContent(criteria: t_kind_criteria)
        break
    case "m":
        changeContent(criteria: m_kind_criteria)
        break
    default:
        break
    }
}
ios swift uiscrollview uistackview
1个回答
3
投票

问题是您没有给“包装”视图任何高度,因此控件被放置在其父视图的范围之外。

您可以通过两种方式确认...

1]在您的>>

for (index, sliderName) in sliders.enumerated() {

阻止,添加:

sliderWrapper.backgroundColor = .green

(当然是在创建sliderWrapper视图之后)。运行应用程序时,您不会看到绿色背景,因为sliderWrapper的高度为零:

enter image description here

2)和/或添加:

sliderWrapper.clipsToBounds = true

您将根本看不到控件:

enter image description here

要解决这个问题,您可以添加约束:

        let sliderWrapper = UIView()
        sliderWrapper.translatesAutoresizingMaskIntoConstraints = false
        sliderWrapper.backgroundColor = .green

        sliderWrapper.addSubview(descLabel)
        sliderWrapper.addSubview(valueLabel)
        sliderWrapper.addSubview(slider)

        // add a topAnchor constraint from the top of descLabel to the top of sliderWrapper
        // center valueLabel vertically to descLabel
        // and a bottomAnchor from the bottom of slider to the bottom of sliderWrapper (negative if you want "padding")
        NSLayoutConstraint.activate([
            descLabel.topAnchor.constraint(equalTo: sliderWrapper.topAnchor, constant: 8.0),
            valueLabel.centerYAnchor.constraint(equalTo: descLabel.centerYAnchor),
            slider.bottomAnchor.constraint(equalTo: sliderWrapper.bottomAnchor, constant: -8.0),
        ])

现在,背景是可见的...控件是可见的...并且控件可以与以下控件进行交互:

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.