Swift:如何让ScrollView与PageControl一起工作? [英] Swift: how to make a ScrollView work with PageControl?

查看:114
本文介绍了Swift:如何让ScrollView与PageControl一起工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

严格遵守



相反,当我向后滑动图像时,它们会以正确的方式显示,对于每个页面。或者更好的是,当我在幻灯片中获得第4个图像时,这个问题就消失了,只有在那个时候,所有以下的图像都是正确的,前面也是如此。这是影响前2或3张图像的问题。



此外幻灯片甚至没有从开始UICollectionViewCell 用户刚刚点击,但总是来自第一个。您可以在这里阅读所有代码:

  import Foundation 
import UIKit

class PagedScrollViewController :UIViewController,UIScrollViewDelegate {

@IBOutlet var scrollView:UIScrollView!
@IBOutlet var pageControl:UIPageControl!

/ *这将保留所有要显示的图像 - 每页1个。
必须从prepareforsegue()方法中的前一个视图控制器设置:
它将是该照片库下载图像的数组* /
var pageImages:[UIImage]!第一个要显示的数组图像中的

/ *位置,即用户刚刚点击的数据* /
var firstToShow:Int!

var currentImageViewForZoom:UIImageView?

/ *这将保存UIImageView的实例,以在其各自的页面上显示每个图像。
这是一个可选项数组,因为你将懒得加载页面(即当你需要它们时)
所以你需要能够处理数组中的nil值。 * /
var pageViews:[UIImageView?] = []

覆盖func viewDidLoad(){
super.viewDidLoad()

self.scrollView .delegate = self
self.scrollView.maximumZoomScale = 1.0
self.scrollView.zoomScale = 10.0

self.pageControl.numberOfPages = self.pageImages.count
self .pageControl.currentPage = self.firstToShow

for _ in 0 ..< self.pageImages.count {
self.pageViews.append(nil)
}

/ *滚动视图与以前一样,需要知道其内容大小。
由于您需要水平分页滚动视图,因此您可以将宽度计算为页数乘以滚动视图的宽度。
内容的高度与滚动视图的高度相同
* /
let pagesScrollViewSize = self.scrollView.frame.size
self.scrollView.contentSize = CGSize (width:pagesScrollViewSize.width * CGFloat(self.pageImages.count),
height:pagesScrollViewSize.height)

//您最初需要显示一些页面,所以你打电话loadVisiblePages()
self.loadVisiblePages()

}

func viewForZoomingInScrollView(scrollView:UIScrollView) - > UIView的? {
返回self.currentImageViewForZoom
}

func scrollViewDidScroll(scrollView:UIScrollView){
self.loadVisiblePages()
}

/ *
请记住,每个页面都是存储在一个可选项数组中的UIImageView。
加载视图控制器时,数组填充为nil。
此方法将加载每个页面的内容。

1 - 如果它超出你必须显示的范围,那么什么都不做

2 - 如果pageView为nil,那么你需要创建一个页面。首先,确定此页面的框架。
它被计算为与滚动视图相同的大小,位于零y偏移,
,然后偏移页面宽度乘以x(水平)方向的页码。

3 - 最后,你用你刚刚创建的视图替换pageViews数组中的nil,
这样如果要求再次加载此页面,你现在就不会进入因为页面的视图已被创建,所以if语句而不执行任何操作,


* /
func loadPage(页面:Int){
if page < 0 || page> = self.pageImages.count {
// 1
返回
}

// 2
如果让_ = self.pageViews [页面] {/ *什么都不做。视图已加载* /}
else {
// 2
var frame = self.scrollView.bounds
frame.origin.x = frame.size.width * CGFloat (页)
frame.origin.y = 0.0

let newPageView = UIImageView(image:self.pageImages [page])
newPageView.contentMode = .ScaleAspectFit
newPageView.frame = frame
self.scrollView.addSubview(newPageView)

// 3
self.pageViews [page] = newPageView
self.currentImageViewForZoom = newPageView
}
}

/ *
此函数清除以前通过loadPage()创建的页面。
它首先检查此页面的pageViews数组中的对象是否为nil。
如果不是,它会从滚动视图中删除视图,并再次使用nil更新pageViews数组,以指示此页面不再存在。
你问为什么懒得加载和清除页面?
嗯,在这个例子中,如果你在开始时加载所有页面并不重要,因为只有五个并且它们不会大到足以占用太多内存。
但是想象你有100页,每张图片大小为5MB。如果你一次加载所有页面,那将占用500MB的内存!
您的应用程序将快速超过可用内存量并被操作系统杀死。
延迟加载意味着您​​在任何给定时间内只能在内存中拥有一定数量的页面。
* /
func purgePage(页面:Int){
if page< 0 || page> = self.pageImages.count {
//如果它超出了您必须显示的范围,则不做任何事情
返回
}

/ /从滚动视图中删除页面并重置容器数组
如果让pageView = self.pageViews [page] {
pageView.removeFromSuperview()
self.pageViews [page] = nil
}
}

func loadVisiblePages(){
//首先,确定当前可见的页面
let pageWidth = self.scrollView.frame.size .width

// floor()函数将十进制数舍入到下一个最小整数
let page = Int(floor((self.scrollView.contentOffset.x * 2.0 + pageWidth) /(pageWidth * 2.0)))/ *** /

//更新页面控件
self.pageControl.currentPage = page

//锻炼你要加载哪些页面
让firstPage = page - 1
let lastPage = page + 1

//清除之前的任何内容var index = 0的第一页
;指数<第一页; ++ index {
self.purgePage(index)
}

//在我们的范围内加载页面
for firstPage ... lastPage {
self.loadPage(index)
}

//在最后一页之后清除任何内容
for var index = lastPage + 1;指数< self.pageImages.count; ++ index {
self.purgePage(index)
}
}
}

