如何从嵌入在UIScrollView内部的UiView内嵌的CollectionView接收触摸事件
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何从嵌入在UIScrollView内部的UiView内嵌的CollectionView接收触摸事件相关的知识,希望对你有一定的参考价值。
我有一个很长的表单,并创建了一个包含自定义日历和几个textFields的containerView(UIView)。 UIView嵌入在scrollView中,以便我可以滚动长格式。
ScrollView
-ContainerView // UIView
-CustomCalendar // contains a UICollectionView
-TextField
-TextField
//etc etc..
Apple说:
您不应该在UIScrollView对象中嵌入UIWebView或UITableView对象。如果这样做,可能会导致意外行为,因为两个对象的触摸事件可能混淆和错误处理。
为了解决这个问题,我尝试在CalendarView上禁用滚动,但它没有任何区别:
// this is inside the CalenderView file
myCollectionView.isScrollEnabled = false
我从here和here获得日历,它使用collectionView来显示日期以及何时选择日期,触发didSelectItem。
问题是,因为自定义日历包含一个collectionView,当我触摸日期时,它在scrollView中,didSelecetItem将无法运行。
如何让日历的collectionView接收触摸事件?
当日历不在scrollView中时,日历工作正常
let scrollView: UIScrollView = {
let sv = UIScrollView()
sv.translatesAutoresizingMaskIntoConstraints = false
sv.showsVerticalScrollIndicator = false
return sv
}()
let containerView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .white
return view
}()
let calendarView: CalendarView = {
let view = CalendarView() // contains a collectionView
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
createAnchors()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
scrollView.contentSize = CGSize(width: scrollView.contentSize.width, height: 1000)
}
func createAnchors() {
view.addSubview(scrollView)
scrollView.addSubview(containerView)
scrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
containerView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
containerView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
containerView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
calendarView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: verticalPadding).isActive = true
calendarView.leftAnchor.constraint(equalTo: containerView.leftAnchor, constant: 10).isActive = true
calendarView.rightAnchor.constraint(equalTo: containerView.rightAnchor, constant: -10).isActive = true
calendarView.heightAnchor.constraint(equalToConstant: 290).isActive = true
// textFields are added...
}
这是CalenderView的文件。还有两个我没有包含的文件,因为这些文件只是创建周和月的视图。
class CalendarView: UIView, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, MonthViewDelegate {
let myCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
let myCollectionView=UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
myCollectionView.showsHorizontalScrollIndicator = false
myCollectionView.translatesAutoresizingMaskIntoConstraints=false
myCollectionView.backgroundColor=UIColor.clear
myCollectionView.allowsMultipleSelection=false
myCollectionView.isScrollEnabled = false
return myCollectionView
}()
var numOfDaysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31]
var currentMonthIndex: Int = 0
var currentYear: Int = 0
var presentMonthIndex = 0
var presentYear = 0
var todaysDate = 0
var firstWeekDayOfMonth = 0 //(Sunday-Saturday 1-7)
override init(frame: CGRect) {
super.init(frame: frame)
initializeView()
}
func initializeView() {
currentMonthIndex = Calendar.current.component(.month, from: Date())
currentYear = Calendar.current.component(.year, from: Date())
todaysDate = Calendar.current.component(.day, from: Date())
firstWeekDayOfMonth=getFirstWeekDay()
//for leap years, make february month of 29 days
if currentMonthIndex == 2 && currentYear % 4 == 0 {
numOfDaysInMonth[currentMonthIndex-1] = 29
}
//end
presentMonthIndex=currentMonthIndex
presentYear=currentYear
setupViews()
myCollectionView.delegate=self
myCollectionView.dataSource=self
myCollectionView.register(dateCVCell.self, forCellWithReuseIdentifier: "Cell")
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return numOfDaysInMonth[currentMonthIndex-1] + firstWeekDayOfMonth - 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell=collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! dateCVCell
cell.backgroundColor=UIColor.clear
if indexPath.item <= firstWeekDayOfMonth - 2 {
cell.isHidden=true
} else {
let calcDate = indexPath.row-firstWeekDayOfMonth+2
cell.isHidden=false
cell.lbl.text="(calcDate)"
if calcDate < todaysDate && currentYear == presentYear && currentMonthIndex == presentMonthIndex {
cell.isUserInteractionEnabled=false
cell.lbl.textColor = UIColor.lightGray
} else {
cell.isUserInteractionEnabled=true
cell.lbl.textColor = Style.activeCellLblColor
}
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell=collectionView.cellForItem(at: indexPath)
cell?.backgroundColor=Colors.darkRed
let lbl = cell?.subviews[1] as! UILabel
lbl.textColor=UIColor.white
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell=collectionView.cellForItem(at: indexPath)
cell?.backgroundColor=UIColor.clear
let lbl = cell?.subviews[1] as! UILabel
lbl.textColor = Style.activeCellLblColor
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = collectionView.frame.width/7 - 8
let height: CGFloat = 30
return CGSize(width: width, height: height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 8.0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 8.0
}
func getFirstWeekDay() -> Int {
let day = ("(currentYear)-(currentMonthIndex)-01".date?.firstDayOfTheMonth.weekday)!
//return day == 7 ? 1 : day
return day
}
func didChangeMonth(monthIndex: Int, year: Int) {
currentMonthIndex=monthIndex+1
currentYear = year
//for leap year, make february month of 29 days
if monthIndex == 1 {
if currentYear % 4 == 0 {
numOfDaysInMonth[monthIndex] = 29
} else {
numOfDaysInMonth[monthIndex] = 28
}
}
//end
firstWeekDayOfMonth=getFirstWeekDay()
myCollectionView.reloadData()
monthView.btnLeft.isEnabled = !(currentMonthIndex == presentMonthIndex && currentYear == presentYear)
}
func setupViews() {
addSubview(monthView)
monthView.topAnchor.constraint(equalTo: topAnchor).isActive=true
monthView.leftAnchor.constraint(equalTo: leftAnchor).isActive=true
monthView.rightAnchor.constraint(equalTo: rightAnchor).isActive=true
monthView.heightAnchor.constraint(equalToConstant: 35).isActive=true
monthView.delegate=self
addSubview(weekdaysView)
weekdaysView.topAnchor.constraint(equalTo: monthView.bottomAnchor).isActive=true
weekdaysView.leftAnchor.constraint(equalTo: leftAnchor).isActive=true
weekdaysView.rightAnchor.constraint(equalTo: rightAnchor).isActive=true
weekdaysView.heightAnchor.constraint(equalToConstant: 30).isActive=true
addSubview(myCollectionView)
myCollectionView.topAnchor.constraint(equalTo: weekdaysView.bottomAnchor, constant: 0).isActive=true
myCollectionView.leftAnchor.constraint(equalTo: leftAnchor, constant: 0).isActive=true
myCollectionView.rightAnchor.constraint(equalTo: rightAnchor, constant: 0).isActive=true
myCollectionView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive=true
}
let monthView: MonthView = {
let v=MonthView()
v.translatesAutoresizingMaskIntoConstraints=false
return v
}()
let weekdaysView: WeekdaysView = {
let v=WeekdaysView()
v.translatesAutoresizingMaskIntoConstraints=false
return v
}()
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
//get first day of the month
extension Date {
var weekday: Int {
return Calendar.current.component(.weekday, from: self)
}
var firstDayOfTheMonth: Date {
return Calendar.current.date(from: Calendar.current.dateComponents([.year,.month], from: self))!
}
}
//get date from string
extension String {
static var dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
return formatter
}()
var date: Date? {
return String.dateFormatter.date(from: self)
}
}
仅用于CalendarView的视觉效果。日期(1-30)是collectionView。
我在@Jaydeep找到了answer here并从@zambrey获得了explanation。
想法是告诉手势识别器不要吞下触摸事件。要执行此操作,您需要将singleTap的cancelsTouchesInView属性设置为NO,默认情况下为YES。
你想告诉scrollView不要吃掉所有的触摸事件,你可以通过添加一个单击手势识别器来设置它并设置水龙头
singleTap.cancelsTouchesInView = false
我在文件中添加了轻触Gesture,它将日历视图嵌入ScrollView内部的UIView内,而不是具有collectionView的文件。
override func viewDidLoad() {
super.viewDidLoad()
createAnchors()
// add these 4 lines
let singleTap = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
singleTap.cancelsTouchesInView = false
singleTap.numberOfTapsRequired = 1
scrollView.addGestureRecognizer(singleTap)
}
// add this target method. I didn't add any code whatsoever inside of it
func handleTap(_ recognizer: UITapGestureRecognizer) {
// I literally left this blank
}
以上是关于如何从嵌入在UIScrollView内部的UiView内嵌的CollectionView接收触摸事件的主要内容,如果未能解决你的问题,请参考以下文章
如何从 Swift 中嵌入 UITableView 的文本字段中检索数据?
当场景嵌入到 NavigationController 中时,如何防止 UIScrollView 被下推?