web-dev-qa-db-ger.com

Der UISplitViewController im Hochformat auf dem iPhone zeigt Details VC anstelle von Meister

Ich verwende ein Universal Storyboard in Xcode 6, das auf iOS 7 und höher abzielt. Ich habe eine UISplitViewController implementiert, die jetzt nativ auf dem iPhone mit iOS 8 unterstützt wird, und Xcode wird automatisch für iOS 7 zurückportiert. Es funktioniert wirklich gut, es sei denn, Sie starten die App auf dem iPhone mit iOS 8, dem Detail der geteilten Ansicht View Controller wird angezeigt, wenn erwartet wurde, dass der Master-View-Controller zum ersten Mal angezeigt wird. Ich habe geglaubt, dass dies ein Fehler mit iOS 8 war, da der Master View Controller beim Ausführen der App unter iOS 7 korrekt angezeigt wird. Aber iOS 8 ist jetzt GM und dies tritt immer noch auf. Wie kann ich es so einrichten, dass der Split-View-Controller (wenn nur ein View-Controller auf dem Bildschirm angezeigt wird) ausgeblendet wird und der Split-View-Controller angezeigt wird, wenn er angezeigt wird, zeigt er nicht den Details des Master-View-Controllers an.

Ich habe diesen Split-View-Controller im Interface Builder erstellt. Der Split-View-Controller ist der erste View-Controller innerhalb eines Registerkarten-Controllers. Sowohl der Master- als auch der Detail-VC sind Navigationscontroller mit eingebetteten Table View-Controllern.

164
Jordan H

Oh Mann, das hat mir einige Tage lang Kopfschmerzen bereitet und ich konnte nicht herausfinden, wie ich das mache. Das Schlimmste war, dass das Erstellen eines neuen Xcode-iOS-Projekts mit der Master-Detail-Vorlage gut funktioniert hat. Glücklicherweise war diese kleine Tatsache letztendlich die Art, wie ich die Lösung fand.

Es gibt einige Beiträge, von denen ich fand, dass die Lösung darin besteht, die neue primaryViewControllerForCollapsingSplitViewController:-Methode für UISplitViewControllerDelegate zu implementieren. Ich habe das versucht, ohne Erfolg. Was Apple in der Master-Detail-Vorlage tut, die scheinbar funktioniert, ist die Implementierung der neuen Methode (atmen Sie tief durch, um alles zu sagen): splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:-Delegatmethode (wieder bei UISplitViewControllerDelegate). Gemäß docs gilt diese Methode:

Fordert den Delegaten auf, den primären View-Controller anzupassen und den sekundären View-Controller in die reduzierte Schnittstelle zu integrieren.

Lesen Sie den Diskussionsteil dieser Methode, um genauere Informationen zu erhalten.

Die Art, wie Apple damit umgeht, ist:

- (BOOL)splitViewController:(UISplitViewController *)splitViewController
collapseSecondaryViewController:(UIViewController *)secondaryViewController
  ontoPrimaryViewController:(UIViewController *)primaryViewController {

    if ([secondaryViewController isKindOfClass:[UINavigationController class]]
        && [[(UINavigationController *)secondaryViewController topViewController] isKindOfClass:[DetailViewController class]]
        && ([(DetailViewController *)[(UINavigationController *)secondaryViewController topViewController] detailItem] == nil)) {

        // Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
        return YES;

    } else {

        return NO;

    }
}

Diese Implementierung führt im Wesentlichen Folgendes aus:

  1. Wenn secondaryViewController das ist, was wir erwarten (eine UINavigationController), und es zeigt, was wir erwarten (eine DetailViewController - Ihr View-Controller), aber kein Modell (detailItem) hat, dann "Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded."
  2. Andernfalls geben Sie "NO" zurück, damit der Split-View-Controller den Inhalt des sekundären View-Controllers in die reduzierte Schnittstelle integrieren kann.

Für das iPhone im Hochformat ergeben sich folgende Ergebnisse (entweder vom Hochformat aus oder bis zum Hochformat drehen - oder genauer in der kompakten Größenklasse):

  1. Wenn Ihre Ansicht richtig ist
    • und hat ein Modell, zeigen die Detailansicht Controller
    • hat aber kein Modell, zeigen Sie den Master-View-Controller
  2. Wenn Ihre Ansicht nicht korrekt ist
    • zeigt den Master-View-Controller

Klar wie Schlamm.

226
Mark S

Hier ist die akzeptierte Antwort in Swift. Erstellen Sie einfach diese Unterklasse und weisen Sie sie Ihrem splitViewController in Ihrem Storyboard zu.

