8

Ich habe zwei verschiedene UIPickerViews in meiner Ansicht. Sie funktionieren hervorragend, wenn ich die dataSource und den Delegaten auf die Ansicht einstelle, in der sie über das Storyboard gehostet werden, aber wenn ich versuche, das über Code wie unten beschrieben zu tun, funktioniert es nicht.UIPickerView mit "externer" DataSource und Delegate in Swift

Beide Picker müssen unterschiedliche Daten anzeigen (und möglicherweise sogar unterschiedliche Verhaltensweisen für den Delegierten). Daher möchte ich sie programmatisch mit verschiedenen Datenquellen verbinden.

Ich habe versucht, meine eigene Klasse zu erstellen, die UIPickerViewDataSource- und UIPickerViewDelegate-Protokolle implementiert und Objekte dieser Klasse mit meinen PickerViews verbindet, aber es funktioniert nicht. Eine Ausnahme wird zur Laufzeit terminating with uncaught exception of type NSException besagt dies geworfen:

2015-01-09 17:50:05.333 Pet Stats[4953:244338] -[NSConcreteMapTable numberOfComponentsInPickerView:]: unrecognized selector sent to instance 0x7b4616d0 
2015-01-09 17:50:05.338 Pet Stats[4953:244338] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSConcreteMapTable numberOfComponentsInPickerView:]: unrecognized selector sent to instance 0x7b4616d0' 

Wie kann ich dies funktioniert? Was habe ich verpasst? Hier ist mein Code:

WeightWheelController.swift

import UIKit 

class WeightWheelController: NSObject, UIPickerViewDelegate, UIPickerViewDataSource { 
    let ElementCount: Int! 

    init(pickerInterval: Int) { 
     ElementCount = pickerInterval 
    } 

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

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

    func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! { 
     return String(row + 1) 
    } 

    func pickerView(pickerView: UIPickerView!, didSelectRow row: Int, inComponent component: Int) 
    { 
     println("External Controller:" + String(row + 1)) 
    } 
} 

WeightWheelInputViewController.swift

import UIKit 

class WeightWheelInputViewController: UIViewController { 
    @IBOutlet weak var picker1: UIPickerView!   
    @IBOutlet weak var picker2: UIPickerView! 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     //picker attached to c1 should show number from 1 to 150 
     let c1 = WeightWheelController(pickerInterval: 150) 

     //picker attached to c1 should show number from 1 to 10 
     let c2 = WeightWheelController(pickerInterval: 10) 

     picker1.dataSource = c1 
     picker1.delegate = c1 

     picker2.dataSource = c2 
     picker2.delegate = c2 
    } 

    override func didReceiveMemoryWarning() { 
     super.didReceiveMemoryWarning() 
    } 
} 

KURZ UPDATE:

In dieser Frage habe ich gefunden, dass Sie verwenden können, verschiedene Tags für verschiedene Pickeransichten. Das wäre eine Option; Trotzdem mag ich es nicht. Ich möchte lieber einen MVC-Ansatz verfolgen und verschiedene Controller an jeden Picker anschließen. Ist das nicht in irgendeiner Weise möglich?

Antwort

13

Sowohl delegate als auch datasource sind nicht eingetragene Referenzen. Dies bedeutet, dass c1 und c2 freigegeben werden, sobald Sie den Geltungsbereich verlassen. Versuchen Sie, c1 und c2 als Eigenschaften der Klasse zu deklarieren.

Nicht zugeordnete Referenzen erzeugen kein starkes Halten des referenzierten Objekts (z. B. erhöhen sie nicht die Retain-Anzahl, um ARC daran zu hindern, das referenzierte Objekt aufzuheben).

Stellen Sie außerdem sicher, dass Sie die Eigenschaften delegate und data source der PickerViews vom Interface Builder entfernen.

class WeightWheelInputViewController: UIViewController { 
    @IBOutlet weak var picker1: UIPickerView!   
    @IBOutlet weak var picker2: UIPickerView! 

    var c1 : WeightWheelController! 
    var c2 : WeightWheelController! 

    override func viewDidLoad() { 
     super.viewDidLoad() 

     c1 = WeightWheelController(pickerInterval: 150) 

     c2 = WeightWheelController(pickerInterval: 10) 

     picker1.dataSource = c1 
     picker1.delegate = c1 

     picker2.dataSource = c2 
     picker2.delegate = c2 
    } 

    override func didReceiveMemoryWarning() { 
     super.didReceiveMemoryWarning() 
    } 
} 
+3

Große Antwort! Arbeitete direkt aus der Box! Ich danke dir sehr!!! Was ich jedoch nicht verstehe, ist Folgendes: In Java könnte man das tun, was ich oben kodiert habe, und der GC wird die Objekte mit der "schwachen" Referenz nicht sammeln, weil sie jetzt von der PickerView referenziert werden. Wie geht das schnell anders? – Christian

+0

gut. Dazu müssen Sie sich mit der automatischen Referenzzählung (ARC) vertraut machen. Ich schlage vor, du liest die Dokumentation von Apple darüber. https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html – rakeshbs

+1

Erstaunliche Antwort. Ich wurde verrückt. Tausend Dank! –