我想问题可能是 / *** / 这一行,这是我从教程中无法理解的。感谢您的关注



更新
在SO中查找类似帖子,有人建议在<$ c $中创建子视图c> viewDidLayoutSubviews(),所以这就是我刚试过的:

 覆盖func viewDidLayoutSubviews() {
self.loadVisiblePages()
}

现在图像正确显示,前3张图片没有那么奇怪的效果了。但为什么?我是初级iOS开发人员,我仍然不知道所有要覆盖的方法以及它们的工作顺序。无论如何,另一个持续存在的问题是,即使拍摄了另一个图像,图像也始终从第一个开始显示。例如,看看这个:





即使点击了另一个图像,也会显示第一个图像(左上角)。最后,在我的代码中,我实现了委托方法以进行缩放,但它也不起作用。



更新2



当用户使用以前的 UICollectionViewController 时,这是 prepareForSegue()的代码点击一个单元格:

 覆盖func prepareForSegue(segue:UIStoryboardSegue,sender:AnyObject?){
if(segue。 identifier ==toSlideShow){
let pagedScrollViewController:PagedScrollViewController = segue.destinationViewController as! PagedScrollViewController
pagedScrollViewController.pageImages = self.imagesDownloaded
pagedScrollViewController.firstToShow = self.collectionView?.indexPathsForSelectedItems()![0] .row
}
}


解决方案

您应该将滚动视图的偏移更新为等于图像的偏移量你希望在过渡后展示。 self.scrollView.contentOffset = CGPoint(x:pagesScrollViewSize.width * CGFloat(self.firstToShow),y:0.0)您可以在viewDidLayoutSubviews中执行此操作,这样可以使其显示喜欢:

 覆盖func viewDidLayoutSubviews(){
super.viewDidLayoutSubviews()
self.scrollView.delegate = self
self.scrollView.maximumZoomScale = 2.0
self.scrollView.zoomScale = 1.0
self.scrollView.minimumZoomScale = 0.5

self.pageControl.numberOfPages = self .pageImages.count
self.pageControl.currentPage = self.firstToShow

for _ in 0 ..< self.pageImages.count {
self.pageViews.append(nil )
}

/ *滚动视图与以前一样,需要知道其内容大小。
由于您需要水平分页滚动视图,因此您可以将宽度计算为页数乘以滚动视图的宽度。
内容的高度与滚动视图的高度相同
* /
let pagesScrollViewSize = self.scrollView.frame.size
self.scrollView.contentSize = CGSize (width:pagesScrollViewSize.width * CGFloat(self.pageImages.count),
height:pagesScrollViewSize.height)

self.scrollView.contentOffset = CGPoint(x:pagesScrollViewSize.width * CGFloat( self.firstToShow),y:0.0)

//你最初需要一些页面,所以你调用loadVisiblePages()
self.loadVisiblePages()

}

