Назначить делегат UIScrollView и делегат UICollectionView одному и тому же классу

#ios #swift #uicollectionview #contentoffset #uicollectionviewdelegate

#iOS #swift #uicollectionview #contentoffset #uicollectionviewdelegate

Вопрос:

У меня есть подкачка UIScrollView , каждая страница заполняется другим контроллером представления. Над scrollview находится UICollectionView , который действует как строка меню. При прокрутке страниц scrollview строка меню немного перемещается. Вы можете видеть на gif слева.

При присвоении их делегатам разных классов все работает правильно, как показано на gif слева. НО установка их в один и тот же класс портит UICollectionView поведение.

Как мне назначить их делегатам один и тот же класс?


 import UIKit

class MenuView: UIView, UICollectionViewDataSource {

    let collcetionView: UICollectionView = {
        let view = UICollectionView()
        // Setup...
        return view
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupCollectionView()
        collcetionView.dataSource = self
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    fileprivate func setupCollectionView() {
        // Autolayout code...
    }

    // Datasource methods to populate collection view cells
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    // Populate cell code...
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        // Populate cell code...
    }
}


class MainView: UIView {
    // Contains paging scroll view and menu bar
    var menu: MenuView!

    let scrollView: UIScrollView = {
        let view = UIScrollView()
        // Setup...
        return view
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupMenu()
        setupScrollView()

    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    fileprivate func setupScrollView() {
        // Autolayout code...
    }
    fileprivate func setupMenu() {
        menu = MenuView()
        // Autolayout code...
    }
}


class MainController: UIViewController, UIScrollViewDelegate, UICollectionViewDelegate {
    var mainView: MainView!

    override func loadView() {
        super.loadView()
        mainView = MainView()
        view = mainView
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        mainView.scrollView.delegate = self
        mainView.menu.collcetionView.delegate = self // <<--- THIS IS WHAT BREAKS EVERYTHING
    }


func scrollViewDidScroll(_ scrollView: UIScrollView) {

        // Moving menu bar with page scroll
        mainView.menu.collectionView.contentOffset = CGPoint(x: scrollView.contentOffset.x/SCROLL_FACTOR - (firstIndexPosition/SCROLL_FACTOR - difference/2), y: 0)



        // Fade in and out highlighted state of menu bar cell
        let exactPage = (scrollView.contentOffset.x / SCREEN_WIDTH)
        let currentPage = (scrollView.contentOffset.x / SCREEN_WIDTH).rounded()
        let unitExact = currentPage - exactPage
        //print(String(format: "exact: %.2f, ", exactPage)   "current: (currentPage), "   String(format: "unit: %.2f, ", unitExact))

        if exactPage > currentPage {
            // exact > current
            // fade out/in left icon
            // select current
            let unit = 0 - unitExact // from 0 - 0.5
            let cell = mainView.menu.collectionView.cellForItem(at: IndexPath(item: Int(currentPage), section: 0)) as! MenuBarCell
            let mapped = unit.map(from: 0.0...0.5, to: 0...149.0)
            print(cell)
            setCellColor(cell: cell, value: mapped)
        } else if exactPage < currentPage {
            // exact < current
            // fade out/in right icon
            // select current
            let unit = unitExact // from 0 - 0.5
            let cell = mainView.menu.collectionView.cellForItem(at: IndexPath(item: Int(currentPage), section: 0)) as! MenuBarCell
            let mapped = unit.map(from: 0.0...0.5, to: 0...149.0)
            setCellColor(cell: cell, value: mapped)
        } else if exactPage == currentPage {
            // exact = current
            // darken that icon
            // select current
        }
    }
}
  

Комментарии:

1. Можете ли вы показать код для scrollViewDidScroll ? Вы проверяете, какой вид прокрутки прокручивается? Представления коллекции являются представлениями прокрутки, и для них также будет вызван этот метод делегирования

2. Я никогда даже не думал, что это будет применено как к CollectionViewCell, так и к scrollview! Есть ли способ это исправить? И я обновлю свой вопрос с scrollViewDidScroll кодом

3. В scrollViewDidScroll выполните: guard scrollView != myCollectionView else { return } ; guard scrollView == myScrollViewAndNotMyCollection else { return } и т.д. Как сказал Paulw11, это потому, что UICollectionView наследуется от UIScrollView .

4. Спасибо! Это сработало отлично. Но есть ли стандартное решение этой проблемы. Например, если бы у меня было 2 UIScrollView s в одном представлении? Или использование guard инструкции является нормой?

Ответ №1:

UICollectionView и UITableView наследовать от UIScrollView ,

scrollViewDidScroll Метод делегирования будет вызываться как для вашего представления коллекции, так и для вашего scrollview, если вы установите для delegate обоих объектов один и тот же класс.

Вам нужно проверить, почему scrollViewDidScroll вызывается, и действовать соответствующим образом.

Самый простой подход — это guard оператор, который возвращает, если метод делегата не вызывается для интересующего вас вида прокрутки.

Если вам нужно выполнить другой код в зависимости от используемого вида прокрутки, вы могли бы использовать серию if инструкций или switch оператор.

 func scrollViewDidScroll(_ scrollView: UIScrollView) {

   guard scrollView == self.scrollView else {
       return
   }

    // Moving menu bar with page scroll
    mainView.menu.collectionView.contentOffset = CGPoint(x: scrollView.contentOffset.x/SCROLL_FACTOR - (firstIndexPosition/SCROLL_FACTOR - difference/2), y: 0)



    // Fade in and out highlighted state of menu bar cell
    let exactPage = (scrollView.contentOffset.x / SCREEN_WIDTH)
    let currentPage = (scrollView.contentOffset.x / SCREEN_WIDTH).rounded()
    let unitExact = currentPage - exactPage
    //print(String(format: "exact: %.2f, ", exactPage)   "current: (currentPage), "   String(format: "unit: %.2f, ", unitExact))

    if exactPage > currentPage {
        // exact > current
        // fade out/in left icon
        // select current
        let unit = 0 - unitExact // from 0 - 0.5
        let cell = mainView.menu.collectionView.cellForItem(at: IndexPath(item: Int(currentPage), section: 0)) as! MenuBarCell
        let mapped = unit.map(from: 0.0...0.5, to: 0...149.0)
        print(cell)
        setCellColor(cell: cell, value: mapped)
    } else if exactPage < currentPage {
        // exact < current
        // fade out/in right icon
        // select current
        let unit = unitExact // from 0 - 0.5
        let cell = mainView.menu.collectionView.cellForItem(at: IndexPath(item: Int(currentPage), section: 0)) as! MenuBarCell
        let mapped = unit.map(from: 0.0...0.5, to: 0...149.0)
        setCellColor(cell: cell, value: mapped)
    } else if exactPage == currentPage {
        // exact = current
        // darken that icon
        // select current
    }
}