IOS image viewer in swift, programmatically - ios

i am trying to make an imageViewer using this toturial
this toturial
i want to use the "PagedScrollViewController" example and make it using swift,
i am able to display images on screen, but i have issues with the scroll.
the scroll is not stoping between pages/photos, it scrolling freely, so that way loadVisiblePages is always called each pixel that i scroll.
probably i am missing something, can you help me with this please?
here is what i am doing :
thank you
import UIKit
class PagedScrollViewController:UIViewController,UIScrollViewDelegate {
var pageImages:[UIImage] = [UIImage]()
var pageViews:[UIView?] = [UIView]()
var scrollView:UIScrollView = UIScrollView()
var pageControl:UIPageControl = UIPageControl()
func loadVisiblePages() {
// First, determine which page is currently visible
var pageWidth:CGFloat = self.scrollView.frame.size.width;
var 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 we want to load
var firstPage = page - 1;
var lastPage = page + 1;
// Purge anything before the first page
for (var i=0; i<firstPage; i++) {
println("1) purge index \(i)")
self.purgePage(i)
}
for (var i=firstPage; i<=lastPage; i++) {
println("2) load index \(i)")
self.loadPage(i)
}
for (var i = lastPage+1 ; i < self.pageImages.count ; i++) {
println("3) purge index \(i)")
self.purgePage(i)
}
}
//
func loadPage(page:Int) {
if page < 0 || page >= self.pageImages.count {
// If it's outside the range of what we have to display, then do nothing
return;
}
// Load an individual page, first seeing if we've already loaded it
var pageView:UIView? = self.pageViews[page];
if pageView == nil {
var frame:CGRect = self.scrollView.bounds;
//frame.origin.x = frame.size.width * CGFloat(page)
frame.origin.x = 320.0 * CGFloat(page)
frame.origin.y = 0.0
var newPageView:UIImageView = UIImageView(image: self.pageImages[page])
newPageView.contentMode = UIViewContentMode.ScaleAspectFit;
newPageView.frame = frame;
self.scrollView.addSubview(newPageView)
self.pageViews[page] = newPageView
}
}
func purgePage(page:Int) {
if page < 0 || page >= self.pageImages.count {
// If it's outside the range of what we have to display, then do nothing
return;
}
// Remove a page from the scroll view and reset the container array
var pageView:UIView? = self.pageViews[page];
if pageView != nil {
pageView?.removeFromSuperview()
self.pageViews[page] = UIView()//nil
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Paged";
self.view.backgroundColor = UIColor.blueColor()
// Set up the image we want to scroll & zoom and add it to the scroll view
self.pageImages.append(UIImage(named: "first.png"))
self.pageImages.append(UIImage(named: "second.png"))
self.pageImages.append(UIImage(named: "first.png"))
self.pageImages.append(UIImage(named: "second.png"))
self.pageImages.append(UIImage(named: "first.png"))
var pageCount = self.pageImages.count
self.scrollView.delegate = self
//self.tableView.showsHorizontalScrollIndicator = false
//self.tableView.showsVerticalScrollIndicator = false
// Set up the page control
self.pageControl.currentPage = 0;
self.pageControl.numberOfPages = pageCount;
self.pageControl.setTranslatesAutoresizingMaskIntoConstraints(false)
self.scrollView.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addSubview(self.pageControl)
self.view.addSubview(self.scrollView)
//Set layout
var viewsDict = Dictionary <String, UIView>()
viewsDict["control"] = self.pageControl;
viewsDict["scrollView"] = self.scrollView;
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[scrollView]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[control]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[scrollView(400)]-[control]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
// Set up the array to hold the views for each page
for (var i = 0; i < pageCount; ++i) {
self.pageViews.append(nil)
}
}
override func viewDidAppear(animated:Bool) {
super.viewDidAppear(animated)
// Set up the content size of the scroll view
var pagesScrollViewSize:CGSize = self.scrollView.frame.size;
self.scrollView.contentSize = CGSizeMake(pagesScrollViewSize.width * CGFloat(self.pageImages.count), pagesScrollViewSize.height);
// Load the initial set of pages that are on screen
self.loadVisiblePages()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// self.scrollView = nil
// self.pageControl = nil
// self.pageImages = nil
// self.pageViews = nil
}
func scrollViewDidScroll(scrollView:UIScrollView ) {
// Load the pages which are now on screen
self.loadVisiblePages()
println("scrollViewDidScroll")
}
}

the problem was i didn't set the 'self.scrollView.pagingEnabled' to true. still i am now to UIKit.
any way i made some performance improvement, and the layout is call only when needed now,
hope someone will find this usfull :)
thanks to http://www.raywenderlich.com
import UIKit
class PagedScrollViewController:UIViewController,UIScrollViewDelegate {
var pageImages:[UIImage] = [UIImage]()
var pageViews:[UIView?] = [UIView]()
var scrollView:UIScrollView = UIScrollView()
var pageControl:UIPageControl = UIPageControl()
var viewingPage = -1
func loadVisiblePages() {
// First, determine which page is currently visible
var pageWidth:CGFloat = self.scrollView.frame.size.width;
var page = Int(floor((self.scrollView.contentOffset.x * 2.0 + pageWidth) / (pageWidth * 2.0)));
/*
Check that page have changed,
in case that user drag left in first page, or drag right in last page
a 'scrollViewDidEndDecelerating' is fired
*/
if viewingPage != page {
// Update the page control
self.pageControl.currentPage = page;
// Work out which pages we want to load
var firstPage = page - 1;
var lastPage = page + 1;
// Purge anything before the first page
for (var i=0; i<firstPage; i++) {
self.purgePage(i)
}
for (var i=firstPage; i<=lastPage; i++) {
self.loadPage(i)
}
for (var i = lastPage+1 ; i < self.pageImages.count ; i++) {
self.purgePage(i)
}
viewingPage = page
}
}
func loadPage(page:Int) {
if page < 0 || page >= self.pageImages.count {
// If it's outside the range of what we have to display, then do nothing
return;
}
// Load an individual page, first seeing if we've already loaded it
var pageView:UIView? = self.pageViews[page];
if pageView == nil {
var frame:CGRect = self.scrollView.bounds;
//frame.origin.x = frame.size.width * CGFloat(page)
frame.origin.x = 320.0 * CGFloat(page)
frame.origin.y = 0.0
var newPageView:UIImageView = UIImageView(image: self.pageImages[page])
newPageView.contentMode = UIViewContentMode.ScaleAspectFit;
newPageView.frame = frame;
self.scrollView.addSubview(newPageView)
self.pageViews[page] = newPageView
}
}
func purgePage(page:Int) {
if page < 0 || page >= self.pageImages.count {
// If it's outside the range of what we have to display, then do nothing
return;
}
// Remove a page from the scroll view and reset the container array
var pageView:UIView? = self.pageViews[page];
if pageView != nil {
pageView?.removeFromSuperview()
self.pageViews[page] = nil
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Image viewer";
self.view.backgroundColor = UIColor.blueColor()
// Set up the image we want to scroll & zoom and add it to the scroll view
self.pageImages.append(UIImage(named: "message_full.png"))
self.pageImages.append(UIImage(named: "heart_full"))
self.pageImages.append(UIImage(named: "star_full.png"))
self.pageImages.append(UIImage(named: "second.png"))
self.pageImages.append(UIImage(named: "first.png"))
var pageCount = self.pageImages.count
self.scrollView.pagingEnabled = true
self.scrollView.delegate = self
self.scrollView.showsHorizontalScrollIndicator = false
self.scrollView.showsVerticalScrollIndicator = false
// Set up the page control
self.pageControl.currentPage = 0;
self.pageControl.numberOfPages = pageCount;
self.pageControl.setTranslatesAutoresizingMaskIntoConstraints(false)
self.scrollView.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addSubview(self.pageControl)
self.view.addSubview(self.scrollView)
//Set layout
var viewsDict = Dictionary <String, UIView>()
viewsDict["control"] = self.pageControl;
viewsDict["scrollView"] = self.scrollView;
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[scrollView]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[control]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[scrollView(400)]-[control]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
// Set up the array to hold the views for each page
for (var i = 0; i < pageCount; ++i) {
self.pageViews.append(nil)
}
}
override func viewDidAppear(animated:Bool) {
super.viewDidAppear(animated)
// Set up the content size of the scroll view
var pagesScrollViewSize:CGSize = self.scrollView.frame.size;
self.scrollView.contentSize = CGSizeMake(pagesScrollViewSize.width * CGFloat(self.pageImages.count), pagesScrollViewSize.height);
// Load the initial set of pages that are on screen
self.loadVisiblePages()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
//TODO make cleaning
// self.scrollView = nil
// self.pageControl = nil
// self.pageImages = nil
// self.pageViews = nil
}
func scrollViewDidEndDecelerating(scrollView: UIScrollView!) {
self.loadVisiblePages()
}
}

Related

how to use a Custom ImageView ( class ) in a TableViewCell

am trying to get this in my UITableViewCell
an imageView which can hold an array of Images and can be scrollable
so i tried this :
class MostWantedImageView: UIImageView , UIScrollViewDelegate{
var pageImages:[UIImage] = [UIImage]()
var pageViews:[UIImageView?] = [UIImageView]()
var scrollView:UIScrollView = UIScrollView()
var pageControl:UIPageControl = UIPageControl()
var viewingPage = -1
func loadVisiblePages() {
// First, determine which page is currently visible
let pageWidth:CGFloat = self.frame.size.width;
let page = Int(floor((self.scrollView.contentOffset.x * 2.0 + pageWidth) / (pageWidth * 2.0)));
/*
Check that page have changed,
in case that user drag left in first page, or drag right in last page
a 'scrollViewDidEndDecelerating' is fired
*/
if viewingPage != page {
// Update the page control
self.pageControl.currentPage = page;
// Work out which pages we want to load
let firstPage = page - 1;
let lastPage = page + 1;
// Purge anything before the first page
for (var i=0; i<firstPage; i += 1) {
self.purgePage(i)
}
for (var i=firstPage; i<=lastPage; i += 1) {
self.loadPage(i)
}
for (var i = lastPage+1 ; i < self.pageImages.count ; i += 1) {
self.purgePage(i)
}
viewingPage = page
}
}
func loadPage(page:Int) {
if page < 0 || page >= self.pageImages.count {
// If it's outside the range of what we have to display, then do nothing
return;
}
// Load an individual page, first seeing if we've already loaded it
let pageView:UIImageView? = self.pageViews[page];
if pageView == nil {
var frame:CGRect = self.bounds;
//frame.origin.x = frame.size.width * CGFloat(page)
frame.origin.x = 320.0 * CGFloat(page)
frame.origin.y = 0.0
var newPageView:UIImageView = UIImageView(image: self.pageImages[page])
newPageView.contentMode = UIViewContentMode.ScaleAspectFit;
newPageView.frame = frame;
self.scrollView.addSubview(newPageView)
self.pageViews[page] = newPageView
}
}
func purgePage(page:Int) {
if page < 0 || page >= self.pageImages.count {
// If it's outside the range of what we have to display, then do nothing
return;
}
// Remove a page from the scroll view and reset the container array
let pageView:UIView? = self.pageViews[page];
if pageView != nil {
pageView?.removeFromSuperview()
self.pageViews[page] = nil
}
}
override init(frame: CGRect ) {
super.init(frame: frame )
self.backgroundColor = UIColor.blueColor()
// Set up the image we want to scroll & zoom and add it to the scroll view
self.pageImages.append(UIImage(named: "event1")!)
self.pageImages.append(UIImage(named: "event2")!)
self.pageImages.append(UIImage(named: "event3")!)
let pageCount = self.pageImages.count
self.scrollView.pagingEnabled = true
self.scrollView.delegate = self
self.scrollView.showsHorizontalScrollIndicator = false
self.scrollView.showsVerticalScrollIndicator = false
// Set up the page control
self.pageControl.currentPage = 0;
self.pageControl.numberOfPages = pageCount;
self.pageControl.frame = self.frame
self.scrollView.frame = self.frame
self.pageControl.translatesAutoresizingMaskIntoConstraints = false
self.scrollView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(self.pageControl)
self.addSubview(self.scrollView)
self.userInteractionEnabled = true
//Set layout
var viewsDict = Dictionary <String, UIView>()
viewsDict["control"] = self.pageControl;
viewsDict["scrollView"] = self.scrollView;
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[scrollView]-0-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: viewsDict))
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[control]-0-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: viewsDict))
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[scrollView(400)]-[control]-0-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: viewsDict))
// Set up the array to hold the views for each page
for (var i = 0; i < pageCount; ++i) {
self.pageViews.append(nil)
}
// Set up the content size of the scroll view
let pagesScrollViewSize:CGSize = self.scrollView.frame.size;
self.scrollView.contentSize = CGSizeMake(pagesScrollViewSize.width * CGFloat(self.pageImages.count), pagesScrollViewSize.height);
// Load the initial set of pages that are on screen
self.loadVisiblePages()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
init() {
super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
}
func scrollViewDidEndDecelerating(scrollView: UIScrollView!) {
self.loadVisiblePages()
}
func ResizeImage(image: UIImage, targetSize: CGSize) -> UIImage {
let size = image.size
let widthRatio = targetSize.width / image.size.width
let heightRatio = targetSize.height / image.size.height
// Figure out what our orientation is, and use that to form the rectangle
var newSize: CGSize
if(widthRatio > heightRatio) {
newSize = CGSizeMake(size.width * heightRatio, size.height * heightRatio)
} else {
newSize = CGSizeMake(size.width * widthRatio, size.height * widthRatio)
}
// This is the rect that we've calculated out and this is what is actually used below
let rect = CGRectMake(0, 0, newSize.width, newSize.height)
// Actually do the resizing to the rect using the ImageContext stuff
//UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
image.drawInRect(rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}
its a ImageView where i putted scrollView and UIPageControll i can use this in my ViewController but have no idea how to use this in my TableViewCell , if somebody can give me some guidance in this then it'll be helpful for me
Please use a CollectionView instead. As the number of images will grow, your app will have performance issues.
You need to take a look at this-
putting-a-uicollectionview-in-a-uitableviewcell-in-swift
This will help you understand how to use collectionView inside a table cell.

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.

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!

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.

Memory warnings when trying to load 200+ images

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

Resources