//GlobalSplitViewController.Swift

import UIKit

class GlobalSplitViewController: UISplitViewController, UISplitViewControllerDelegate {

  override func viewDidLoad() {
    super.viewDidLoad()

    self.delegate = self
  }

  func splitViewController(splitViewController: UISplitViewController, collapseSecondaryViewController secondaryViewController: UIViewController!, ontoPrimaryViewController primaryViewController: UIViewController!) -> Bool{
    return true
  }

}
58
Clifton Labrum

Schnelle Version von Mark S 'richtige Antwort

Wie von der Master-Detail-Vorlage von Apple bereitgestellt.

func splitViewController(splitViewController: UISplitViewController, collapseSecondaryViewController secondaryViewController:UIViewController, ontoPrimaryViewController primaryViewController:UIViewController) -> Bool {
    guard let secondaryAsNavController = secondaryViewController as? UINavigationController else { return false }
    guard let topAsDetailController = secondaryAsNavController.topViewController as? DetailViewController else { return false }
    if topAsDetailController.detailItem == nil {
        // Return true to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
        return true
    }
    return false
}

Klärung

(Was Mark S sagte, war etwas verwirrend)

Diese Delegatmethode heißt splitViewController: collapseSecondaryViewController: ontoPrimaryViewController:, weil dies der Fall ist. Wenn Sie auf eine kompaktere Breite (z. B. beim Drehen des Telefons von Querformat auf Hochformat) umstellen, muss der Split-View-Controller nur in einen von ihnen reduziert werden.

Diese Funktion gibt einen Booleschen Wert zurück, um zu entscheiden, ob das Detail minimiert und der Master angezeigt werden soll oder nicht.

In unserem Fall entscheiden wir uns also, ob ein Detail ausgewählt wurde oder nicht. Woher wissen wir, ob unser Detail ausgewählt wurde? Wenn wir der Master-Detail-Vorlage von Apple folgen, sollte der Detailansicht-Controller eine optionale Variable mit den Detailinformationen enthalten. Wenn also null (.None) gilt, ist noch nichts ausgewählt, und wir sollten den Master anzeigen, damit der Benutzer etwas auswählen kann.

Das ist es.

19
NiñoScript

Meine App wurde in Swift 2.x geschrieben und konnte gut laufen. Nach der Konvertierung in Swift 3.0 (mithilfe des XCode-Konverters) werden im Hochformat zuerst Details anstelle von Master angezeigt. Das Problem ist, dass der Name der Funktion splitViewController nicht geändert wurde, um mit der neuen von UISplitViewControllerDelegate übereinzustimmen.

Nachdem Sie den Namen dieser Funktion manuell geändert haben, kann meine App jetzt ordnungsgemäß funktionieren:

func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool {
    guard let secondaryAsNavController = secondaryViewController as? UINavigationController else { return false }
    guard let topAsDetailController = secondaryAsNavController.topViewController as? DetailViewController else { return false }
    if topAsDetailController.game == nil {
        // Return true to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
        return true
    }
    return false
}
9
Tony

In der documentation müssen Sie einen Delegaten verwenden, um der UISplitViewControllernot mitzuteilen, dass die Detailansicht in die "reduzierte Benutzeroberfläche" (d. H. In Ihrem Fall der "Portrait-Modus") integriert wird. In Swift 4 wurde die zu implementierende Delegat-Methode umbenannt:

func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool {
    return true
}
8
olito
   #import <UIKit/UIKit.h>

    @interface SplitProductView : UISplitViewController<UISplitViewControllerDelegate>




    @end

.m:

#import "SplitProductView.h"
#import "PriceDetailTableView.h"

@interface SplitProductView ()

@end

@implementation SplitProductView

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.delegate = self;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/
- (BOOL)splitViewController:(UISplitViewController *)splitViewController
collapseSecondaryViewController:(UIViewController *)secondaryViewController
  ontoPrimaryViewController:(UIViewController *)primaryViewController {

    if ([secondaryViewController isKindOfClass:[UINavigationController class]]
        && [[(UINavigationController *)secondaryViewController topViewController] isKindOfClass:[PriceDetailTableView class]]

        //&& ([(PriceDetailTableView *)[(UINavigationController *)secondaryViewController topViewController] detailItem] == nil)

        ) {

        // Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
        return YES;

    } else {

        return NO;

    }
}
@end
8
Gank

