0

"There's a similar question already answered, but doesn't work on my app"

Is there any way that I can make my UIPickerView round as a loop? I've got 0 to 9 on each column but I don't want it to end at 9. I also have a UIButton thats have a func which makes the numbers skip a digit and it starts from 000 to 999, if I make the UIPickerView as a loop I don't want it to make any changes or effects on its mathematics order Here's what I have so far:

class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {

@IBOutlet weak var label: UILabel!
@IBOutlet weak var pickerView: UIPickerView!

let numbers = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]

func numberOfComponents(in pickerView: UIPickerView) -> Int {
    return 3
}

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
    return numbers[row]
}

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
    return numbers.count
}

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {

    let val1 = numbers[pickerView.selectedRow(inComponent: 0)]
    let val2 = numbers[pickerView.selectedRow(inComponent: 1)]
    let val3 = numbers[pickerView.selectedRow(inComponent: 2)]

    label.text = "\(val1) \(val2) \(val3)"
}

fileprivate func num(_ i: Int) -> Int {
    return pickerView.selectedRow(inComponent: i)
}

@IBAction func buttonPressed() {
    let currentNum = num(0) * 100 + num(1) * 10 + num(2)
    let nextNum = currentNum + 1

    pickerView.selectRow(nextNum % 1000 / 100, inComponent: 0, animated: true)
    pickerView.selectRow(nextNum % 100 / 10, inComponent: 1, animated: true)
    pickerView.selectRow(nextNum % 10, inComponent: 2, animated: true)

    changeLabelText()
}

fileprivate func changeLabelText() {
    label.text = "\(num(0)) \(num(1)) \(num(2))"
}

according to the other question thats been answered, I have to edit my code like:

class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {

func valueForRow(row: Int) -> Int {
    // the rows repeat every `pickerViewData.count` items
    return pickerViewData[row % pickerViewData.count]
}

func rowForValue(value: Int) -> Int? {
    if let valueIndex = find(pickerViewData, value) {
        return pickerViewMiddle + value
    }
    return nil
}

func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
    return "\(valueForRow(row))"
}

func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
    return 3
}

func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
    return pickerViewRows
}

// whenever the picker view comes to rest, we'll jump back to
// the row with the current value that is closest to the middle
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
    let newRow = pickerViewMiddle + (row % pickerViewData.count)
    pickerView.selectRow(newRow, inComponent: 0, animated: false)
}



@IBOutlet weak var label: UILabel!
@IBOutlet weak var pickerView: UIPickerView!

let numbers = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]

func numberOfComponents(in pickerView: UIPickerView) -> Int {
    return 3
}

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
    return numbers[row]
}

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
    return numbers.count
}

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {

    let val1 = numbers[pickerView.selectedRow(inComponent: 0)]
    let val2 = numbers[pickerView.selectedRow(inComponent: 1)]
    let val3 = numbers[pickerView.selectedRow(inComponent: 2)]

    label.text = "\(val1) \(val2) \(val3)"
}

fileprivate func num(_ i: Int) -> Int {
    return pickerView.selectedRow(inComponent: i)
}

@IBAction func buttonPressed() {
    let currentNum = num(0) * 100 + num(1) * 10 + num(2)
    let nextNum = currentNum + 1

    pickerView.selectRow(nextNum % 1000 / 100, inComponent: 0, animated: true)
    pickerView.selectRow(nextNum % 100 / 10, inComponent: 1, animated: true)
    pickerView.selectRow(nextNum % 10, inComponent: 2, animated: true)

    changeLabelText()
}

fileprivate func changeLabelText() {
    label.text = "\(num(0)) \(num(1)) \(num(2))"
}


private let pickerViewData = Array(0...59)     // contents will be 0, 1, 2, 3...59, change to whatever you want
private let pickerViewRows = 10_000            // any big number
private let pickerViewMiddle = ((pickerViewRows / pickerViewData.count) / 2) * pickerViewData.count






override func viewDidLoad() {
    super.viewDidLoad()
    self.picker.delegate = self
    self.picker.dataSource = self
    let initialValue = 0
    if let row = rowForValue(initialValue) {
        self.picker.selectRow(row, inComponent: 0, animated: false)
    }
    // or if you just want to start in the middle:
    // self.picker.selectRow(pickerViewMiddle, inComponent: 0, animated: false)
}

But I'm getting 6 errors on these 6 different lines:

 if let valueIndex = find(pickerViewData, value) {
        return pickerViewMiddle + value
//
return "\(valueForRow(row))"
//
 private let pickerViewMiddle = ((pickerViewRows / pickerViewData.count) / 2) * pickerViewData.count

//
self.picker.delegate = self
    self.picker.dataSource = self
    let initialValue = 0
    if let row = rowForValue(initialValue) {
        self.picker.selectRow(row, inComponent: 0, animated: false)
Shahin
  • 71
  • 2
  • 11
  • Your question is very unclear. Please [edit] your question to clearly explain what issue you are having with the code you posted and explain how [the other question](https://stackoverflow.com/questions/26063039/uipickerview-loop-the-data) doesn't solve your issue of allowing each component to loop. – rmaddy Sep 13 '17 at 21:15
  • That is not possible with a `UIPickerView`. You have to implement your own custom `UIView` subclass to create such a behaviour. – André Slotta Sep 13 '17 at 21:29
  • @AndréSlotta this should be possible - Apple does it with picker views when selecting time. It's an infinite looping scroll – JAB Sep 13 '17 at 22:13
  • @BJHStudios No. Even the date picker is not infinite. Scroll the year part back to the year `1` and you will see that it ends there. – André Slotta Sep 13 '17 at 22:21
  • @AndréSlotta if you scroll minutes in the calendar it will let you scroll (sort of) infinitely. After a lot of scrolling it eventually reaches the end, but then if you let it sit for a few seconds, it will reload itself or reset the column. Didn't look at years, but maybe there are different rules depending on the column. – JAB Sep 13 '17 at 22:37
  • @rmaddy I’ve edited my question – Shahin Sep 13 '17 at 22:38

1 Answers1

5

This is not really infinite loop but should work pretty well. The idea is that you increase your data source range by multiple of whatever margin you want to add (I just randomly set the margin to 40) and when scroll stops, resume back to the middle of the data source range. This implementation will work like infinite loop unless a user scroll (40 / 2 x 10) items without stopping scroll. Added the code to show the idea.

let loopingMargin: Int = 40

var data: [String] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]

var picker: UIPickerView  = UIPickerView()

override func viewDidLoad() {
    super.viewDidLoad()

    picker.delegate = self
    picker.dataSource = self
    view.addSubview(picker)
    picker.selectRow((loopingMargin / 2) * data.count, inComponent: 0, animated: false)
}


// MARK: UIPickerViewDelegate, UIPickerViewDataSource
func numberOfComponents(in pickerView: UIPickerView) -> Int {
    return 1
}

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
    return loopingMargin * data.count
}

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
    return data[row % data.count]
}

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
    let currentIndex = row % data.count
    picker.selectRow((loopingMargin / 2) * data.count + currentIndex, inComponent: 0, animated: false)
}
HMHero
  • 2,333
  • 19
  • 11