使用 UIScrollView 循环浏览图像

Using UIScrollView to cycle through images

我在 scrollView 中有一个 imageView,我希望能够上传最多 3 张图片并让用户在它们之间滑动。我只是通过用我想要的信息替换视图的内容来完成此操作,但它看起来或感觉不如带分页的滚动视图好。

我拥有使每个图像在我需要时显示的所有代码,但我的问题是滚动视图约束以及如何向具有固定宽度的滚动视图添加更多内容。无论我做什么,当我将图像添加到视图时,我似乎都无法扩展滚动范围。

这是我现在正在尝试的:

let newImageView:UIImageView = UIImageView()
self.imageScrollView.contentSize = CGSize(width: self.imageView.frame.size.width * 3, height: self.imageView.frame.size.height)
self.imageScrollView.pagingEnabled = true

for i = 0; i < 3; ++i {
    var xOrigin:CGFloat = CGFloat(i) * self.view.frame.size.width
    newImageView.frame = CGRectMake(self.imageView.image!.size.width * i, 0, self.imageView.image!.size.width, self.imageScrollView.frame.size.height)
    newImageView.image = images[i]
    newImageView.contentMode = UIViewContentMode.ScaleAspectFit
    self.imageScrollView.addSubview(newImageView)
}

使用此代码我可以添加图像,但无法滚动到它们。在我的故事板中,滚动视图约束如下所示:

我确定其中一些限制是多余的,但我无法弄清楚为什么内容宽度不会扩展并允许我滚动浏览滚动视图中的所有图像。

编辑

我已经使用@matt 的教科书示例进行了更改。我制作了一个新的视图控制器并将它放在我的另一个视图控制器(与我原来的视图控制器相同)的视图中。它显示第一个没问题,但当我向左或向右滑动时,它似乎没有调用 UIPageViewControllerDataSource 函数。我不确定为什么会这样。

这是它在原始视图控制器 (UploadPhotoViewController) 中的样子

func setUpPageViewController() {
    sharedIm.images.append(UIImage(named: "placeholder")!)
    sharedIm.images.append(UIImage(named: "TestProfile")!)
    sharedIm.images.append(UIImage(named: "button")!)
    let pvc = UIPageViewController(transitionStyle: .Scroll, navigationOrientation: .Horizontal, options: nil)
    let page:ImagePage = ImagePage()
    pvc.dataSource = self
    pvc.setViewControllers([page], direction: .Forward, animated: false, completion: nil)

    pvc.view.frame = self.imageContainer.bounds
    self.imageContainer.addSubview(pvc.view)
    pvc.didMoveToParentViewController(self)
}

//this is placed at the end of the class
extension UploadPhotoViewController : UIPageViewControllerDataSource {
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
    let image = (viewController as! ImagePage).image
    let ix = find(sharedIm.images, image)! + 1
    println(ix)
    if ix >= sharedIm.images.count {
        println("no next page")
        return nil
    }
    sharedIm.currentIndex = ix
    let page = ImagePage()
    return page
}


func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
    let image = (viewController as! ImagePage).image
    let ix = find(sharedIm.images, image)! - 1
    println(ix)
    if ix < 0 {
        println("no previous page")
        return nil
    }
    sharedIm.currentIndex = ix
    let page = ImagePage()
    return page
}

下面是我的新视图控制器中的内容,它被添加到另一个视图控制器中:

class ImagePage: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

var image : UIImage? 
var imagePicker: UIImagePickerController!
@IBOutlet var imageView : UIImageView!
@IBOutlet var forwardButton: UIButton!
@IBOutlet var backButton: UIButton!
@IBOutlet var closeButton: UIButton!
@IBOutlet var pictureButton: UIButton!
@IBOutlet var cameraButton: UIButton!



init() {
    self.image = sharedIm.images[sharedIm.currentIndex]
    super.init(nibName: "ImagePage", bundle: nil)
}

required init(coder: NSCoder) {
    fatalError("NSCoding not supported")
}

override func viewDidLoad() {
    super.viewDidLoad()
    self.imageView.image = self.image
}
//bunch of other code for buttons and uploading pictures
}

使用 UIPageViewController(具有滚动样式),您尝试做的事情会容易得多。我建议您改用 UIPageViewController 实现。

原因是您一次只能提供 一个 "page"(在您的例子中是一张图片)。您可以实时决定在任一方向是否还有另一个"page",以及您要假装"really"有多少"pages"。簿记工作发生在您的模型(幕后数据)中,而不是像 UIScrollView 让您那样配置整个视图来同时处理所有页面。

使用自动布局和 uiscrollview 时,诀窍是知道何时加载您的内容。由于您在混合模式(框架和自动布局)下工作,您需要确保约束有时间加载,然后在它们加载后,您可以添加子视图并调整约束。如果您尝试在 viewDidLoad 上加载您的内容,则为时过早并且您的约束可能尚未设置。如果您在 viewWillAppear 上加载您的内容,那么为时已晚并且约束已经锁定以向用户呈现视图。

因此,我发现加载内容的最佳位置是 viewDidLayoutSubviews 此时自动布局已将约束应用于您的 scrollView,您现在可以安全地进行更改。在此处添加对 运行 填充 scrollView 的代码的调用,您应该没问题。此外,您需要设置一个标志以仅进行一次调用,因为 viewDidLayoutSubviews 在视图创建期间可能会被多次调用。您还应该在 scrollView 上调用 setNeedsUpdateConstraints 以确保在添加子视图时更新其约束。