Wenn Sie nicht über Standardwerte verfügen, die im Detailansicht-Controller angezeigt werden sollen, können Sie einfach das Standardsegment zwischen dem SplitViewController und Ihrem detaillierten UIViewController im Storyboard löschen. Dies führt dazu, dass der Master View Controller immer zuerst in den Master View-Controller eingefügt wird.

Der Nebeneffekt ist, dass in SplitViewController nicht zwei Ansichten im Querformat angezeigt werden, bis "Detailansicht anzeigen" im Master-Ansichts-Controller ausgelöst wird.

7
Hao-Cher Hong

Für alle Leute, die den Freitag-Abschnitt von cs193p nicht finden konnten: 

In Swift 3.1.1 hat das Erstellen einer Unterklasse von UISplitViewController und das Implementieren einer seiner Delegat-Methoden für mich wie ein Zauber funktioniert: 

class MainSplitViewController: UISplitViewController, UISplitViewControllerDelegate {

override func viewDidLoad() {
    super.viewDidLoad()
    self.delegate = self
}

func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool {
    return true
} }

Mein Storyboard

3
Bartosz Kunat

Entfernen Sie einfach DetailViewController von den SplitView-Controllern, wenn Sie es vom Master aus starten möchten.

UISplitViewController *splitViewController = (UISplitViewController *)[self.storyboard instantiateViewControllerWithIdentifier:@"SETTINGS"];
splitViewController.delegate = self;
[self.navigationController presentViewController:splitViewController animated:YES completion:nil];
if (IPHONE) {
    NSMutableArray * cntlrs = [splitViewController.viewControllers mutableCopy];
    [cntlrs removeLastObject];
    splitViewController.viewControllers = cntlrs;
}
2

Dies funktionierte für mich auf iOS-11 und Swift 4:

//Following code in application didFinishLaunching (inside Application Delegate)
guard let splitViewController = window?.rootViewController as? UISplitViewController,
            let masterNavVC = splitViewController.viewControllers.first as? UINavigationController,
            let masterVC = masterNavVC.topViewController as? MasterViewController
else { fatalError() }
splitViewController.delegate = masterVC

//Following code in MasterViewController class
extension MasterViewController:UISplitViewControllerDelegate {
    func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool {
        return true
    }
}
2
Vishal Chaudhry

Meiner Meinung nach sollten Sie dieses Problem allgemeiner lösen. Sie können den UISplitViewController subclassieren und ein Protokoll in den Embedded View-Controllern implementieren.

class MasterShowingSplitViewController: UISplitViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        delegate = self
    }
}

extension MasterShowingSplitViewController: UISplitViewControllerDelegate {
    func splitViewController(splitViewController: UISplitViewController,
                             collapseSecondaryViewController secondaryViewController: UIViewController,
                             ontoPrimaryViewController primaryViewController: UIViewController) -> Bool {
        guard let masterNavigationController = primaryViewController as? UINavigationController,
                  master = masterNavigationController.topViewController as? SplitViewControllerCollapseProtocol else {
            return true
        }
        return master.shouldShowMasterOnCollapse()

    }
}

protocol SplitViewControllerCollapseProtocol {
    func shouldShowMasterOnCollapse() -> Bool
}

Beispielimplementierung in UITableViewController:

extension SettingsTableViewController: SplitViewControllerCollapseProtocol {
    func shouldShowMasterOnCollapse() -> Bool {
        return tableView.indexPathForSelectedRow == nil
    }
}

Ich hoffe, es hilft. So können Sie diese Klasse wiederverwenden und müssen nur ein Protokoll implementieren.

2
Maik639

Setzen Sie einfach die Eigenschaft preferredDisplayMode von UISplitViewController auf .allVisible

class MySplitViewController: UISplitViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.preferredDisplayMode = .allVisible
    }

}
0
Arash Etemad

Xamarin/C # -Lösung

public partial class MainSplitViewController : UISplitViewController
{
    public MainSplitViewController(IntPtr handle) : base(handle)
    {
    }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();

        Delegate = new MainSplitViewControllerDelegate();
    }
}

public class MainSplitViewControllerDelegate : UISplitViewControllerDelegate
{
    public override bool CollapseSecondViewController(UISplitViewController splitViewController, UIViewController secondaryViewController, UIViewController primaryViewController)
    {
        return true;
    }
}
0
Mark Moeykens

Die Funktion wurde in neue Versionen von Swift umbenannt, daher funktioniert dieser Code in Swift 4:

import UIKit

class GlobalSplitViewController: UISplitViewController, UISplitViewControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        self.delegate = self
    }

    func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool {
        return true
    }

}
0
Pink Panther