您突出显示的行只是向下舍入到最近的图像索引以确定哪个页面滚动视图已打开。问题是当您的视图控制器显示时没有滚动,因此它将始终显示第一个图像。要获得想要首先显示的图像,您只需计算图像的偏移量,将滚动视图的偏移量设置为如上所示。


Strctly following this tutorial, in section Paging with UIScrollView, I have just implemented a ScrollView to use as a slideshow with downloaded photos from a previous UICollectionViewController. When scroll view is loaded, it does not work well because I see these ones:

Instead, when I slide back the images they are displayed in the correct way, one for each page. Or better, this problem disappears when I get the 4-th image in the slideshow, and only at that point all the following ones are correct, and so are the previous too. It is a problem which affects the first 2 or 3 images.

Moreover the slideshow does not even start from the UICollectionViewCell the user has just tapped, but always from the first. You can read all the code here:

import Foundation
import UIKit

class PagedScrollViewController: UIViewController, UIScrollViewDelegate {

@IBOutlet var scrollView: UIScrollView!
@IBOutlet var pageControl: UIPageControl!

/* This will hold all the images to display – 1 per page. 
   It must be set from the previous view controller in prepareforsegue() method: 
   it will be the array of downloaded images for that photo gallery */
var pageImages:[UIImage]!

/* position in array images of the first to be showed, i.e. the one the user has just tapped */
var firstToShow:Int!

var currentImageViewForZoom:UIImageView?

/* This will hold instances of UIImageView to display each image on its respective page. 
   It’s an array of optionals, because you’ll be loading the pages lazily (i.e. as and when you need them) 
   so you need to be able to handle nil values from the array. */
var pageViews:[UIImageView?] = []

override func viewDidLoad() {
    super.viewDidLoad()

    self.scrollView.delegate = self
    self.scrollView.maximumZoomScale = 1.0
    self.scrollView.zoomScale = 10.0

    self.pageControl.numberOfPages = self.pageImages.count
    self.pageControl.currentPage = self.firstToShow

    for _ in 0..<self.pageImages.count {
        self.pageViews.append(nil)
    }

    /* The scroll view, as before, needs to know its content size. 
       Since you want a horizontal paging scroll view, you calculate the width to be the number of pages multiplied by the width of the scroll view. 
       The height of the content is the same as the height of the scroll view
    */
    let pagesScrollViewSize = self.scrollView.frame.size
    self.scrollView.contentSize = CGSize(width: pagesScrollViewSize.width * CGFloat(self.pageImages.count),
        height: pagesScrollViewSize.height)

    // You’re going to need some pages shown initially, so you call loadVisiblePages()
    self.loadVisiblePages()

}

func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
    return self.currentImageViewForZoom
}

func scrollViewDidScroll(scrollView: UIScrollView) {
    self.loadVisiblePages()
}

/*
Remember each page is a UIImageView stored in an array of optionals. 
When the view controller loads, the array is filled with nil. 
This method will load the content of each page.

1 - If it's outside the range of what you have to display, then do nothing

2 - If pageView is nil, then you need to create a page. So first, work out the frame for this page. 
    It’s calculated as being the same size as the scroll view, positioned at zero y offset, 
    and then offset by the width of a page multiplied by the page number in the x (horizontal) direction.

3 - Finally, you replace the nil in the pageViews array with the view you’ve just created, 
    so that if this page was asked to load again, you would now not go into the if statement and instead do nothing, 
    since the view for the page has already been created

*/
func loadPage(page: Int) {
    if page < 0 || page >= self.pageImages.count {
        //1
        return
    }

    //2
    if let _ = self.pageViews[page] {/*Do nothing. The view is already loaded*/}
    else {
        // 2
        var frame = self.scrollView.bounds
        frame.origin.x = frame.size.width * CGFloat(page)
        frame.origin.y = 0.0

        let newPageView = UIImageView(image: self.pageImages[page])
        newPageView.contentMode = .ScaleAspectFit
        newPageView.frame = frame
        self.scrollView.addSubview(newPageView)

        // 3
        self.pageViews[page] = newPageView
        self.currentImageViewForZoom = newPageView
    }
}

