12+ Jahre App-Entwicklung
Schnell mit KI, stark durch Erfahrung
50+ Erfolgreiche App-Projekte

Blog

UISplitViewController - mehrere Detailansichten mit UINavigationController verwenden

In einem aktuellen Projekt besteht die Anforderung eine mehrere Ebenen tiefe Navigation mit entsprechenden Unterebenen für die Navigation umzusetzen. Da es sich um eine iPad-App handelt, bietet sich für die Navigation der UISplitViewController mit entsprechenden UINavigationController für den Master, die linke Seite, und die Details, die rechte Seite, an.

Ich bin dabei auf das Problem gestoßen, dass beim Wechsel der Detailansichten der Hinweise auf das Menü im Portrait-Modus verschwindet. Nachfolgend möchte ich beschreiben, wie ich das Problem lösen konnte.

Voraussetzungen schaffen

Innerhalb von FinishedLaunching wird der SplitViewController instanziiert und dem Window als RootViewController zugewiesen.

var splitViewController = new UISplitViewController();
splitViewController.Delegate = new SplitViewControllerDelegate();

var detailViewController = new UIViewController();
var navigationRootController = new MainNavigationController();

splitViewController.ViewControllers = new UIViewController[]{ new UINavigationController(navigationRootController), new UINavigationController(detailViewController) };
...
window.RootViewController = splitViewController;

Weil im Portrait-Mode die linke Navigation, der sogenannte Master, ausgeblendet wird, ist für den Benutzer der App nicht sofort erkenntlich, dass er weitere Funktionen darüber erreichen kann. Wie hilfreich wäre es da, wenn die App einen entsprechen Hinweise einblenden würde?

Mit wenig Aufwand lässt sich das sehr schnell realisieren. Es muss lediglich die Delegate-Property des SplitViewControllers genutzt werden.

Dazu erstellen wir uns eine eigene UISplitViewDelegate-Implementierung.

class SplitViewControllerDelegate : UISplitViewControllerDelegate
{
    public override bool ShouldHideViewController(UISplitViewController svc, UIViewController viewController, UIInterfaceOrientation inOrientation)
    {
        return inOrientation == UIInterfaceOrientation.Portrait || inOrientation == UIInterfaceOrientation.PortraitUpsideDown;
    }
    [Export("splitViewController:willHideViewController:withBarButtonItem:forPopoverController:")]
    public void WillHideViewController(UISplitViewController splitController, UIViewController viewController, UIBarButtonItem barButtonItem, UIPopoverController popoverController)
    {
        barButtonItem.Title = viewController.Title;
 
        var detailNavController = splitController.ViewControllers[1] as UINavigationController;
        var detailViewController = detailNavController.TopViewController;
		detailViewController.NavigationItem.SetLeftBarButtonItem(barButtonItem, true);
    }

    [Export("splitViewController:willShowViewController:invalidatingBarButtonItem:")]
    public void WillShowViewController(UISplitViewController svc, UIViewController vc, UIBarButtonItem button)
    {
		svc.ChildViewControllers[1].ChildViewControllers[0].NavigationItem.SetLeftBarButtonItem(null, true);
    }
}

Fehlen noch die Controller für Master und Detailansicht. Ihr findet sie im Repository zu dieser App. An dieser Stelle möchte ich lediglich zeigen wie die Detailansicht entsprechend der Auswahl im Master ausgetauscht wird. Dazu betrachten wir die RowSelected-Methode der entsprechenden TableViewSource.

public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
    switch (indexPath.Row)
    {
        case 0:
            var demoCtrl1 = new DemoController1();

            parentController.SplitViewController.ViewControllers = new UIViewController[]{ parentController.SplitViewController.ViewControllers[0], new UINavigationController(demoCtrl1) };
            break;
        case 1:
            var demoCtrl2 = new DemoController2();

            parentController.SplitViewController.ViewControllers = new UIViewController[]{ parentController.SplitViewController.ViewControllers[0], new UINavigationController(demoCtrl2) };
            break;
    }
}

Das Problem

Die App kann nun zu verschiedene Ebenen verschiedene Detailansichten bereitstellen. Man könnte jetzt zufrieden sein, würde nicht der Menü-Hinweis bei jedem Wechsel der Detailansicht verschwinden und erst mit einem Wechsel zur horizontalen und wieder zurück zur Portrait-Ansicht sichtbar werden.

Die Lösung

