Increment number and apply it to tableView header - Swift - ios

I have made a table view inside a view controller. The table view also has headers and when clicked on them you can open and close each section.
I want to add a different title in each sections header with the first one being 01, second being 02 and so on.
I have tried getting the count number of the section and making that the title. However, it just makes every single title the same number. I don't know how to increment up from 01 and apply it to each section?
Any help would be amazing, thank you in advance.
I have a two dimensional array which looks like this:
var twoDimensionalArray = [
shootDaysExpandableNames(isExpanded: true, shotNumber: ["Cell_001", "Cell_002", "Cell_015"]),
shootDaysExpandableNames(isExpanded: true, shotNumber: ["Cell_004", "Cell_006", "Cell_008", "Cell_010", "Cell_014"]),
shootDaysExpandableNames(isExpanded: true, shotNumber: ["Cell_0017", "Cell_009", "Cell_020", "Cell_024", "Cell_34"])
]
And then I have my viewForHeaderInSection that looks like this:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let shotNumberInt = twoDimensionalArray[section].shotNumber.count
let shootDayInt = twoDimensionalArray.count
var shotNumberAmount = String(shotNumberInt) + (" Shots")
var shootDay = String(shootDayInt)
let line: UIView = {
let line = UIView()
line.backgroundColor = UIColor(red:0.63, green:0.63, blue:0.63, alpha:1.0)
line.translatesAutoresizingMaskIntoConstraints = false
return line
}()
let openClose: UILabel = {
let openClose = UILabel()
openClose.text = "Open"
openClose.textColor = UIColor(red:0.17, green:0.59, blue:0.30, alpha:1.0)
openClose.font = UIFont(name: "Avenir-Medium", size: 12.0)
openClose.translatesAutoresizingMaskIntoConstraints = false
return openClose
}()
let shootDayNumber: UILabel = {
let shootDayNumber = UILabel()
shootDayNumber.text = shootDay
shootDayNumber.textColor = UIColor(red:0.18, green:0.20, blue:0.21, alpha:1.0)
shootDayNumber.font = UIFont(name: "AvenirNext-Bold", size: 40.0)
shootDayNumber.translatesAutoresizingMaskIntoConstraints = false
return shootDayNumber
}()
let shootDate: UILabel = {
let shootDate = UILabel()
shootDate.text = shotNumberAmount
shootDate.textColor = UIColor(red:0.63, green:0.63, blue:0.63, alpha:1.0)
shootDate.font = UIFont(name: "Avenir-Medium", size: 12.0)
shootDate.translatesAutoresizingMaskIntoConstraints = false
return shootDate
}()
let button = UIButton(type: .system)
button.backgroundColor = UIColor.white
button.addSubview(line)
button.addSubview(shootDayNumber)
button.addSubview(openClose)
button.addSubview(shootDate)
button.addTarget(self, action: #selector(HandleOpenClose), for: .touchUpInside)
button.tag = section
// CONSTRAINTS
shootDayNumber.centerYAnchor.constraint(equalTo: button.centerYAnchor, constant: 0).isActive = true
shootDayNumber.leftAnchor.constraint(equalTo: button.leftAnchor, constant: 17).isActive = true
line.topAnchor.constraint(equalTo: button.topAnchor, constant: 10).isActive = true
line.leftAnchor.constraint(equalTo: shootDayNumber.rightAnchor, constant: 5).isActive = true
line.rightAnchor.constraint(equalTo: button.rightAnchor).isActive = true
line.heightAnchor.constraint(equalToConstant: 1).isActive = true
shootDate.bottomAnchor.constraint(equalTo: button.bottomAnchor, constant: -10).isActive = true
shootDate.leftAnchor.constraint(equalTo: shootDayNumber.rightAnchor, constant: 5).isActive = true
openClose.bottomAnchor.constraint(equalTo: button.bottomAnchor, constant: -10).isActive = true
openClose.rightAnchor.constraint(equalTo: button.rightAnchor, constant: -17).isActive = true
return button
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let shotNumberInt = twoDimensionalArray[section].shotNumber.count
let shootDayInt = twoDimensionalArray.count
var shotNumberAmount = String(shotNumberInt) + (" Shots")
var shootDay = String(shootDayInt)
let line: UIView = {
let line = UIView()
line.backgroundColor = UIColor(red:0.63, green:0.63, blue:0.63, alpha:1.0)
line.translatesAutoresizingMaskIntoConstraints = false
return line
}()
let shootDayNumber: UILabel = {
let shootDayNumber = UILabel()
shootDayNumber.text = shootDay
shootDayNumber.textColor = UIColor(red:0.18, green:0.20, blue:0.21, alpha:1.0)
shootDayNumber.font = UIFont(name: "AvenirNext-Bold", size: 40.0)
shootDayNumber.translatesAutoresizingMaskIntoConstraints = false
return shootDayNumber
}()
let shotCount: UILabel = {
let shotCount = UILabel()
shotCount.text = shotNumberAmount
shotCount.textColor = UIColor(red:0.63, green:0.63, blue:0.63, alpha:1.0)
shotCount.font = UIFont(name: "Avenir-Medium", size: 12.0)
shotCount.translatesAutoresizingMaskIntoConstraints = false
return shotCount
}()
let button = UIButton(type: .system)
button.backgroundColor = UIColor.white
button.addSubview(line)
button.addSubview(shootDayNumber)
button.addSubview(shotCount)
button.addTarget(self, action: #selector(HandleOpenClose), for: .touchUpInside)
button.tag = section
// CONSTRAINTS
shootDayNumber.centerYAnchor.constraint(equalTo: button.centerYAnchor, constant: 0).isActive = true
shootDayNumber.leftAnchor.constraint(equalTo: button.leftAnchor, constant: 17).isActive = true
line.topAnchor.constraint(equalTo: button.topAnchor, constant: 10).isActive = true
line.leftAnchor.constraint(equalTo: shootDayNumber.rightAnchor, constant: 5).isActive = true
line.rightAnchor.constraint(equalTo: button.rightAnchor).isActive = true
line.heightAnchor.constraint(equalToConstant: 1).isActive = true
openClose.bottomAnchor.constraint(equalTo: button.bottomAnchor, constant: -10).isActive = true
openClose.rightAnchor.constraint(equalTo: button.rightAnchor, constant: -17).isActive = true
return button
FYI: I am also adding a few other UILabels, UIImages to each header (these will be the same in each header), but I couldn't find a way to add them to each header without creating and giving constraints inside the viewForHeaderInSection function. Not sure if this the right way to do it?

This line:
let shootDayInt = twoDimensionalArray.count
Is wrong. It should be
let shootDayInt = section + 1
(The count of elements in your twoDimensionalArray doesn't change, but the section number passed into your tableView(_:viewForHeaderInSection:) does change.)

Related

ViewController Layout Help Needed

I'm using XCode to build a UI for a countdown timer/metronome app. After struggling with layout constraints, I discovered vertical and horizontal stack views, which I thought would make things much easier.
However, the outermost stackview's boundaries appear to be outside the view of the device. I removed all the layout constraints, but that didn't make any difference.
Here's a partial screenshot of the project in XCode:
StackView boundaries
Sorry if this question has been asked before - I looked but haven't found anything.
I'm a professional software developer for the last 30 years or so. The languages I work with on a daily basis for the last 20 years or so are Java, C++, and Fortran. So dealing with UI layout in IOS is frustrating, to say the least!
Thanks in advance for any help!
Edit: Here's a screenshot of the uppermost stack view's size inspector: size inspector
I know your frustration well because it was mine too when I start, and I decided to give you a hand. Make all UI programmatically like this:
in your Controller class built your objects programmatically:
let buttonPlay: UIButton = {
let button = UIButton(type: .system)
button.backgroundColor = UIColor.yourColor
let image = UIImage(systemName: "play.fill")
button.setImage(image, for: .normal)
button.tintColor = .black
button.heightAnchor.constraint(equalToConstant: 50).isActive = true
button.layer.cornerRadius = 25
button.clipsToBounds = true
return button
}()
let buttonPause: UIButton = {
let button = UIButton(type: .system)
button.backgroundColor = UIColor.yourColor
let image = UIImage(systemName: "pause.fill")
button.setImage(image, for: .normal)
button.tintColor = .black
button.heightAnchor.constraint(equalToConstant: 50).isActive = true
button.layer.cornerRadius = 25
button.clipsToBounds = true
return button
}()
let buttonStop: UIButton = {
let button = UIButton(type: .system)
button.backgroundColor = UIColor.yourColor
let image = UIImage(systemName: "stop.fill")
button.setImage(image, for: .normal)
button.tintColor = .black
button.heightAnchor.constraint(equalToConstant: 50).isActive = true
button.layer.cornerRadius = 25
button.clipsToBounds = true
return button
}()
let minutesLabel: UILabel = {
let label = UILabel()
label.backgroundColor = .white
label.text = "MM"
label.textColor = .gray
label.textAlignment = .center
label.font = .systemFont(ofSize: 20, weight: .semibold)
return label
}()
let secondsLabel: UILabel = {
let label = UILabel()
label.backgroundColor = .white
label.text = "SS"
label.textColor = .gray
label.textAlignment = .center
label.font = .systemFont(ofSize: 20, weight: .semibold)
return label
}()
let separatorLabel: UILabel = {
let label = UILabel()
label.backgroundColor = .clear
label.text = ":"
label.textColor = .black
label.textAlignment = .center
label.font = .systemFont(ofSize: 20, weight: .semibold)
return label
}()
let titleLabel: UILabel = {
let label = UILabel()
label.backgroundColor = UIColor.yourColor
label.text = "Title label"
label.textColor = .black
label.textAlignment = .center
label.font = .systemFont(ofSize: 20, weight: .semibold)
label.translatesAutoresizingMaskIntoConstraints = false // this distactive automatic constraints
return label
}()
let timeLabel: UILabel = {
let label = UILabel()
label.backgroundColor = .clear
label.text = "Tempo:"
label.textColor = .black
label.font = .systemFont(ofSize: 20, weight: .regular)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let imageViewTime: UIImageView = {
let image = UIImage(named: "yourImage")?.withRenderingMode(.alwaysTemplate)
let imageView = UIImageView(image: image)
imageView.tintColor = .white
imageView.backgroundColor = .purple
imageView.contentMode = .scaleAspectFit
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
let slider: UISlider = {
let s = UISlider()
s.maximumValue = 0
s.maximumValue = 100
s.isContinuous = true
s.tintColor = .black
s.addTarget(self, action: #selector(sliderInAction), for: .valueChanged) // call the function below to show slider value
s.translatesAutoresizingMaskIntoConstraints = false
return s
}()
let sliderLabel: UILabel = {
let label = UILabel()
label.backgroundColor = .clear
label.text = "2"
label.textAlignment = .center
label.textColor = .black
label.font = .systemFont(ofSize: 16, weight: .regular)
label.widthAnchor.constraint(equalToConstant: 30).isActive = true
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let containerSlider: UIView = {
let v = UIView()
v.backgroundColor = UIColor.yourColor
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
In viewDidLoad set your background color and call the function to setup the programmatically constraints:
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.yourColor
setupConstraints()
}
now set the constraints with autolayout:
fileprivate func setupConstraints() {
let stackViewTime = UIStackView(arrangedSubviews: [minutesLabel, separatorLabel, secondsLabel])
stackViewTime.distribution = .fillProportionally
stackViewTime.spacing = 4
stackViewTime.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackViewTime)
stackViewTime.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10).isActive = true
stackViewTime.heightAnchor.constraint(equalToConstant: 50).isActive = true
stackViewTime.widthAnchor.constraint(equalToConstant: 148).isActive = true
stackViewTime.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
let stackViewPlayer = UIStackView(arrangedSubviews: [buttonPlay, buttonPause, buttonStop])
stackViewPlayer.distribution = .fillEqually
stackViewPlayer.spacing = 10
stackViewPlayer.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackViewPlayer)
stackViewPlayer.widthAnchor.constraint(equalToConstant: 170).isActive = true
stackViewPlayer.heightAnchor.constraint(equalToConstant: 50).isActive = true
stackViewPlayer.centerXAnchor.constraint(equalTo: stackViewTime.centerXAnchor).isActive = true
stackViewPlayer.topAnchor.constraint(equalTo: stackViewTime.bottomAnchor, constant: 20).isActive = true
view.addSubview(imageViewTime)
imageViewTime.topAnchor.constraint(equalTo: stackViewPlayer.bottomAnchor, constant: 20).isActive = true
imageViewTime.heightAnchor.constraint(equalToConstant: 80).isActive = true
imageViewTime.widthAnchor.constraint(equalToConstant: 60).isActive = true
imageViewTime.centerXAnchor.constraint(equalTo: stackViewTime.centerXAnchor).isActive = true
view.addSubview(titleLabel)
titleLabel.topAnchor.constraint(equalTo: imageViewTime.bottomAnchor, constant: 20).isActive = true
titleLabel.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 30).isActive = true
titleLabel.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -30).isActive = true
titleLabel.heightAnchor.constraint(equalToConstant: 50).isActive = true
view.addSubview(timeLabel)
timeLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 10).isActive = true
timeLabel.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10).isActive = true
timeLabel.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10).isActive = true
timeLabel.heightAnchor.constraint(equalToConstant: 50).isActive = true
let stackSlider = UIStackView(arrangedSubviews: [sliderLabel, slider])
stackSlider.distribution = .fill
stackSlider.spacing = 10
stackSlider.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(containerSlider)
containerSlider.topAnchor.constraint(equalTo: timeLabel.bottomAnchor, constant: 0).isActive = true
containerSlider.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10).isActive = true
containerSlider.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10).isActive = true
containerSlider.heightAnchor.constraint(equalToConstant: 30).isActive = true
containerSlider.addSubview(stackSlider)
stackSlider.topAnchor.constraint(equalTo: containerSlider.topAnchor).isActive = true
stackSlider.bottomAnchor.constraint(equalTo: containerSlider.bottomAnchor).isActive = true
stackSlider.leadingAnchor.constraint(equalTo: containerSlider.leadingAnchor, constant: 10).isActive = true
stackSlider.trailingAnchor.constraint(equalTo: containerSlider.trailingAnchor, constant: -10).isActive = true
}
the last step set the function to show the slider value in your slider label when you move it:
#objc fileprivate func sliderInAction() {
sliderLabel.text = "\(Int(slider.value))"
}
This is the result:
Play with this code, an experienced developer like you should take a short time to write the code.

