Glitchy UIScrollView бесконечный свиток - PullRequest
1 голос
/ 16 апреля 2019

Я пытаюсь реализовать UIScrollView с бесконечной страницей на основе Advanced ScrollView Techniques из WWDC 2011 . Проблема, с которой я сталкиваюсь, заключается в том, что при прокрутке экран продолжает прыгать назад, а не двигаться вперед в массиве. Есть ли способ создать этот эффект. Ниже приведен код, который я реализовал до сих пор.

    import Foundation
    import UIKit

    class CustomScrollView:UIScrollView{
        var label1:CustomLabel!
        var label2:CustomLabel!
        var label3:CustomLabel!

        var labels:[UILabel]!
        var visibleLabels:[UILabel]!

        var recycledPages:Set<CustomLabel>!
        var visiblePages:Set<CustomLabel>!

        override init(frame: CGRect) {
            super.init(frame: frame)
            indicatorStyle = .white

            recycledPages = Set<CustomLabel>()
            visiblePages = Set<CustomLabel>()

            var firstScreenPostion:CGRect = CGRect(origin: CGPoint(x: 0 * bounds.width, y: 0), size: bounds.size)
            var secondeScreenPosition:CGRect = CGRect(origin: CGPoint(x: 1 * bounds.width, y: 0), size: bounds.size)
            var thirdScreenPosition:CGRect = CGRect(origin: CGPoint(x: 2 * bounds.width, y: 0), size: bounds.size)

            label1 = CustomLabel(frame: firstScreenPostion)
            label1.backgroundColor = .red
            label1.text = "1"

            label2 = CustomLabel(frame: secondeScreenPosition)
            label2.backgroundColor = .green
            label2.text = "2"

            label3 = CustomLabel(frame: thirdScreenPosition)
            label3.backgroundColor = .blue
            label3.text = "3"

            visibleLabels = [label1,label2,label3]
            labels = [label1,label2,label3]
        }

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

        func recenterIfNecessary(){
            let currentOffset = contentOffset
            let contentWidth = contentSize.width
            let centerOffset = (contentWidth - bounds.width)/2
            let distanceFromCenter = abs(currentOffset.x - centerOffset)

            if distanceFromCenter > contentWidth/4{
                self.contentOffset = CGPoint(x: centerOffset, y: 0)

                for label in visibleLabels{
                    var center = label.center
                    center.x += centerOffset - currentOffset.x
                    label.center = center
                }

            }
        }

        override func layoutSubviews() {
            recenterIfNecessary()
            let visibleBounds = bounds
            let minimumVisibleX = bounds.minX
            let maximumVisbleX = bounds.maxX

            tilePages(minimumVisibleX: minimumVisibleX, toMaxX: maximumVisbleX)
    //        tileLabelsFromMinX(minimumVisibleX: minimumVisibleX, toMaxX: maximumVisbleX)
        }

        func insertLabel()->UILabel{
            let recycledLabels = visibleLabels.filter { (label) -> Bool in
                return label.superview == nil
            }

            let label = recycledLabels.last ?? UILabel(frame: bounds)

            self.addSubview(label)
            return label
        }    

        func placeNewLabelOnRight(rightEdge: CGFloat)->CGFloat{
            let label = self.insertLabel()
            visibleLabels.append(label) // add rightmost label at the end of the array

            label.frame.origin.x = rightEdge;
            label.frame.origin.y = 0
            label.text = labels.last?.text
            return label.frame.maxX
        }




    func placeNewLabelOnLeft(leftEdge:CGFloat)->CGFloat{
        let label = self.insertLabel()
        self.visibleLabels.insert(label, at: 0)   // add leftmost label at the beginning of the array

        label.frame.origin.x = leftEdge - frame.size.width;
        label.frame.origin.y = bounds.size.height - frame.size.height;

        label.text = labels[0].text

        return label.frame.minX
    }

//function used in video
//    func tileLabelsFromMinX(minimumVisibleX:CGFloat, toMaxX maximumVisibleX:CGFloat){
//        // the upcoming tiling logic depends on there already being at least one label in the visibleLabels array, so
//        // to kick off the tiling we need to make sure there's at least one label
//        if (self.visibleLabels.count == 0)
//        {
//            self.placeNewLabelOnRight(rightEdge: minimumVisibleX);
//        }
//        print("visible labels.count: \(visibleLabels.count)")
//
//        // add labels that are missing on right side
//        //    UILabel *lastLabel = [self.visibleLabels lastObject];
//        var lastLabel = visibleLabels.last!
//        var rightEdge = lastLabel.frame.maxX
//        while (rightEdge < maximumVisibleX){
//            rightEdge = self.placeNewLabelOnRight(rightEdge: rightEdge)
//        }
//
//        // add labels that are missing on left side
//        var firstLabel = self.visibleLabels[0]
//        var leftEdge = firstLabel.frame.minX
//        while (leftEdge > minimumVisibleX){
//            leftEdge = self.placeNewLabelOnLeft(leftEdge:leftEdge)
//        }
//
//        // remove labels that have fallen off right edge
//        //    lastLabel = [self.visibleLabels lastObject];
//
//        while (lastLabel.frame.origin.x > maximumVisibleX){
//            lastLabel.removeFromSuperview()
//            self.visibleLabels.removeLast()
//            lastLabel = self.visibleLabels.last!
//        }
//
//        // remove labels that have fallen off left edge
//        firstLabel = self.visibleLabels[0];
//        while (firstLabel.frame.maxX < minimumVisibleX){
//            firstLabel.removeFromSuperview()
//            self.visibleLabels.removeFirst()
//            firstLabel = self.visibleLabels[0];
//        }
//    }


    func tilePages(minimumVisibleX:CGFloat, toMaxX maximumVisibleX:CGFloat){
        let visibleBounds = bounds

        var firstNeededPageIndex:Int = Int(floorf(Float(minimumVisibleX/visibleBounds.width)))
        var lastNeededPageIndex:Int = Int(floorf(Float((maximumVisibleX - 1)/visibleBounds.width)))
        firstNeededPageIndex = max(firstNeededPageIndex, 0)
        lastNeededPageIndex = min(lastNeededPageIndex, labels.count - 1)

        //Recycle no-longer needed pages

        for page in visiblePages{
            if page.index < Int(firstNeededPageIndex) || page.index > Int(lastNeededPageIndex){
                recycledPages.insert(page)
                page.removeFromSuperview()
            }
        }
        visiblePages.subtract(recycledPages)

        //add missing pages

        for i in firstNeededPageIndex...lastNeededPageIndex{
            if !isDisplaying(pageForIndex: i){
                let page:CustomLabel = dequeueRecycledPage() ?? CustomLabel()
                print("index i: \(i)")
                self.configurePage(page: page, forIndex: i)
                self.addSubview(page)
                visiblePages.insert(page)
            }
        }
    }

    func isDisplaying(pageForIndex index:Int)->Bool{
        for page in visiblePages{
            if page.index == index{
                return true
            }
        }
        return false
    }

    func configurePage(page:CustomLabel,forIndex index:Int){
        page.index = index
        page.text = "current index: \(index)"
        let width = bounds.width
        let newX:CGFloat = CGFloat(index) * width
        page.backgroundColor = labels[index].backgroundColor
        page.frame = CGRect(origin: CGPoint(x: newX, y: 0), size: bounds.size)
    }

    func dequeueRecycledPage()->CustomLabel?{
        let page = recycledPages.first
        if let page = page{
            recycledPages.remove(page)
            return page
        }
        return nil
    }
}
...