/*
This function purges a page that was previously created via loadPage(). 
It first checks that the object in the pageViews array for this page is not nil. 
If it’s not, it removes the view from the scroll view and updates the pageViews array with nil again to indicate that this page is no longer there.
Why bother lazy loading and purging pages, you ask? 
Well, in this example, it won’t matter too much if you load all the pages at the start, since there are only five and they won’t be large enough to eat up too much memory. 
But imagine you had 100 pages and each image was 5MB in size. That would take up 500MB of memory if you loaded all the pages at once! 
Your app would quickly exceed the amount of memory available and be killed by the operating system. 
Lazy loading means that you’ll only have a certain number of pages in memory at any given time.
*/
func purgePage(page: Int) {
    if page < 0 || page >= self.pageImages.count {
        // If it's outside the range of what you have to display, then do nothing
        return
    }

    // Remove a page from the scroll view and reset the container array
    if let pageView = self.pageViews[page] {
        pageView.removeFromSuperview()
        self.pageViews[page] = nil
    }
}

func loadVisiblePages() {
    // First, determine which page is currently visible
    let pageWidth = self.scrollView.frame.size.width

    // floor() function will round a decimal number to the next lowest integer
    let page = Int(floor((self.scrollView.contentOffset.x * 2.0 + pageWidth) / (pageWidth * 2.0))) /***/

    // Update the page control
    self.pageControl.currentPage = page

    // Work out which pages you want to load
    let firstPage = page - 1
    let lastPage = page + 1

    // Purge anything before the first page
    for var index = 0; index < firstPage; ++index {
        self.purgePage(index)
    }

    // Load pages in our range
    for index in firstPage...lastPage {
        self.loadPage(index)
    }

    // Purge anything after the last page
    for var index = lastPage+1; index < self.pageImages.count; ++index {
        self.purgePage(index)
    }
}
}

I guess the problem could be the line with /***/ which is something I have not understood from the tutorial. Thank you for your attention

UPDATE Looking here in SO for similar posts, someone advised to create subviews in viewDidLayoutSubviews(), so here's what I have just tried:

override func viewDidLayoutSubviews() {
    self.loadVisiblePages()
}

and now the images are correctly displayed and there's no more that strange effect for the first 3 images. But why? I am a junior iOS developer and I still don't know all those methods to override and the order in which they work. Anyway, the other problem which persists is that images are showed always from the first, even if another image is tapped. For example, have a look at this:

always the first image (left up corner) is displayed even if another one is tapped. And finally, in my code I implemented the delegate method to have zoom but it does not work too.

UPDATE 2

Here's the code of prepareForSegue() from the previous UICollectionViewController when the user taps a cell:

 override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if (segue.identifier == "toSlideShow") {
        let pagedScrollViewController:PagedScrollViewController = segue.destinationViewController as! PagedScrollViewController
        pagedScrollViewController.pageImages = self.imagesDownloaded
        pagedScrollViewController.firstToShow = self.collectionView?.indexPathsForSelectedItems()![0].row
    }
}

解决方案

You should update the scroll view's offset to the be equal to the offset of the image you want to be showing after you transition. self.scrollView.contentOffset = CGPoint(x: pagesScrollViewSize.width * CGFloat(self.firstToShow), y: 0.0) You can do this in viewDidLayoutSubviews, which would make it look like:

override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        self.scrollView.delegate = self
        self.scrollView.maximumZoomScale = 2.0
        self.scrollView.zoomScale = 1.0
        self.scrollView.minimumZoomScale = 0.5

        self.pageControl.numberOfPages = self.pageImages.count
        self.pageControl.currentPage = self.firstToShow

        for _ in 0..<self.pageImages.count {
            self.pageViews.append(nil)
        }

        /* The scroll view, as before, needs to know its content size.
        Since you want a horizontal paging scroll view, you calculate the width to be the number of pages multiplied by the width of the scroll view.
        The height of the content is the same as the height of the scroll view
        */
        let pagesScrollViewSize = self.scrollView.frame.size
        self.scrollView.contentSize = CGSize(width: pagesScrollViewSize.width * CGFloat(self.pageImages.count),
            height: pagesScrollViewSize.height)

        self.scrollView.contentOffset = CGPoint(x: pagesScrollViewSize.width * CGFloat(self.firstToShow), y: 0.0)

        // You’re going to need some pages shown initially, so you call loadVisiblePages()
        self.loadVisiblePages()

    }

The line you highlighted is just rounding down to the closest image index to determine what page the scroll view is on. The problem is when your view controller is displayed no scrolling has been done so it will always show the first image. To get the image you want to show first, you can just calculate what the offset should be for the image set your scroll view's offset to that as shown above.

这篇关于Swift:如何让ScrollView与PageControl一起工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