如何从嵌入在UIScrollView内的UiView内的CollectionView接收触摸事件

时间:2018-09-16 04:24:22

标签: ios swift uiscrollview uicollectionview touch-event

我的表单很长,创建了一个containerView(UIView),其中包含一个自定义Calendar和几个textField。 UIView嵌入在scrollView中,因此我可以滚动长格式。

ScrollView
   -ContainerView // UIView
       -CustomCalendar // contains a UICollectionView
       -TextField
       -TextField
        //etc etc..

苹果说:

  

您不应在UIScrollView中嵌入UIWebView或UITableView对象   对象。如果这样做,可能会导致意外行为,因为触摸   这两个对象的事件可能会混合在一起并被错误地处理。

要解决这个问题,我尝试禁用CalendarView上的滚动,但没有任何区别:

// this is inside the CalenderView file
myCollectionView.isScrollEnabled = false

我从herehere那里获得了日历,它使用collectionView显示日期以及何时选择了didSelectItem触发日期。

问题是因为自定义日历包含collectionView,并且当我触摸日期didSelecetItem无法运行时,它位于scrollView内。

如何获取日历的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的文件。我没有包含另外2个文件,因为这些只是创建星期和几个月的视图。

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。

enter image description here

1 个答案:

答案 0 :(得分:1)

我找到了@Jaydeep的answer here,并从@zambrey获得了explanation

这个想法是告诉手势识别器不要吞下触摸事件。为此,您需要将singleTap的cancelsTouchesInView属性设置为NO,默认情况下为YES。

您想告诉scrollView不要吃掉所有的触摸事件,您可以通过向其添加一个点击手势识别器并设置点击的

来做到这一点
singleTap.cancelsTouchesInView = false

我在将CalenderView嵌入ScrollView内的UIView的文件中添加了tapGesture,而不是具有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
}