2
votes

iOS: UIScrollView n'échantillonne pas toutes les valeurs

Je voudrais savoir précisément quand le décalage du scrollView atteint le centre de la page. Malheureusement, scrollViewDidScroll n'échantillonne pas toutes les valeurs. Donc je n'entre pas le if à chaque fois que je passe au centre de l'écran. Comment puis-je surmonter cela?

func scrollViewDidScroll(_ scrollView: UIScrollView) {

        let page = Int(scrollView.contentOffset.x / scrollView.frame.size.width)
        let offsetInPage = (scrollView.contentOffset.x / scrollView.frame.size.width) - CGFloat(page)

        if offsetInPage == 0.5 { // Does not called every time

            toggleColors()
        }
}


3 commentaires

Pourquoi n'utilisez-vous pas une plage (par exemple, 0,45 .. <0,55) pour le vérifier?


toute comparaison ( == ) avec des nombres à virgule flottante doit déclencher une alarme.


pourquoi ça devrait? uniquement si la valeur offsetInPage == 0.5


3 Réponses :


0
votes

Parce que vous utilisez des flottants pour comparer, je suggère d'utiliser des plages comme ici:

func scrollViewDidScroll(_ scrollView: UIScrollView) {

    let page = Int(scrollView.contentOffset.x / scrollView.frame.size.width)
    let offsetInPage = (scrollView.contentOffset.x / scrollView.frame.size.width) - CGFloat(page)

    if (0.45..<0.55).contains(offsetInPage) {
        print("near to center")
        toggleColors()
    }

}

les fonctions telles que scrollViewDidScroll ne peuvent pas se déclencher sur toutes les valeurs flottantes, car elles sont impossible! Vous devez vérifier comme ci-dessus ou essayer d'attraper 2 valeurs lowerValue , biggerValue.


1 commentaires

Je pense que cela produira des résultats indésirables car cela sera déclenché plusieurs fois lorsque le scrollview défile dans une direction.



1
votes

contentOffset est mis à jour en gérant les gestes tactiles, ce qui ne peut pas être fait en continu car ce n'est pas comme si vous receviez des messages de l'écran tactile à chaque changement, mais à la place, le système vérifie l'état de l'écran tactile dans certains boucle. Dans ce cas, c'est runloop. Il se peut donc que des changements se produisent entre les itérations de la boucle d'exécution et ne soient pas capturés par l'application. Théoriquement, dans certains cas, l'écart pourrait être encore plus que la page entière.

Il est donc préférable de vérifier quelle zone est visible maintenant et de calculer la couleur à appliquer lorsque cette zone est visible plutôt que d'attendre une valeur exacte de contentOffset pour apparaître

Selon votre code, il semble que vous vouliez simplement changer de couleur lorsque l'utilisateur fait défiler afin que toutes les pages impaires aient une couleur et toutes les pages paires - une autre. Si c'est le cas, je le ferais comme ça:

func scrollViewDidScroll(_ scrollView: UIScrollView) {

    let page = Int(scrollView.contentOffset.x / scrollView.frame.size.width)
    let isOddPage = (page % 2 == 1)
    let offsetInPage = (scrollView.contentOffset.x / scrollView.frame.size.width) - CGFloat(page)

    if (isOddPage && offsetInPage < 0.5) || (!isOddPage && offsetInPage >= 0.5) {
        switchToOddColor()
    } else {
        switchToEvenColor()
    }
}

Ou quelque chose comme ça. Peut-être que la condition est plus complexe dans votre cas et que vous avez peut-être plus que deux couleurs, mais la stratégie est la même.

Il est également utile de vérifier si les couleurs ne sont pas déjà définies sur la valeur nécessaire dans switchToOddColor et switchToEvenColor pour éviter de faire un travail supplémentaire.


1 commentaires

J'aime beaucoup votre intro et votre explication!



1
votes

Je vous suggère de 1) vérifier une valeur représentant si le décalage de page est supérieur à la moitié et 2) de basculer les couleurs uniquement si la valeur est modifiée:

scrollView.rx.didSCroll
    .map { [weak scrollView] _ in scrollView?.offsetInPage ?? 0 }
    .map { $0 > 0.5 }
    .distinctUntilChanged() // this line prevents toggleColors() from being called for the same values multiple times
    .subscribe(onNext: { [weak self] _ in
        self?.toggleColors()
    })
    .disposed(by: disposeBag)

extension UIScrollView {
    var offsetInPage: CGFloat {
        let page = contentOffset.x / frame.size.width
        return page - floor(page)
    }
}

Peut-être que c'est un genre de réponse indésirable cependant, voici également la version RxSwift :

var isOverHalf: Bool = true
func scrollViewDidScroll(_ scrollView: UIScrollView) {

        let page = Int(scrollView.contentOffset.x / scrollView.frame.size.width)
        let offsetInPage = (scrollView.contentOffset.x / scrollView.frame.size.width) - CGFloat(page)

        let isOverHalf = offsetInPage > 0.5
        If self.isOverHalf != isOverHalf {
            toggleColors()
        }
        self.isOverHalf = isOverHalf
}


0 commentaires