Memory warnings when trying to load 200+ images - ios

Update:
Now just using a CollectionView. I should have done this from the start but I was very new to iOS. CollectionViews manage all the getting and releasing of images for you. So no memory trouble at all. Enough tutorials on them on the internet. Just a quick note. Give images a name with a number and don't add zero's before the number. Then you can fetch them with the indexPath.
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("AnimationCell", forIndexPath: indexPath) as! AnimationCell
cell.imageView?.image = UIImage(named: "bg_\(indexPath.row)")
return cell
}
I am building a image viewing app for a graphic novel. One giant scrollview holding 200+ images.
I tried to build in some memory management based on sample code, but this doesn't seem to work. I guess it doesn't work because I only have memory management on the images and not on the views that hold them.
Is this something where I need to use more "weak" variables and how do I get these to work?
When I put weak in with a variable it tells me it needs a class.
import UIKit
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet var scrollView: UIScrollView!
var imageGroup : [UIImage?] = []
var containerViews : [UIImageView?] = []
var containerView :UIImageView?
var imgWidthMult : CGFloat = 2.121875
let imageGroupCount : CGFloat?
let containerHeight : CGFloat?
let containerWidth : CGFloat?
var imageCounter : Int = 0
override func viewDidLoad() {
super.viewDidLoad()
// 1
imageGroup = [
UIImage(named: "bg_001")!,
UIImage(named: "bg_002")!,
UIImage(named: "bg_003")!,
UIImage(named: "bg_004")!,
//200+ images to follow here
]
let imageGroupCount = CGFloat(imageGroup.count)
println(imageGroupCount)
// 3
for i in 0..<imageGroup.count {
containerViews.append(nil)
}
// 4
let imagesScrollViewSize = UIScreen.mainScreen().applicationFrame;
scrollView.contentSize = CGSizeMake(imagesScrollViewSize.height * imgWidthMult * CGFloat(imageGroup.count), imagesScrollViewSize.height)
// 5
let containerframe = UIScreen.mainScreen().applicationFrame;
let containerHeight : CGFloat = containerframe.height
let containerWidth : CGFloat = (imgWidthMult * containerHeight)
loadVisibleImages()
println("containerWidth")
println(containerWidth)
println(containerframe.size)
println(scrollView.contentSize)
return
}
// this loads images and should be adjusted and taken out of local scope
func loadImage (imageCounter:Int) {
let containerframe = UIScreen.mainScreen().applicationFrame;
let containerHeight : CGFloat = containerframe.height
let containerWidth : CGFloat = (imgWidthMult * containerHeight)
if imageCounter < 0 || imageCounter >= imageGroup.count {
// If it's outside the range of what you have to display, then do nothing
return
}
// 1
if let containerView = containerViews[imageCounter] {
// Do nothing. The view is already loaded.
} else {
// 2
var frame = UIScreen.mainScreen().applicationFrame;
frame.origin.x = frame.size.height * CGFloat(imageCounter) * 2.121875
frame.origin.y = 0.0
frame.size = CGSize(width: containerWidth, height: containerHeight)
// 3
var newcontainerView = UIImageView(image: imageGroup[imageCounter])
newcontainerView.contentMode = .ScaleAspectFit
newcontainerView.frame = frame
scrollView.addSubview(newcontainerView)
containerViews[imageCounter] = newcontainerView
}
}
func purgeImage(imageCounter:Int) {
if imageCounter < 0 || imageCounter >= imageGroup.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 containerView = containerViews[imageCounter] {
containerView.removeFromSuperview()
containerViews[imageCounter] = nil
println("removed page")
}
}
func loadVisibleImages() {
// First, determine which page is currently visible
let containerframe = UIScreen.mainScreen().applicationFrame;
let containerHeight : CGFloat = containerframe.height
let containerWidth : CGFloat = (imgWidthMult * containerHeight)
let pagesWidth = (containerWidth)
let imageCounter = Int(floor((scrollView.contentOffset.x * 2.0 + pagesWidth) / (pagesWidth * 2.0)))
println(Int(floor((scrollView.contentOffset.x * 2.0 + pagesWidth) / (pagesWidth * 2.0))))
println(pagesWidth)
println(containerHeight)
println(imageCounter)
// Update the page control
// Work out which pages you want to load
let firstPage = imageCounter - 1
let lastPage = imageCounter + 1
// Purge anything before the first page
for var index = 0; index < firstPage; ++index {
purgeImage(index)
}
// Load pages in our range
for var index = firstPage; index <= lastPage; ++index {
loadImage(index)
}
// Purge anything after the last page
for var index = lastPage+1; index < imageGroup.count; ++index {
purgeImage(index)
}
println("loadVisibleImages")
}
func scrollViewDidScroll(scrollView: UIScrollView) {
// Load the pages that are now on screen
println("did scroll")
loadVisibleImages()
println("did scroll")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Edit:
import UIKit
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet var scrollView: UIScrollView!
var containerViews : [UIImageView?] = []
var containerView : UIImageView?
var imgWidthMult : CGFloat = 2.121875
let imageGroupCount : Int = 149
let containerHeight : CGFloat?
let containerWidth : CGFloat?
var imageCounter : Int = 000
var loadingVisibleImages : Bool?
override func viewDidLoad() {
super.viewDidLoad()
// 1
loadingVisibleImages = false
// 3
for i in 0..<imageGroupCount {
containerViews.append(nil)
}
// 4
let imagesScrollViewSize = UIScreen.mainScreen().applicationFrame;
scrollView.contentSize = CGSizeMake(imagesScrollViewSize.height * imgWidthMult * CGFloat(imageGroupCount), imagesScrollViewSize.height)
// 5
let containerframe = UIScreen.mainScreen().applicationFrame;
let containerHeight : CGFloat = containerframe.height
let containerWidth : CGFloat = (imgWidthMult * containerHeight)
loadVisibleImages()
return
}
// this loads images and should be adjusted and taken out of local scope
func loadImage (imageCounter:Int) {
let containerframe = UIScreen.mainScreen().applicationFrame;
let containerHeight : CGFloat = containerframe.height
let containerWidth : CGFloat = (imgWidthMult * containerHeight)
if imageCounter < 0 || imageCounter >= imageGroupCount {
// If it's outside the range of what you have to display, then do nothing
return
}
// 1
if let containerView = containerViews[imageCounter] {
// Do nothing. The view is already loaded.
} else {
// 2
var frame = UIScreen.mainScreen().applicationFrame;
frame.origin.x = ((frame.size.height * CGFloat(imageCounter) * 2.121875) + containerframe.width)
frame.origin.y = 0.0
frame.size = CGSize(width: containerWidth, height: containerHeight)
// 3
let newImage = UIImage(named: "bg_\(imageCounter + 1)")
var newcontainerView = UIImageView(image: newImage)
newcontainerView.contentMode = .ScaleAspectFit
newcontainerView.frame = frame
scrollView.addSubview(newcontainerView)
containerViews[imageCounter] = newcontainerView
}
}
func purgeImage(imageCounter:Int) {
if imageCounter < 0 || imageCounter >= imageGroupCount {
// 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 containerView = containerViews[imageCounter] {
containerView.removeFromSuperview()
containerViews[imageCounter] = nil
println("removed page")
}
}
func loadVisibleImages() {
loadingVisibleImages = true
// First, determine which page is currently visible
let containerframe = UIScreen.mainScreen().applicationFrame;
let containerHeight : CGFloat = containerframe.height
let containerWidth : CGFloat = (imgWidthMult * containerHeight)
let pagesWidth = (containerWidth)
let imageCounter = Int(floor(((scrollView.contentOffset.x * 2.0 + pagesWidth) / (pagesWidth * 2.0)))
println(pagesWidth)
println(containerHeight)
println(imageCounter)
// Update the page control
// Work out which pages you want to load
let firstPage = imageCounter - 1
let lastPage = imageCounter + 1
// Purge anything before the first page
for var index = 0; index < firstPage; ++index {
purgeImage(index)
}
// Load pages in our range
for var index = firstPage; index <= lastPage; ++index {
loadImage(index)
}
// Purge anything after the last page
for var index = lastPage+1; index < imageGroupCount; ++index {
purgeImage(index)
}
loadingVisibleImages = false
println("loadVisibleImages")
}
func scrollViewDidScroll(scrollView: UIScrollView) {
// Load the pages that are now on screen
println("did scroll")
if loadingVisibleImages == false {
loadVisibleImages()
}
}
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
if loadingVisibleImages == false {
loadVisibleImages()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Your memory management's the problem. Not Swift's. Here's where you went wrong...
In your viewDidLoad method, you've instantiated your imageGroup array with 200+ images. Assuming these images aren't super tiny, you've already allocated a huge chunk of memory to an unnecessary array from the get go. You should instead fetch these images as needed during your loadImage method.
For example, you can replace this line:
var newcontainerView = UIImageView(image: imageGroup[imageCounter])
with something along the lines of:
let newImage = UIImage(named: "bg_\(imageCounter + 1)")
var newcontainerView = UIImageView(image: newImage)
so you instead get the image from the bundle on an as-needed basis.
NB: You'll need to figure out an appropriate filename-getting algorithm to suit your case...named: "bg_\(imageCounter + 1)" is just an example.
As for your use of imageGroup.count throughout your code, since it's apparent in your viewDidLoad that your images are of a known quantity, I'd suggest replacing all instances of imageGroup.count with an equivalent constant.
Edit:
To address one other issue with your code, calling loadVisibleImages in scrollViewDidScroll can also cause memory issues.
The UIScrollView delegate method scrollViewDidScroll is called continuously as the scrollview scrolls, and thus, as your code stands, loadVisibleImages will be called nearly constantly as well. So especially when the user's scrolling quickly and your app's simultaneously running multiple iterations of loadVisibleImages (where one pass of the method hasn't completed before the next begins), this could result in a crash in this particular case.
So here's a suggestion for now right off the top of my head (I could very well come up with a better one later and I'm sure better ones exist). In your scrollViewDidScroll method, perhaps only call loadVisibleImages if your app isn't already going through it. Then call it again as a safeguard once your scrollview has finished decelerating just to make sure you have the visible images. For example:
var loadingVisibleImages
override func viewDidLoad() {
super.viewDidLoad()
loadingVisibleImages = false
...
}
func loadVisibleImages() {
loadingVisibleImages = true
...
loadingVisibleImages = fasle
}
func scrollViewDidScroll(scrollView: UIScrollView) {
// Load the pages that are now on screen
println("did scroll")
if loadingVisibleImages == false
loadVisibleImages()
}
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
if loadingVisibleImages == false
loadVisibleImages()
}

Don't load images with [UIImage imageNamed:] for load image in ImageView, it's cause memory issue when no.of images are more,
use this, In Objective-C
NSString *imgpath= [[NSBundle mainBundle] pathForResource:#"imageName" ofType:#"jpg"];
imageView.image=[UIImage imageWithContentsOfFile: imgpath];
and in Swift 3.0
let imgath = Bundle.main.path(forResource: "imageName", ofType: "jpg")
imageView.image = UIImage(contentsOfFile: imgath!)

Use reusable scroll view 3rd party framework. It loads and displays images similar way as table view. It uses reusable view on each scroll. You can display small size placeholders and reload actual size of image on focus. Very easy to use:
https://github.com/sumofighter666/ReusableScrollView

Related

Zoom Images in Paged UIScrollView

I am creating a paging UIScrollView following this tutorial.
This is the Swift code which allow paging of images with UIScrollView. However, it has a limitation. I am unable to zoom the images.
class PagedScrollViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet var scrollView: UIScrollView!
#IBOutlet var pageControl: UIPageControl!
var pageImages: [UIImage] = []
var pageViews: [UIImageView?] = []
override func viewDidLoad() {
super.viewDidLoad()
// 1
pageImages = [UIImage(named:"photo1.png")!,
UIImage(named:"photo2.png")!,
UIImage(named:"photo3.png")!,
UIImage(named:"photo4.png")!,
UIImage(named:"photo5.png")!]
let pageCount = pageImages.count
// 2
pageControl.currentPage = 0
pageControl.numberOfPages = pageCount
// 3
for _ in 0..<pageCount {
pageViews.append(nil)
}
// 4
let pagesScrollViewSize = scrollView.frame.size
scrollView.contentSize = CGSizeMake(pagesScrollViewSize.width * CGFloat(pageImages.count), pagesScrollViewSize.height)
// 5
loadVisiblePages()
}
func loadPage(page: Int) {
if page < 0 || page >= pageImages.count {
// If it's outside the range of what you have to display, then do nothing
return
}
// 1
if let pageView = pageViews[page] {
// Do nothing. The view is already loaded.
} else {
// 2
var frame = scrollView.bounds
frame.origin.x = frame.size.width * CGFloat(page)
frame.origin.y = 0.0
// 3
let newPageView = UIImageView(image: pageImages[page])
newPageView.contentMode = .ScaleAspectFit
newPageView.frame = frame
scrollView.addSubview(newPageView)
// 4
pageViews[page] = newPageView
}
}
func purgePage(page: Int) {
if page < 0 || page >= 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 = pageViews[page] {
pageView.removeFromSuperview()
pageViews[page] = nil
}
}
func loadVisiblePages() {
// First, determine which page is currently visible
let pageWidth = scrollView.frame.size.width
let page = Int(floor((scrollView.contentOffset.x * 2.0 + pageWidth) / (pageWidth * 2.0)))
// Update the page control
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 {
purgePage(index)
}
// Load pages in our range
for var index = firstPage; index <= lastPage; ++index {
loadPage(index)
}
// Purge anything after the last page
for var index = lastPage+1; index < pageImages.count; ++index {
purgePage(index)
}
}
func scrollViewDidScroll(scrollView: UIScrollView!) {
// Load the pages that are now on screen
loadVisiblePages()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
How to make the images zoomable?
Problem solved using the code from
https://github.com/Charles-Hsu/ScrollViewDemo
You can create a custom class derived from UIScrollView named as ImageScrollView in which there is a UIImageView.
Create this using Interface Builder. Now in page views array you need to add ImageScrollView instead of UIImageView and you will achieve the zooming effect.

Images not loaded in UITableViewCell Swift

This is the function that repeats for every Cell in TableView. As you can see im passing an array of images(called imageset) inside an object named Product.
productImages is an array of Product. Inside this object we have Name, Price and imageSet.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("productCell", forIndexPath: indexPath) as! ProductHomeCell
cell.productName.text = productImages[indexPath.row].name
cell.productPrice.text = "\(productImages[indexPath.row].price)"
//cell.PageImages contains Array of Images
cell.pageImages = productImages[indexPath.row].imageSet
return cell
}
Now we will se the Custom UITableViewCell where the information of the Product seen will be loaded and shown. Notice Im using a ScrollView to scroll through the images.(I took the code from a question in stackoverflow)
PageImages is the variable that should have the imageSet passed.
var pageImages: [UIImage] = []
var pageViews: [UIImageView?] = []
var pageControl : UIPageControl = UIPageControl(frame: CGRectMake(60, 300, 195, 5))
override func awakeFromNib() {
super.awakeFromNib()
scrollView.delegate = self
pageControl = UIPageControl(frame: CGRectMake(230, 14, 107, 37))
//PageImages should be loaded.
let pageCount = pageImages.count
self.pageControl.numberOfPages = pageCount
configurePageControl()
pageControl.enabled = false
// 3
for _ in 0..<pageCount {
pageViews.append(nil)
}
// 4
let pagesScrollViewSize = scrollView.frame.size
scrollView.contentSize = CGSize(width: pagesScrollViewSize.width * CGFloat(pageImages.count),
height: pagesScrollViewSize.height)
loadVisiblePages()
}
func loadPage(page: Int) {
if page < 0 || page >= pageImages.count {
// If it's outside the range of what you have to display, then do nothing
return
}
// 1
if let pageView = pageViews[page] {
// Do nothing. The view is already loaded.
} else {
// 2
var frame = scrollView.bounds
frame.origin.x = frame.size.width * CGFloat(page)
frame.origin.y = 0.0
// 3
let newPageView = UIImageView(image: pageImages[page])
// let newPageView = UIImageView(image: imageC)
newPageView.contentMode = .ScaleAspectFit
newPageView.frame = frame
scrollView.addSubview(newPageView)
// 4
pageViews[page] = newPageView
}
}
func loadVisiblePages() {
// First, determine which page is currently visible
let pageWidth = scrollView.frame.size.width
let page = Int(floor((scrollView.contentOffset.x * 2.0 + pageWidth) / (pageWidth * 2.0)))
// 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 {
purgePage(index)
}
// Load pages in our range
for index in firstPage...lastPage {
loadPage(index)
}
// Purge anything after the last page
for var index = lastPage+1; index < pageImages.count; ++index {
purgePage(index)
}
}
func scrollViewDidScroll(scrollView: UIScrollView) {
// Load the pages that are now on screen
loadVisiblePages()
}
func purgePage(page: Int) {
if page < 0 || page >= 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 = pageViews[page] {
pageView.removeFromSuperview()
pageViews[page] = nil
}
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func configurePageControl() {
self.pageControl.currentPage = 0
self.pageControl.tintColor = UIColor.blackColor()
self.pageControl.pageIndicatorTintColor = UIColor.lightGrayColor()
self.pageControl.currentPageIndicatorTintColor = UIColor.blackColor()
self.addSubview(pageControl)
}
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
let pageNumber = round(scrollView.contentOffset.x / scrollView.frame.size.width)
pageControl.currentPage = Int(pageNumber)
}
PROBLEM - The product name and price Load perfectly. The images dont. When i debug it looks like PageImages is empty when is awakeFromNib() is running, but when I debug in the cellForARowAtIndexPath method, the var PageImages is loaded with the imageSet at that indexPath.row.
Thanks
awakeFromNib is executed before the cell is returned by dequeue... So trying to set up your pages there isn't going to work. The two approaches I've seen are to either have a method to explicitly set up the cell:
func setupCell(productName:String, productPrice:Double, images:[UIImage]) {
// Do most of what you have in awakeFromNib here
}
or to do it in the didSet for the property:
var pageImages : [UIImage] = [] {
didSet {
// Set up your page controller here.
}
}

Start UIScrollView with an image of particular index from another view

I have a collectionView with multiple images. When tapping on one of them a new view is being opened - UIScrollView.
Now every time UIScrollView starts with the first image of the array from collectionView.
I am passing the index(imageNumber) of the image tapped to UIScrollView but i don't know how to make the first image shown to be the one with the index.
override func viewDidLoad() {
super.viewDidLoad()
var numberOfImagesPerALbum = data[albumNumber].count - 1
for index in 0...numberOfImagesPerALbum {
pageImages.append(UIImage(named: data[albumNumber][index]["image"]!)!)
}
var pageCount = pageImages.count
// 2
pageControl.currentPage = imageNumber
println(imageNumber)
pageControl.numberOfPages = pageCount
// 3
for _ in 0..<pageCount {
pageViews.append(nil)
}
}
override func viewDidLayoutSubviews() {
let pagesScrollViewSize = scrollView.frame.size
scrollView.contentSize = CGSizeMake(pagesScrollViewSize.width * CGFloat(pageImages.count), pagesScrollViewSize.height)
// 5
loadVisiblePages()
}
func loadPage(page: Int) {
if page < 0 || page >= pageImages.count {
// If it's outside the range of what you have to display, then do nothing
return
}
// 1
if let pageView = pageViews[page] {
// Do nothing. The view is already loaded.
} else {
// 2
var frame = scrollView.bounds
frame.origin.x = frame.size.width * CGFloat(page)
frame.origin.y = 0.0
3
let newPageView = UIImageView(image: pageImages[page])
newPageView.contentMode = .ScaleAspectFit
newPageView.frame = frame
scrollView.addSubview(newPageView)
// 4
pageViews[page] = newPageView
}
}
func purgePage(page: Int) {
if page < 0 || page >= 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 = pageViews[page] {
pageView.removeFromSuperview()
pageViews[page] = nil
}
}
func loadVisiblePages() {
// First, determine which page is currently visible
let pageWidth = scrollView.frame.size.width
let page = Int(floor((scrollView.contentOffset.x * 2.0 + pageWidth) / (pageWidth * 2.0)))
println("Page is \(page)")
imageTitleLabel.text = data[albumNumber][page]["title"]
imageDescriptionLabel.text = data[albumNumber][page]["text"]
// Update the page control
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 {
purgePage(index)
}
// Load pages in our range
for var index = firstPage; index <= lastPage; ++index {
loadPage(index)
}
// Purge anything after the last page
for var index = lastPage+1; index < pageImages.count; ++index {
purgePage(index)
}
}
func scrollViewDidScroll(scrollView: UIScrollView) {
// Load the pages that are now on screen
loadVisiblePages()
}
Consider using UICollectionView with UICollectionViewFlowLayout (set scrollDirection to UICollectionViewScrollDirectionHorizontal) instead of UIScrollView. Also set UICollectionView's pagingEnabled property to true.
This will save you a lot of unnecessary code and you will get the UICollectionView's - scrollToItemAtIndexPath:atScrollPosition:animated: method "for free". This should solve all your problems and make code cleaner. Good luck!

Images missing from UIScrollView

I have been working on resolving a particular issue within my app for a few days now with no success. I have a UIScrollView which is used basically in the same way as the one in the App Store for displaying screenshots.
The problem I am having is that for some reason, only two images are being displayed (there should be five). I have confirmed that this isn't an issue with the images themselves, so it must be something to do with the code. Strangely, I actually have another UIScrollView in my app that does almost the exact same thing, but with three labels, and that works fine.
Here is my code:
override func awakeFromNib() {
super.awakeFromNib()
sixPlusImages = [
UIImage(named: "Glance6Plus.png")!,
UIImage(named: "Format6Plus.png")!,
UIImage(named: "Swipe6Plus.png")!,
UIImage(named: "Currencies6Plus.png")!,
UIImage(named: "Search6Plus.png")!
]
let pageCount = sixPlusImages.count
for _ in 0..<pageCount {
pageViews.append(nil)
}
let pagesScrollViewSize = scrollView.frame.size
scrollView.contentSize = CGSize(width: pagesScrollViewSize.width * CGFloat(sixPlusImages.count), height: pagesScrollViewSize.height)
loadVisiblePages()
}
func loadPage(page: Int) {
if page < 0 || page >= pageViews.count {
return
}
if let pageView = pageViews[page] {
return
} else {
var frame = scrollView.frame //CGRect(x: 0, y: 0, width: 200, height: scrollView.bounds.height)
frame.origin.x = frame.size.width * CGFloat(page)
frame.origin.y = 0.0
let newPageView = UIImageView(image: sixPlusImages[page])
newPageView.contentMode = .ScaleAspectFit
newPageView.frame = frame
scrollView.addSubview(newPageView)
pageViews[page] = newPageView
}
}
func loadVisiblePages() {
let pageWidth = scrollView.frame.size.width
page = Int(floor((scrollView.contentOffset.x * 2.0 + pageWidth) / (pageWidth * 2.0)))
let firstPage = page - 1
let lastPage = page + 1
for var index = 0; index < firstPage; ++index {
purgePage(index)
}
for index in firstPage...lastPage {
loadPage(index)
}
for var index = lastPage+1; index < sixPlusImages.count; ++index {
purgePage(index)
}
}
func scrollViewDidScroll(scrollView: UIScrollView) {
loadVisiblePages()
}
func purgePage(page: Int) {
if page < 0 || page >= sixPlusImages.count {
return
}
if let pageView = pageViews[page] {
pageView.removeFromSuperview()
pageViews[page] = nil
}
}
So like I say, the first two images are being displayed, but the other three are just blank pages.
Anyone see any issues?
Add UIScrollViewDelegate to the class:
class MyViewController: UIViewController, UIScrollViewDelegate {
}
Then in the storyboard, be sure to right click/drag from the scroll view to the view controller, click delegate.

How to make the pageControl swipe effect? The scroll view slides continuously so far

I would like to make the swipe effect with the scrollView, I found a project where nothing is specifically invoked in the code or in the storyboard. I repeated the code, but in my project, the scrollView just slides continuously, it does not swipe from one imageView to the other. Would you know how to do this?
Here is the code, the scroll respond to the touch, the pagecontrol is a simple iboutlet, but there is no swipe effect (the scroll stops where the touch ends, instead of going back or moving the entire imageview ) :
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var pageControl: UIPageControl!
#IBOutlet weak var scrollView: UIScrollView!
var pageViews: [UIImageView?] = []
var pageImages : [UIImage?] = []
override func viewDidLoad() {
super.viewDidLoad()
pageImages = [UIImage(named:"photo1.png"),
UIImage(named:"photo2.png"),
UIImage(named:"photo3.png"),
UIImage(named:"photo4.png")]
let pageCount = pageImages.count
pageControl.currentPage = 0
pageControl.numberOfPages = pageCount
for _ in 0..<pageCount {
pageViews.append(nil)
}
let pagesScrollViewSize = scrollView.frame.size
println("pagesScrollViewSize : \(pagesScrollViewSize)")
scrollView.contentSize = CGSizeMake(pagesScrollViewSize.width * CGFloat(pageCount), pagesScrollViewSize.height)
loadVisiblePages()
}
//when scrolling
func scrollViewDidScroll(scrollView: UIScrollView!){
loadVisiblePages()
}
func loadVisiblePages(){
let pageWidth = scrollView.frame.size.width
let page = Int(floor( (scrollView.contentOffset.x * 2.0 + pageWidth) / (pageWidth*2.0) ))
println("page: \(page)")
pageControl.currentPage = page
let firstPage = page-1
let lastPage = page+1
//remove all the pages before firstPage
for var index = 0; index < firstPage; ++index{
purgePage(index)
}
//load pages in our range
for var index = firstPage; index <= lastPage; ++index {
loadPage(index)
}
//remove after lastPage
for var index = lastPage+1; index < pageImages.count; ++index {
purgePage(index)
}
}
func loadPage(index:Int){
if index < 0 || index >= pageImages.count {
return
}
if let pageView = pageViews[index] {
//already loaded
}
else {
var frame = scrollView.bounds
frame.origin.x = frame.size.width * CGFloat(index)
frame.origin.y = 0.0
println("\(frame)")
var newImageView = UIImageView(image:pageImages[index])
newImageView.contentMode = .ScaleAspectFit
newImageView.frame = frame
scrollView.addSubview(newImageView)
pageViews[index] = newImageView
}
}
func purgePage(index:Int){
if index < 0 || index >= pageImages.count {
return
}
if let pageView = pageViews[index] {
pageView.removeFromSuperview()
pageViews[index] = nil
}
}
}
UIPageControl doesn't do the page-scrolling behavior — it provides only the dots that indicate which page is shown (and that a user can tap to jump between pages). Use the pagingEnabled property of UIScrollView to scroll by page instead of continuously.
Or just use UIPageViewController to get just about all the scrolling and paging business handled for you.

Resources