UIScrollView overlaps other UIElements so they are not clickable anymore

Dear StackOverflowCommunity,
im currently working on a project, where I need to make a fully dynamic user interface.
For that, im building all of it programatically to make it as easy as possible.
I've now come to a problem which is as soon as I wrap my contentView(UIStackView) with an UIScrollView to make it scrollable, the scrollView is in front of all the other UIElements so that I can only scroll. I can not interact with Buttons, Sliders, Switches or anything, no event will get triggered.
I've literally done anything I could think of (working on that Problem for DAYS) but couldn't find any suiting answer whether on google directly nor on stack overflow or apple forums.
I'm pretty sure its a pretty small change that I just wasn't capable of thinking of. I really appreciate any of your help.
Structure is like this:
ViewController > UIScrollView > UIStackView > Item Wrappers (for example contains a UISwitch and a Describing Label) > Single UIElement
On User Interaction (for example choosing a different mode) the wrappers get removed and/or added to the view as the user needs.
I did only post the code that is somehow relevant for this problem (in my opinion). If you need any further information feel free to ask.
I maybe need to add:
As soon as I remove the UIScrollView and just add the StackView (named contentView in code) to the main view it all works just fine, I just can't scroll it which is a big problem as soon as I have more than like 5 element wrappers attached to the view.
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
}
}
The problem is that you are not giving your "wrapper" views any height, so the controls are being placed outside the bounds of their parent views.
You can confirm this two ways...
1) In your
for (index, sliderName) in sliders.enumerated() {
block, add:
sliderWrapper.backgroundColor = .green
(after creating the sliderWrapper view, of course). When you run the app, you won't see the green background, because sliderWrapper has a height of Zero:
2) And / or add:
sliderWrapper.clipsToBounds = true
and you won't see the controls at all:
To solve this, you can add constraints:
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),
])
Now, background is visible... controls are visible... and controls can be interacted with:

Pictures disappear when I scroll down in CollectionView

I have a collectionView for display posts, I have 3 different post types (Text,Image and Video). I`ve added an imageview to cell and Im using if else codes in cellForItemAt function for display imageview for Image and Video posts or hide it with heightAnchor = 0 for text posts.
its loading correct at beginning, but when I scroll down and scroll up again images heightAnchor resetting to "0" for every posts. How can I solve this issue ?
When the posts are loaded
When I Scroll down and scroll up again
TimelinePosts CollectionViewCell
class TimelinePosts: UICollectionViewCell {
let avatar: UIImageView = {
let avatar = UIImageView()
avatar.contentMode = .scaleAspectFill
avatar.clipsToBounds = true
avatar.layer.cornerRadius = 24
avatar.translatesAutoresizingMaskIntoConstraints = false
return avatar
}()
let name: UILabel = {
let name = UILabel()
name.numberOfLines = 1
name.translatesAutoresizingMaskIntoConstraints = false
return name
}()
let content: ActiveLabel = {
let content = ActiveLabel()
content.numberOfLines = 0
content.font = UIFont.systemFont(ofSize: 15)
content.translatesAutoresizingMaskIntoConstraints = false
return content
}()
let image: UIImageView = {
let image = UIImageView()
image.contentMode = .scaleAspectFill
image.clipsToBounds = true
image.layer.cornerRadius = 12
image.translatesAutoresizingMaskIntoConstraints = false
return image
}()
let time: UILabel = {
let time = UILabel()
time.numberOfLines = 1
time.textColor = .gray
time.font = UIFont.systemFont(ofSize: 14)
time.textAlignment = .center
time.translatesAutoresizingMaskIntoConstraints = false
return time
}()
let moreButton: UIButton = {
let button = UIButton()
let image = UIImage(named: "arrow")
button.setImage(image, for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
let favoriteButton: FaveButton = {
let button = FaveButton(frame: CGRect(x:0, y:0, width: 28, height: 28), faveIconNormal: UIImage(named: "favorite"))
button.normalColor = UIColor(hexString: "#CBCBCB")
button.selectedColor = UIColor(hexString: "#FFBE00")
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
let boostButton: FaveButton = {
let button = FaveButton(frame: CGRect(x:0, y:0, width: 28, height: 28), faveIconNormal: UIImage(named: "boost-pressed"))
button.normalColor = UIColor(hexString: "#CBCBCB")
button.selectedColor = UIColor(hexString: "#6e00ff")
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
let actions: UILabel = {
let view = UILabel()
view.numberOfLines = 1
view.textAlignment = .left
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
addViews()
setupViews()
}
func addViews(){
addSubview(avatar)
addSubview(moreButton)
addSubview(time)
addSubview(name)
addSubview(content)
addSubview(image)
addSubview(favoriteButton)
addSubview(boostButton)
addSubview(actions)
}
func setupViews(){
avatar.leftAnchor.constraint(equalTo: leftAnchor, constant: 15).isActive = true
avatar.topAnchor.constraint(equalTo: topAnchor, constant: 15).isActive = true
avatar.widthAnchor.constraint(equalToConstant: 48).isActive = true
avatar.heightAnchor.constraint(equalToConstant: 48).isActive = true
moreButton.rightAnchor.constraint(equalTo: rightAnchor, constant: -18).isActive = true
moreButton.topAnchor.constraint(equalTo: topAnchor, constant: 15).isActive = true
moreButton.widthAnchor.constraint(equalToConstant: 14).isActive = true
moreButton.heightAnchor.constraint(equalToConstant: 14).isActive = true
time.leftAnchor.constraint(equalTo: leftAnchor, constant: 15).isActive = true
time.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10).isActive = true
time.widthAnchor.constraint(equalToConstant: 48).isActive = true
time.heightAnchor.constraint(equalToConstant: 20).isActive = true
name.leftAnchor.constraint(equalTo: avatar.rightAnchor, constant: 10).isActive = true
name.rightAnchor.constraint(equalTo: moreButton.leftAnchor, constant: -14).isActive = true
name.topAnchor.constraint(equalTo: topAnchor, constant: 15).isActive = true
name.heightAnchor.constraint(equalToConstant: 20).isActive = true
content.leftAnchor.constraint(equalTo: leftAnchor, constant: 73).isActive = true
content.rightAnchor.constraint(equalTo: rightAnchor, constant: -46).isActive = true
content.topAnchor.constraint(equalTo: name.bottomAnchor, constant: 5).isActive = true
image.leftAnchor.constraint(equalTo: leftAnchor, constant: 73).isActive = true
image.rightAnchor.constraint(equalTo: rightAnchor, constant: -46).isActive = true
image.topAnchor.constraint(equalTo: content.bottomAnchor, constant: 5).isActive = true
image.heightAnchor.constraint(equalToConstant: ((UIScreen.main.bounds.width - 120) * 2) / 3).isActive = true
actions.leftAnchor.constraint(equalTo: avatar.rightAnchor, constant: 10).isActive = true
actions.rightAnchor.constraint(equalTo: moreButton.leftAnchor, constant: -14).isActive = true
actions.topAnchor.constraint(equalTo: image.bottomAnchor, constant: 10).isActive = true
actions.heightAnchor.constraint(equalToConstant: 20).isActive = true
actions.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10).isActive = true
favoriteButton.rightAnchor.constraint(equalTo: rightAnchor, constant: -14).isActive = true
favoriteButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10).isActive = true
favoriteButton.widthAnchor.constraint(equalToConstant: 20).isActive = true
favoriteButton.heightAnchor.constraint(equalToConstant: 20).isActive = true
boostButton.rightAnchor.constraint(equalTo: rightAnchor, constant: -14).isActive = true
boostButton.bottomAnchor.constraint(equalTo: favoriteButton.topAnchor, constant: -12).isActive = true
boostButton.widthAnchor.constraint(equalToConstant: 20).isActive = true
boostButton.heightAnchor.constraint(equalToConstant: 20).isActive = true
/* name.backgroundColor = .yellow
content.backgroundColor = .red
image.backgroundColor = .green */
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And cellForItemAt function
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TimelinePosts", for: indexPath) as! TimelinePosts
if indexPath.row < id.count{
cell.avatar.sd_setImage(with: URL(string: avatars[indexPath.row]))
cell.time.text = hours[indexPath.row]
cell.favoriteButton.isSelected = isLike[indexPath.row]
cell.favoriteButton.isUserInteractionEnabled = true
cell.favoriteButton.tag = indexPath.row
cell.boostButton.isSelected = isBoost[indexPath.row]
cell.boostButton.isUserInteractionEnabled = true
cell.boostButton.tag = indexPath.row
let selectedArrowTap = UITapGestureRecognizer(target: self, action: #selector(self.selectedArrow))
selectedArrowTap.numberOfTapsRequired = 1
cell.moreButton.isUserInteractionEnabled = true
cell.moreButton.tag = indexPath.row
cell.moreButton.addGestureRecognizer(selectedArrowTap)
cell.content.customize { label in
label.text = content[indexPath.row]
label.hashtagColor = UIColor(hexString: "#6e00ff")
label.mentionColor = UIColor(hexString: "#6e00ff")
label.URLColor = UIColor(hexString: "#0366d6")
}
cell.content.handleHashtagTap { hashtag in
print("Success. You just tapped the \(hashtag) hashtag")
}
cell.content.handleURLTap { url in
let urlString = url.absoluteString
if urlString.hasPrefix("http://")
{
let openURL = URL(string: urlString)!
let svc = SFSafariViewController(url: openURL)
self.present(svc, animated: true, completion: nil)
}
else if urlString.hasPrefix("https://")
{
let openURL = URL(string: urlString)!
let svc = SFSafariViewController(url: openURL)
self.present(svc, animated: true, completion: nil)
}
else
{
let openURL = URL(string: "https://" + urlString)!
let svc = SFSafariViewController(url: openURL)
self.present(svc, animated: true, completion: nil)
}
}
if types[indexPath.row] == 2{
cell.image.sd_setImage(with: URL(string: images[indexPath.row]))
let selectedImageTap = UITapGestureRecognizer(target: self, action: #selector(self.photoZoom))
selectedImageTap.numberOfTapsRequired = 1
cell.image.isUserInteractionEnabled = true
cell.image.tag = indexPath.row
cell.image.addGestureRecognizer(selectedImageTap)
}else if types[indexPath.row] == 3{
cell.image.sd_setImage(with: URL(string: "https://i.ytimg.com/vi/\(self.extractYoutubeIdFromLink(link: videos[indexPath.row])!)/mqdefault.jpg"))
let playBtn = UIImageView()
playBtn.image = UIImage(named: "youtube-play")
playBtn.tag = indexPath.row
playBtn.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.videoPlay)))
playBtn.isUserInteractionEnabled = true
playBtn.translatesAutoresizingMaskIntoConstraints = false
cell.image.isUserInteractionEnabled = true
cell.image.addSubview(playBtn)
playBtn.centerXAnchor.constraint(equalTo: cell.image.centerXAnchor).isActive = true
playBtn.centerYAnchor.constraint(equalTo: cell.image.centerYAnchor).isActive = true
playBtn.widthAnchor.constraint(equalToConstant: 74).isActive = true
playBtn.heightAnchor.constraint(equalToConstant: 52).isActive = true
}else{
cell.image.heightAnchor.constraint(equalToConstant: 0).isActive = true
}
}
return cell
}
you should not modify image height by
cell.image.heightAnchor.constraint(equalToConstant: 0).isActive = true
create a new variable imageHeightConstraint in you TimelinesPosts,
set this variable in setupViews()
imageHeightConstraint = image.heightAnchor.constraint(equalToConstant: ((UIScreen.main.bounds.width - 120) * 2) / 3).isActive = true
then change the height by this
// don't miss this code.
imageHeightConstraint.constant = ((UIScreen.main.bounds.width - 120) * 2) / 3
if types[indexPath.row] == 2 {
//***************
} else if types[indexPath.row] == 3 {
//***************
} else {
imageHeightConstraint.constant = 0
}
UIScrollView is parent UICollectionView. You use UIScrollView with UIScrollViewDelegate for setup images with va scrollOffset: CGFloat follow .y (referent code)
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
var scrollOffset : CGFloat = scrollView.contentOffset.y
// process with scrollOffset
}
}

UIButton in a view with animation not detecting touch

I'm following a tutorial to create an interactive popup animation (http://www.swiftkickmobile.com/building-better-app-animations-swift-uiviewpropertyanimator/), and now would like to add buttons to the popup rather than have static text as in the tutorial.
The animation works fine, but the buttons are not detecting touch. Touching the button causes the animation to reverse, instead of printing my "test" statement.
From research, it looks to either be an issue with view hierarchies, the animation disabling the button touch, or layout/constraint issues with the button. I've tried addressing the above issues, but nothing has worked, was hoping someone might be able to help?
I've left out the code pertaining to the animation since I think the issue is to do with layout (and the animation seems to be working fine) - but it's still a lot; apologies in advance for the large amount of code.
class ViewController: UIViewController {
private let popupOffset: CGFloat = 440
private lazy var contentImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = #imageLiteral(resourceName: "Background")
return imageView
}()
private lazy var overlayView: UIView = {
let view = UIView()
view.backgroundColor = .black
view.alpha = 0
return view
}()
private lazy var popupView: UIView = {
let view = UIView()
view.backgroundColor = .white
view.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner]
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOpacity = 0.1
view.layer.shadowRadius = 10
return view
}()
private lazy var closedTitleLabel: UILabel = {
let label = UILabel()
label.text = "Hello"
label.font = UIFont.systemFont(ofSize: 16, weight: UIFont.Weight.medium)
label.textColor = UIColor.darkGray
label.textAlignment = .center
return label
}()
private lazy var openTitleLabel: UILabel = {
let label = UILabel()
label.text = "Which door will you choose?"
label.font = UIFont.systemFont(ofSize: 16, weight: UIFont.Weight.medium)
label.textColor = UIColor.darkGray
label.textAlignment = .center
label.alpha = 0
label.transform = CGAffineTransform(scaleX: 1.6, y: 1.6).concatenating(CGAffineTransform(translationX: 0, y: 15))
return label
}()
private lazy var reviewsImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = #imageLiteral(resourceName: "LabelBackground")
return imageView
}()
let stackView = UIStackView()
let buttonA = UIButton()
let buttonB = UIButton()
let buttonC = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
layout()
popupView.addGestureRecognizer(panRecognizer)
}
override var prefersStatusBarHidden: Bool {
return true
}
//Layout
private var bottomConstraint = NSLayoutConstraint()
private func layout() {
contentImageView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(contentImageView)
contentImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
contentImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
contentImageView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
contentImageView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
overlayView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(overlayView)
overlayView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
overlayView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
overlayView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
overlayView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
popupView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(popupView)
popupView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
popupView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
bottomConstraint = popupView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: popupOffset)
bottomConstraint.isActive = true
popupView.heightAnchor.constraint(equalToConstant: 500).isActive = true
closedTitleLabel.translatesAutoresizingMaskIntoConstraints = false
popupView.addSubview(closedTitleLabel)
closedTitleLabel.leadingAnchor.constraint(equalTo: popupView.leadingAnchor).isActive = true
closedTitleLabel.trailingAnchor.constraint(equalTo: popupView.trailingAnchor).isActive = true
closedTitleLabel.topAnchor.constraint(equalTo: popupView.topAnchor, constant: 20).isActive = true
openTitleLabel.translatesAutoresizingMaskIntoConstraints = false
popupView.addSubview(openTitleLabel)
openTitleLabel.leadingAnchor.constraint(equalTo: popupView.leadingAnchor).isActive = true
openTitleLabel.trailingAnchor.constraint(equalTo: popupView.trailingAnchor).isActive = true
openTitleLabel.topAnchor.constraint(equalTo: popupView.topAnchor, constant: 20).isActive = true
reviewsImageView.translatesAutoresizingMaskIntoConstraints = false
popupView.addSubview(reviewsImageView)
reviewsImageView.leadingAnchor.constraint(equalTo: popupView.leadingAnchor).isActive = true
reviewsImageView.trailingAnchor.constraint(equalTo: popupView.trailingAnchor).isActive = true
reviewsImageView.bottomAnchor.constraint(equalTo: popupView.bottomAnchor).isActive = true
reviewsImageView.heightAnchor.constraint(equalToConstant: 428).isActive = true
buttonA.backgroundColor = UIColor.clear
let heightConstraintA = buttonA.heightAnchor.constraint(equalToConstant: 135)
heightConstraintA.isActive = true
heightConstraintA.priority = UILayoutPriority(rawValue: 999)
buttonA.translatesAutoresizingMaskIntoConstraints = false
buttonA.setTitle("A", for: .normal)
buttonA.setTitleColor(UIColor.darkGray, for: .normal)
buttonA.backgroundColor = UIColor.clear
buttonA.addTarget(self, action: #selector(buttonATapped(sender:)), for: .touchDown)
//self.popupView.addSubview(buttonA)
buttonB.backgroundColor = UIColor.clear
let heightConstraintB = buttonB.heightAnchor.constraint(equalToConstant: 135)
heightConstraintB.isActive = true
heightConstraintB.priority = UILayoutPriority(rawValue: 999)
buttonB.translatesAutoresizingMaskIntoConstraints = false
buttonB.setTitle("B", for: .normal)
buttonB.setTitleColor(UIColor.darkGray, for: .normal)
buttonB.backgroundColor = UIColor.clear
//self.popupView.addSubview(buttonB)
buttonC.backgroundColor = UIColor.clear
let heightConstraintC = buttonC.heightAnchor.constraint(equalToConstant: 135)
heightConstraintC.isActive = true
heightConstraintC.priority = UILayoutPriority(rawValue: 999)
buttonC.translatesAutoresizingMaskIntoConstraints = false
buttonC.setTitle("C", for: .normal)
buttonC.setTitleColor(UIColor.darkGray, for: .normal)
buttonC.backgroundColor = UIColor.clear
//self.popupView.addSubview(buttonC)
popupView.addSubview(stackView)
stackView.backgroundColor = UIColor.clear
stackView.addArrangedSubview(buttonA)
stackView.addArrangedSubview(buttonB)
stackView.addArrangedSubview(buttonC)
stackView.translatesAutoresizingMaskIntoConstraints = false
popupView.addSubview(stackView)
stackView.leadingAnchor.constraint(equalTo: popupView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: popupView.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: popupView.bottomAnchor).isActive = true
stackView.heightAnchor.constraint(equalToConstant: 428).isActive = true
stackView.axis = .vertical
stackView.distribution = .fill
stackView.translatesAutoresizingMaskIntoConstraints = false
}
#objc func buttonATapped(sender: UIButton) {
print ("test")
}
private func animateTransitionIfNeeded(to state: State, duration: TimeInterval) {
//Animation code
}
#objc private func popupViewPanned(recognizer: UIPanGestureRecognizer) {
//Animation code
}
}
***For anyone else having the same issue, here is how I solved it, thanks to #OverD:
I removed the reviewsImageView completely (because that was only for color in my case, and I can easily add the color to the UIButton instead) Then instead of adding the buttons to the popupView, I added them to the stack view, and the stack view to the popupView. Lastly, my syntax for addTarget was not correct, and for some reason changing it to touchDown instead of touchUpInside made it work.
I noticed from the provided code that you are adding the same buttons multiple times, once as a subview of the popView and once in the stackView. Also you are not assigning any targets for the buttons.
I hope this helps

Setting up UI constraint (Image and stackView inside a UIView) programmatically in Swift

I'm trying to build a custom AD when the app opens it pop up some UIViews and image and two buttons then control it from my Firebase, for now I have problem adding the adImage and buttonsStack(contains 2 buttons) inside my backView programmatically and so far nothing works ..
I need the image takes ~ %75 of the backView up and the buttonsStack ~ %25 of the rest
here some code and I have upload it to my GitHub
import UIKit
class ViewController: UIViewController {
let backroundView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .black
view.alpha = 0.5
return view
}()
let backView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .white
view.layer.cornerRadius = 15
return view
}()
let adImage: UIImageView = {
var image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.contentMode = .scaleAspectFill
return image
}()
let buttonsStack: UIStackView = {
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.alignment = UIStackViewAlignment.fill
stack.axis = UILayoutConstraintAxis.vertical
stack.distribution = .equalSpacing
stack.spacing = 8
stack.backgroundColor = UIColor.red
return stack
}()
let actionButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Open", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = UIColor(red: 0, green: 0.60, blue: 1, alpha: 1)
button.layer.cornerRadius = 8
button.titleLabel?.adjustsFontSizeToFitWidth = true
button.titleLabel?.textAlignment = .center
return button
}()
let dismessButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Exit", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = .lightGray
button.layer.cornerRadius = 8
button.titleLabel?.adjustsFontSizeToFitWidth = true
button.titleLabel?.textAlignment = .center
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
func setupUI(){
// backroundView
view.addSubview(backroundView)
backroundView.frame = view.frame
// backView
view.addSubview(backView)
backView.topAnchor.constraint(equalTo: view.topAnchor, constant: 80).isActive = true
backView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -50).isActive = true
backView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -25).isActive = true
backView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 25).isActive = true
// adImage
backView.addSubview(adImage)
adImage.image = UIImage(named: "testImage")
adImage.topAnchor.constraint(equalTo: backView.topAnchor).isActive = true
adImage.rightAnchor.constraint(equalTo: backView.rightAnchor).isActive = true
adImage.leftAnchor.constraint(equalTo: backView.leftAnchor).isActive = true
adImage.heightAnchor.constraint(equalTo: backView.heightAnchor, multiplier: 0.50).isActive = true
// buttonsStack
buttonsStack.addArrangedSubview(actionButton)
buttonsStack.addArrangedSubview(dismessButton)
backView.addSubview(buttonsStack)
buttonsStack.topAnchor.constraint(equalTo: backView.topAnchor, constant: 15).isActive = true
buttonsStack.bottomAnchor.constraint(equalTo: backView.bottomAnchor, constant: -15).isActive = true
buttonsStack.rightAnchor.constraint(equalTo: backView.rightAnchor, constant: -15).isActive = true
buttonsStack.leftAnchor.constraint(equalTo: backView.leftAnchor, constant: 15).isActive = true
}
}
For the image to take 0.75 change this
adImage.heightAnchor.constraint(equalTo: backView.heightAnchor, multiplier: 0.50).isActive = true
to
adImage.heightAnchor.constraint(equalTo: backView.heightAnchor, multiplier: 0.75).isActive = true
//
then the buttonStack should goes under it so change this
buttonsStack.topAnchor.constraint(equalTo: backView.topAnchor, constant: 15).isActive = true
to
buttonsStack.topAnchor.constraint(equalTo: adImage.bottomAnchor, constant: 15).isActive = true

Resources