Die Lösung hat mich ein bisschen Zeit gekostet, denn laut Dokumentation ist der SplitViewControllerDelegate zum Einblenden des Menüs sowie des Hinweises verantwortlich. Das besondere ist, dass er tatsächlich nur beim Wechsel der Portrait und horizontalen Ansicht ausgelöst wird. Hinzu kommt, dass wir beim Wechsel der Detailansicht die komplette Hierarchie austauschen. Das wiederum ist notwendig, weil sich der RootController eines UINavigationControllers nicht austauschen lässt. Zu diesem Zeitpunkt verlieren wir den Hinweis.

Mit einer kleinen Modifikation im SplitViewDelegate und in der RowSelected Methode lässt sich das Problem beheben.

SplitViewDelegate erweitern

Die Implementierung des Delegates wird um eine UIBarButtonItem-Property erweitert.

public UIBarButtonItem BarButtonItem
{
    get;
    private set;
}

Die WillHideViewController-Methode wird um die Zeile BarButtonItem = barButtonItem; erweitert um die Property zu setzen.

[Export("splitViewController:willHideViewController:withBarButtonItem:forPopoverController:")]
public void WillHideViewController(UISplitViewController splitController, UIViewController viewController, UIBarButtonItem barButtonItem, UIPopoverController popoverController)
{
    ...
    BarButtonItem = barButtonItem;
}

TableViewSource anpassen

In den TableViewSourcen greifen wir auf die Property zu und fügen so den Hinweistext einfach unseren neuen Detailansichten hinzu.

public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
    var barButtonItem = (parentController.SplitViewController.Delegate as SplitViewControllerDelegate).BarButtonItem;

    switch (indexPath.Row)
    {
        case 1:
            var customerData = new CustomerDataController();

            if (barButtonItem != null)
                customerData.NavigationItem.SetLeftBarButtonItem(barButtonItem, false);

            parentController.SplitViewController.ViewControllers = new UIViewController[]{ parentController.SplitViewController.ViewControllers[0], new UINavigationController(customerData) };
            break;
        case 0:
            var userInfo = new UserInformationController();

            if (barButtonItem != null)
                userInfo.NavigationItem.SetLeftBarButtonItem(barButtonItem, false);

            parentController.SplitViewController.ViewControllers = new UIViewController[]{ parentController.SplitViewController.ViewControllers[0], new UINavigationController(userInfo) };
            break;
    }
}

Das war es auch schon. Der Code als ganzes steht auf GitHub zur Verfügung. Es ist ein funktionierendes Beispiel wie man ein UISplitViewController mit mehreren Master und Detailansichten verwenden kann.

Sebastian Seidel

Sebastian Seidel

Als Mobile-Enthusiast und Geschäftsführer der Cayas Software GmbH ist es mir ein großes Anliegen, mein Team und unsere Kunden zu unterstützen, neue potenziale zu entdecken und gemeinsam zu wachsen. Hier schreibe ich vor allem zur Entwicklung von Android und iOS-Apps mit Xamarin und .NET MAUI.

Verwandte Artikel

Erweitere deine Xamarin.iOS-App mit OpenStreetMap

Kartenfunktionen sind im mobilen Bereich ein wichtiges Feature. Kaum eine App gewinnt nicht an Wert, wenn sie Karten darstellen kann. Apple und Google machen mit ihren Map-SDKs vieles richtig, aber manchmal stößt man an Grenzen - sei es aus rechtlichen Gründen oder wegen eines Bugs. In meinem Fall war Letzteres der Auslöser. Ich brauchte außerdem eine Alternative, die auch offline funktioniert.

Ein BeeWi-Auto mit Xamarin iOS steuern

Lerne in 5 einfachen Schritten, wie Bluetooth und das External Accessory Framework zusammenspielen, um ein BeeWi-Auto mit Xamarin.iOS zu steuern.

Spracheingabe erleichtert Dokumentation - ein Hackathon-Thema
Spracheingabe erleichtert Dokumentation - ein Hackathon-Thema

Spracheingabe macht es möglich, gegessene und getrunkene Lebensmittel intuitiv zu erfassen, ohne ständig auf ein Gerät schauen oder tippen zu müssen. Statt alles mühsam per Hand einzugeben, können Nutzer Mahlzeiten und Snacks einfach per Sprache dokumentieren. Das spart Zeit und senkt die Hürde für eine regelmäßige Erfassung.