anyone please help me, I have a drop down but the I can't select it the second row. I can't figure out why, I try playing with bringSubviewsToFront or back. I try using layer.zPosition it still can't select the second row, but when I choose the first row it work. is it my auto layout setup incorrectly?
here is my ui and code setup
// This is my custom Button
class GDropdownSchedule: UIButton {
let headerLbl = GTitleLabel(name: "Schedule Type".localized(), fontSize: 13, color: #colorLiteral(red: 0.4588235294, green: 0.4941176471, blue: 0.5647058824, alpha: 1))
let bodyLbl = GSubtitleLabel(name: "Additional Note".localized(), fontSize: 16, color: #colorLiteral(red: 0.09803921569, green: 0.09803921569, blue: 0.09803921569, alpha: 1))
let dropDownIV = GIconImageView(img: #imageLiteral(resourceName: "down-chevron"))
var isOpen = false
let dropView = DropDownView()
var delegate: AddScheduleVCDelegate?
var height: NSLayoutConstraint!
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func configure() {
backgroundColor = #colorLiteral(red: 0.9764705882, green: 0.9764705882, blue: 0.9764705882, alpha: 1)
layer.cornerRadius = 5
layer.borderWidth = 1
layer.borderColor = #colorLiteral(red: 0.9411764706, green: 0.9411764706, blue: 0.9450980392, alpha: 1)
dropView.completion = { text in
self.bodyLbl.text = text
self.bodyLbl.font = UIFont(name: "NunitoSans-Regular", size: 12)
self.delegate?.toggleHide(selected: text)
self.dismissDropDown()
}
}
override func didMoveToSuperview() {
addSubview(headerLbl)
headerLbl.anchor(top: topAnchor, trailing: nil, bottom: nil, leading: leadingAnchor, topPadding: 10, rightPadding: 0, bottomPadding: 0, leftPadding: 10, width: 70, height: 18)
addSubview(bodyLbl)
bodyLbl.anchor(top: headerLbl.bottomAnchor, trailing: nil, bottom: bottomAnchor, leading: leadingAnchor, topPadding: 2, rightPadding: 10, bottomPadding: 10, leftPadding: 10, width: 0, height: 0)
addSubview(dropDownIV)
dropDownIV.tintColor = #colorLiteral(red: 0.2549019608, green: 0.3019607843, blue: 0.3568627451, alpha: 1)
dropDownIV.anchor(top: nil, trailing: trailingAnchor, bottom: bottomAnchor, leading: nil, topPadding: 0, rightPadding: 18, bottomPadding: 17, leftPadding: 0, width: 12, height: 10)
addSubview(dropView)
dropView.translatesAutoresizingMaskIntoConstraints = false
dropView.layer.zPosition = 1
height = dropView.heightAnchor.constraint(equalToConstant: 0)
NSLayoutConstraint.activate([
dropView.topAnchor.constraint(equalTo: headerLbl.bottomAnchor),
dropView.leadingAnchor.constraint(equalTo: leadingAnchor),
dropView.trailingAnchor.constraint(equalTo: trailingAnchor)
])
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if isOpen == false {
isOpen = true
NSLayoutConstraint.deactivate([height])
if self.dropView.tableView.contentSize.height > 150 {
height.constant = 150
} else {
height.constant = dropView.tableView.contentSize.height
}
NSLayoutConstraint.activate([height])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.dropView.layoutIfNeeded()
self.dropView.center.y += self.dropView.frame.height / 2
})
} else {
isOpen = false
NSLayoutConstraint.deactivate([height])
height.constant = 0
NSLayoutConstraint.activate([height])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.dropView.center.y -= self.dropView.frame.height / 2
self.dropView.layoutIfNeeded()
})
}
}
func dismissDropDown() {
isOpen = false
NSLayoutConstraint.deactivate([height])
height.constant = 0
NSLayoutConstraint.activate([height])
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: {
self.dropView.center.y -= self.dropView.frame.height / 2
self.dropView.layoutIfNeeded()
})
}
}
class DropDownView: UIView, UITableViewDelegate, UITableViewDataSource {
let tableView = UITableView()
var options = [String]()
var completion: ((String) -> Void)?
var isHideSchedule = false
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func configure() {
translatesAutoresizingMaskIntoConstraints = false
addSubview(tableView)
tableView.backgroundColor = #colorLiteral(red: 0.9764705882, green: 0.9764705882, blue: 0.9764705882, alpha: 1)
tableView.separatorStyle = .none
tableView.delegate = self
tableView.dataSource = self
tableView.anchor(top: topAnchor, trailing: trailingAnchor, bottom: bottomAnchor, leading: leadingAnchor, topPadding: 0, rightPadding: 0, bottomPadding: 0, leftPadding: 0, width: 0, height: 0)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return options.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = options[indexPath.row]
cell.textLabel?.font = UIFont(name: "NunitoSans-Regular", size: 12)
cell.textLabel?.textColor = #colorLiteral(red: 0.09803921569, green: 0.09803921569, blue: 0.09803921569, alpha: 1)
cell.backgroundColor = #colorLiteral(red: 0.9764705882, green: 0.9764705882, blue: 0.9764705882, alpha: 1)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
completion?(options[indexPath.row])
tableView.deselectRow(at: indexPath, animated: true)
}
}
// This is in my viewController, the chooseScheduleDropDown is my customButton
[chooseScheduleDropDown, entryView, chooseDateView, chooseClass, startTimeView, endTimeView, descriptionView, saveBtn].forEach {
v in
v.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(v)
}
scrollView.insertSubview(entryView, belowSubview: chooseScheduleDropDown)
Because of the hit-test mechanism
You add the dropView on the Button GDropdownSchedule
The layout is
addSubview(dropView)
dropView.translatesAutoresizingMaskIntoConstraints = false
dropView.layer.zPosition = 1
height = dropView.heightAnchor.constraint(equalToConstant: 0)
NSLayoutConstraint.activate([
dropView.topAnchor.constraint(equalTo: headerLbl.bottomAnchor),
dropView.leadingAnchor.constraint(equalTo: leadingAnchor),
dropView.trailingAnchor.constraint(equalTo: trailingAnchor)
])
From the looking and the existing code,
dropView's frame is out of the Button GDropdownSchedule
's bounds Partly.
So u can see it , and your clicking does not work.
To override the hit-test mechanism is OK
class GDropdownSchedule: UIButton {
// ...
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// if the button is hidden/disabled/transparent it can't be hit
if self.isHidden || !self.isUserInteractionEnabled || self.alpha < 0.01 { return nil }
let dropViewF = dropView.frame
var index = 9
if bounds.contains(point){
index = 0
}
if dropViewF.contains(point){
index = 1
}
switch index {
case 0:
for subV in subviews.reversed(){
let realPoint = subV.convert(point, from: self)
let hit = subV.hitTest(realPoint, with: event)
if let v = hit{
return v
}
}
return self
case 1:
if dropView.alpha > 0.01{
let realPoint = dropView.convert(point, from: self)
let hit = dropView.hitTest(realPoint, with: event)
if let v = hit{
return v
}
}
default:
()
}
return nil
}
}
hitTest(_:with:)
This method traverses the view hierarchy by calling the point(inside:with:) method of each subview to determine which subview should receive a touch event.
If point(inside:with:) returns true, then the subview’s hierarchy is similarly traversed until the frontmost view containing the specified point is found. If a view does not contain the point, its branch of the view hierarchy is ignored.
You rarely need to call this method yourself, but you might override it to hide touch events from subviews.