Blog

Modellaktualisierungen in Präsentationssteuerungen

Lammert Westerhoff

Lammert Westerhoff

Aktualisiert Oktober 22, 2025
6 Minuten

In diesem Beitrag erkläre ich, wie Sie mit Updates umgehen, wenn Sie Presentation Controls in iOS verwenden. Er ist eine Fortsetzung meines vorherigen Beitrags, in dem ich beschrieben habe, wie Sie Presentation Controls anstelle von MVVM oder in Kombination mit MVVM verwenden können.

Der vorherige Beitrag befasste sich nicht mit Aktualisierungen. Aber meistens können sich die Dinge, die auf dem Bildschirm angezeigt werden, ändern. Das kann passieren, weil neue Daten von einem Server geholt werden, durch Benutzerinteraktion oder vielleicht auch automatisch im Laufe der Zeit. Damit das funktioniert, müssen wir unsere Präsentationssteuerungen über alle Aktualisierungen unserer Modellobjekte informieren. Lassen Sie uns den Trip aus dem vorherigen Beitrag noch einmal verwenden: [code language="obj-c"] struct Trip { let Abfahrt: NSDate let arrival: NSDate let duration: NSTimeInterval var actualDeparture: NSDate var delay: NSTimeInterval { return self.actualDeparture.timeIntervalSinceDate(self.departure) } var delayed: Bool { return delay > 0 } init(departure: NSDate, arrival: NSDate, actualDeparture: NSDate? = nil) { self.departure = departure self.arrival = arrival self.actualDeparture = actualDeparture ?? departure // Berechnungen duration = self.arrival.timeIntervalSinceDate(self.departure) } } [/code] Anstatt die Eigenschaften delay und delayed in init zu berechnen und zu setzen, haben wir sie in berechnete Eigenschaften umgewandelt. Das liegt daran, dass wir in den nächsten Beispielen den Wert der Eigenschaft actualDeparture ändern und den neuen Wert der Eigenschaft delay ebenfalls anzeigen möchten. Wie werden wir also über Änderungen in Trip informiert? Ein guter Ansatz dafür ist die Bindung. Sie könnten dazu ReactiveCocoa verwenden, aber der Einfachheit halber werde ich in diesem Beitrag eine Klasse Dynamic verwenden, die in einem Beitrag über Bindungen, Generics, Swift und MVVM von Srdan Rasic vorgestellt wurde (viele Dinge in meinem Beitrag sind von seinen Texten inspiriert, also lesen Sie unbedingt seinen großartigen Beitrag). Die Dynamic sieht wie folgt aus: [code language="obj-c"] Klasse Dynamisch<T> { typealias Listener = T -> Void var listener: Listener? func bind(listener: Listener?) { self.listener = listener } func bindAndFire(listener: Listener?) { self.listener = listener listener?(Wert) } var value: T { didSet { listener?(Wert) } } init( v: T) { Wert = v } } [/code] Damit können wir einen Listener registrieren, der über jede Änderung des Wertes informiert wird. Ein kurzes Beispiel für seine Verwendung: [code language="obj-c"] let delay = Dynamic("+5 Minuten") delay.bindAndFire { print("Delay: ($0)") } delay.value = "+6 Minuten" // wird 'Delay: +6 Minuten' [/code] Unsere Präsentationssteuerung verwendete eine TripViewViewModel-Klasse, um alle Werte zu erhalten, die sie in unserer Ansicht anzeigen musste. Bei diesen Eigenschaften handelte es sich um einfache Konstanten mit Typen wie String und Bool, die sich nie ändern würden. Wir können die Eigenschaften, die sich ändern können, durch eine dynamische Eigenschaft ersetzen.In Wirklichkeit würden wir wahrscheinlich alle Eigenschaften dynamisch machen und eine neue Reise von unserem Server abrufen und diese verwenden, um alle Werte aller dynamischen Eigenschaften zu setzen, aber in unserem Beispiel werden wir nur die actualDeparture der Reise ändern und dynamische Eigenschaften für die delay und delayed Eigenschaften erstellen. So können Sie später genau sehen, was passiert. Unser neues TripViewViewModel sieht jetzt wie folgt aus: [code language="obj-c"] Klasse TripViewViewModel { let date: String let departure: String let Ankunft: String let duration: String private static let durationShortFormatter: NSDateComponentsFormatter = { let durationFormatter = NSDateComponentsFormatter() durationFormatter.allowedUnits = [.Hour, .Minute] durationFormatter.unitsStyle = .Short return durationFormatter }() private static let durationFullFormatter: NSDateComponentsFormatter = { let durationFormatter = NSDateComponentsFormatter() durationFormatter.allowedUnits = [.Hour, .Minute] durationFormatter.unitsStyle = .Full return durationFormatter }() let delay: Dynamic<String?> let delayed: Dynamic<Bool> var trip: Reise init( Trip: Trip) { self.trip = trip date = NSDateFormatter.localizedStringFromDate(trip.departure, dateStyle: .ShortStyle, timeStyle: .NoStyle) departure = NSDateFormatter.localizedStringFromDate(trip.departure, dateStyle: .NoStyle, timeStyle: .ShortStyle) arrival = NSDateFormatter.localizedStringFromDate(trip.arrival, dateStyle: .NoStyle, timeStyle: .ShortStyle) duration = TripViewViewModel.durationShortFormatter.stringFromTimeInterval(trip.duration)! delay = Dynamic(trip.delayString) verzögert = Dynamic(trip.verzögert) } func changeActualDeparture(delta: NSTimeInterval) { trip.actualDeparture = NSDate(timeInterval: delta, sinceDate: trip.actualDeparture) self.delay.value = trip.delayString self.delayed.value = trip.delayed } } Verlängerung Trip { private var delayString: String? { return verzögert ? String.localizedStringWithFormat(NSLocalizedString("%@ Verzögerung", Kommentar: "Zeige die Verzögerung"), TripViewViewModel.durationFullFormatter.stringFromTimeInterval(delay)!) : nil } } [/code] Mit der Methode changeActualDeparture können wir die Zeit von trip.actualDeparture erhöhen oder verringern. Da die Eigenschaften delay und delayed von trip jetzt berechnete Eigenschaften sind, werden auch die zurückgegebenen Werte aktualisiert. Wir verwenden sie, um neue Werte für die Eigenschaften Dynamic delay und delayed unseres TripViewViewModells festzulegen. Auch die Logik zur Formatierung des Delay-Strings wurde in eine Erweiterung von Trip verschoben, um doppelten Code zu vermeiden. Um dies wieder zum Laufen zu bringen, müssen wir jetzt nur noch Bindungen im TripPresentationControl erstellen: [code language="obj-c"] class TripPresentationControl: NSObject { @IBOutlet weak var dateLabel: UILabel! @IBOutlet weak var departureTimeLabel: UILabel! @IBOutlet weak var arrivalTimeLabel: UILabel! @IBOutlet weak var durationLabel: UILabel! @IBOutlet weak var delayLabel: UILabel! var tripModel: TripViewViewModel! { didSet { dateLabel.text = tripModel.date departureTimeLabel.text = tripModel.departure arrivalTimeLabel.text = tripModel.arrival durationLabel.text = tripModel.arrival tripModel.delay.bindAndFire { [unowned self] in self.delayLabel.text = $0 } tripModel.delayed.bindAndFire { [unowned self] delayed in self.delayLabel.hidden = !delayed self.departureTimeLabel.textColor = delayed ? .redColor() : UIColor(rot: 0, grün: 0, blau: 0.4, alpha: 1.0) } } } } [/code] Auch wenn alles wieder kompiliert wird, sind wir noch nicht fertig. Wir brauchen noch eine Möglichkeit, die Verzögerung zu ändern. Das machen wir mit einer einfachen Benutzerinteraktion und fügen unserer Ansicht zwei Schaltflächen hinzu. Eine, um die Verzögerung um eine Minute zu erhöhen und eine, um sie zu verringern. Die Bearbeitung der Schaltflächen erfolgt im normalen View Controller, da wir unser Presentation Control nicht für die Benutzerinteraktion verantwortlich machen wollen. Unser endgültiger View Controller sieht nun wie folgt aus: [code language="obj-c"] class ViewController: UIViewController { @IBOutlet var tripPresentationControl: TripPresentationControl! let tripModel = TripViewViewModel(Trip(Abfahrt: NSDate(timeIntervalSince1970: 1444396193), arrival: NSDate(timeIntervalSince1970: 1444397193), actualDeparture: NSDate(timeIntervalSince1970: 1444396493))) override func viewDidLoad() { super.viewDidLoad() tripPresentationControl.tripModel = tripModel } @IBAction func increaseDelay(sender: AnyObject) { tripModel.changeActualDeparture(60) } @IBAction func decreaseDelay(sender: AnyObject) { tripModel.changeActualDeparture(-60) } } [/code] Wir haben jetzt eine elegante Möglichkeit, die Ansicht zu aktualisieren, wenn wir auf die Schaltfläche tippen. Unser View Controller kommuniziert eine logische Änderung des Modells an das TripViewViewModel, das wiederum das TripPresentationControl über eine Änderung der Daten informiert, das wiederum die Benutzeroberfläche aktualisiert. Auf diese Weise muss das Presentation Control nichts über die Benutzerinteraktion wissen und unser View Controller muss nicht wissen, welche UI-Komponenten er nach der Benutzerinteraktion ändern muss. Und das Ergebnis:

[video width="404" height="720" mp4="https://storage.googleapis.com/xebia-blog/1/2015/10/viewcontrol.mp4"][/video]

Wir hoffen, dass dieser Beitrag Ihnen ein besseres Verständnis für die Verwendung von Presentation Controls und MVVM vermittelt. Wie ich bereits in meinem vorherigen Beitrag erwähnt habe, empfehle ich Ihnen die Lektüre von Einführung in MVVM von Ash Furrow und From MVC to MVVM in Swift von Srdan Rasic sowie seinen am Anfang dieses Beitrags erwähnten Folgebeitrag. Und natürlich sollten Sie unbedingt am 9. November 2015 an der do {iOS} Konferenz in Amsterdam teilnehmen. Hier wird Natasha "the Robot" Murashev einen Vortrag über protokollorientierte MVVM halten.

Verfasst von

Lammert Westerhoff

Contact

Let’s discuss how we can support your journey.