2

I have an app (Swift 5) using a UIImageView and a gesture method to change pictures when touching the image. It works great! The below code cycles thru all 26 pictures and starts over when at the last image. I want to create a button to go back to the previous image - if i was at image 5, how would i go back to image 4?

Global variable set: var number = 1

Gesture method set:

            if segControl.selectedSegmentIndex == 0 && segControl2.selectedSegmentIndex == 0 {
                imageView.image = UIImage(named: "card2")
                imageView.image = UIImage(named: "card\(number)")
                number = number % 26 + 1
            }
            else if segControl.selectedSegmentIndex == 0 && segControl2.selectedSegmentIndex == 1 {
                imageView.image = UIImage(named: "upper2")
                imageView.image = UIImage(named: "upper\(number)")
                number = number % 26 + 1
            }
}
Derek N
  • 49
  • 7
  • Why are you setting `imageView.image` to a `UIImage` and then immediately replacing it with another image on the next line? The first one is unnecessary. – vacawama Jun 27 '19 at 14:34

3 Answers3

2

if i was at image 5, how would i go back to image 4?

You could just subtract one from number, but there's a slight complication in that Swift's remainder operator (%) is happy to return a negative number. To make it work more like a modulo operator in other languages, you can add 25 instead of subtracting 1:

number = (number + 25) % 26

That works because (-1 mod 26) is congruent to (25 mod 26), so adding 25 and subtracting 1 are the same thing.

Update: I hadn't noticed that you're counting starting from 1 instead of from 0. I'd still recommend using the method above to cycle through the images, so you're working with the set 0...25 instead of 1...26, and then add an offset (1 in this case) separately to shift the set to match your needs. That way, you're not dealing with some formula that's not easy to understand — it's just plain old modular arithmetic. Keep calculating the index separate from determining the photo number based on that index. Or better, if you can, adjust the numbers you use for the photos to be in the range 0...25. Your future self will thank you.

Caleb
  • 124,013
  • 19
  • 183
  • 272
  • OP is cycling `26 -> 1` not `25 -> 0`. – vacawama Jun 27 '19 at 14:02
  • @vacawama That's a good point, but I think it's still easier to do the math this way and then add any offset (1 in this case) separately. Will add info to that effect. – Caleb Jun 27 '19 at 14:09
  • So i tried it and it works but it takes me two clicks before it works. So if im on card 2, i have to click twice before it goes to card 1. Using this: ```number = (number + 25) % 26 imageView.image = UIImage(named: "card\(number)")``` – Derek N Jun 27 '19 at 15:51
  • @DerekN See vacawama's comment... it sounds like your photos are numbered 1...26, not 0...25. So you'd need to compensate for that difference. For example, you could leave `number` going from 0 to 25, and just add 1 to get the image, e.g. `UIImage(named:"card\(number+1)")`. Or you could number your images starting from 0 instead of 1. – Caleb Jun 27 '19 at 19:21
1

number = number % 26 + 1 cycles through the numbers in the range 1...26 in increasing order.

To cycle through 1...26 in decreasing order

Replace:

number = number % 26 + 1

with:

number = 26 - (27 - number) % 26

or with:

number = (number + 24) % 26 + 1

In general, the formula to cycle 1...N in reverse is:

number = (number + N - 2) % N + 1

The logic behind this formula is that the modulus function % can return 0, so we always add 1 at the end. So, we subtract 1 by first subtracting 2. We add N so that the value doesn't go negative. Adding N is like adding 0 when modded by N. Doing % N leaves us in the range 0...(N-1), and adding 1 gives us 1...N. Since we subtracted 2 and added 1, the result is subtracting 1.

vacawama
  • 150,663
  • 30
  • 266
  • 294
  • So its definitely working but it takes me pressing the back button twice before it works and then it works fine after that: ```number = 26 - (27 - number) % 26 imageView.image = UIImage(named: "card\(number)")``` – Derek N Jun 27 '19 at 16:12
  • Change the number *before* setting the image. – vacawama Jun 27 '19 at 16:14
0

I have build a simple project for you, which works fine for me, hope this helps to you;

import UIKit
import Photos

class ViewController: UIViewController, UIImagePickerControllerDelegate,UINavigationControllerDelegate {

    let appDelegate = UIApplication.shared.delegate as! AppDelegate
    let picker = UIImagePickerController()

    var imageArray: [UIImage] = []
    var photoIndex = 0

    @IBOutlet weak var Billboard: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        picker.delegate = self
        checkPermission()
    }

    @objc  func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {


        if let imageSelected = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
            imageArray.append(imageSelected)
            print(imageArray.count)
            Billboard.image = imageSelected
        }
        appDelegate.window?.rootViewController!.dismiss(animated: true, completion: nil)

    }

    func checkPermission() {
        let photoAuthorizationStatus = PHPhotoLibrary.authorizationStatus()
        switch photoAuthorizationStatus {
        case .authorized:
            print("Access is granted by user")
        case .notDetermined:
            PHPhotoLibrary.requestAuthorization({
                (newStatus) in
                print("status is \(newStatus)")
                if newStatus ==  PHAuthorizationStatus.authorized {
                    /* do stuff here */
                    print("success")
                }
            })
            print("It is not determined until now")
        case .restricted:
            // same same
            print("User do not have access to photo album.")
        case .denied:
            // same same
            print("User has denied the permission.")
        }
    }

    @IBAction func selectMediaBtnPressed(_ sender: Any) {

        picker.delegate = self
        picker.sourceType = .photoLibrary
        picker.allowsEditing = true
        appDelegate.window?.rootViewController!.present(picker, animated: true, completion: nil)
    }


    @IBAction func nextBtn(_ sender: Any) {
        if imageArray.count != 0 && (photoIndex + 1) < imageArray.count {
            Billboard.image = imageArray[ photoIndex + 1 ]
            photoIndex = photoIndex + 1
        } else if photoIndex == imageArray.count {
            Billboard.image = imageArray.first
            photoIndex = 0
        }
    }

    @IBAction func backBtn(_ sender: Any) {
        if imageArray.count != 0 && (photoIndex - 1 ) >= 0 {
            Billboard.image = imageArray[ photoIndex - 1 ]
            if photoIndex != 0 {
            photoIndex = photoIndex - 1
            }
        }
    }


}

On storyboard i have one UIImageView and 3 buttons.

  • 2
    Lots of effort here, but I think you're missing the point of the question. The OP is just asking how to cycle through the images in a circular way. They've got it working such that when they get to the last image, the next image displayed is the first one again, and they just want to know how to go in the other direction. – Caleb Jun 27 '19 at 14:01
  • Thank you for warning Caleb i have just added these lines; else if photoIndex == imageArray.count { Billboard.image = imageArray.first photoIndex = 0 } and now its complete solution :D – Gökhan Sayılgan Jun 27 '19 at 14:06
  • Thanks guys. Lots of effort but all I need is to cycle back. I will be going thru the tips here in a bit. – Derek N Jun 27 '19 at 15:26