Откройте UISplitViewController для Master View, а не подробно

У меня есть интерфейс разделенного просмотра с целевым приложением iPhone 6. При первом запуске приложения оно открывается в подробном представлении; Я хотел бы, чтобы это открылось в Master View. Я пытался:

self.splitViewController?.preferredDisplayMode = UISplitViewControllerDisplayMode.PrimaryOverlay

Который был предложен в другом месте (предыдущий вопрос StackOverFlow), но, похоже, он ничего не делает и не открывает главное представление при запуске. Я также попытался добавить следующую строку в мой AppDelegate:

splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:

Но, несмотря на возврат true или false ( Другой вопрос переполнения предыдущего стека), я не добился успеха.

Я запустил пример приложения Master-Detail в XCode, и оно загружается в представление Master на основе splitViewController: call returning false; однако, я не уверен, как заставить это работать в более сложном расположении.

9 ответов

Решение

стриж

UISplitViewController отображает главный вид выше детализации в книжной ориентации не о показе основного вида, а о представлении подробного представления во всю ширину, под основным видом.

UISplitViewController в портретной ориентации на iPhone демонстрирует детализацию VC, а не мастер о принципе механизма коллапса.

Настоящий ответ адресован:

  • Мастер → Деталь(Компактная ширина)
    • iPhone 4s, 5, 5s, SE, 6, 6s, 7 (любая ориентация)
    • Ipod Touch
    • любой айфон плюс (портрет)
  • бок о бок(все остальные размеры)
    • IPad
    • любой айфон плюс (пейзаж)

Вы должны установитьpreferredDisplayMode, Вы бы хотели это.primaryVisible если бы он существовал! С помощью .allVisibleiOS выбирает Detail если подходит только 1 вид (компактная ширина); в этом размере код ниже подберет Master,

Хитрость заключается в том, чтобы изменить как preferredDisplayMode в .allVisible и вернуться true в collapseSecondary:onto,

class PrimarySplitViewController: UISplitViewController,
                                  UISplitViewControllerDelegate {

    override func viewDidLoad() {
        self.delegate = self
        self.preferredDisplayMode = .allVisible
    }

    func splitViewController(
             _ splitViewController: UISplitViewController,
             collapseSecondary secondaryViewController: UIViewController,
             onto primaryViewController: UIViewController) -> Bool {
        // Return true to prevent UIKit from applying its default behavior
        return true 
    }
}

iOS 14

Мне не перезвонили splitViewController(_:collapseSecondary:onto:) и вместо этого использовал следующий новый метод.

func splitViewController(_ svc: UISplitViewController, topColumnForCollapsingToProposedTopColumn proposedTopColumn: UISplitViewController.Column) -> UISplitViewController.Column {
    return .primary
}

Шаг 1 - Откройте MasterViewController

Шаг 2 - убедитесь, что табличное представление имеет протокол UISplitViewControllerDelegate. Например:

class ListVC: UITableViewController,UISplitViewControllerDelegate {}

Шаг 3 - Добавьте его в ViewDidLoad

splitViewController?.delegate = self

Шаг 4 - Затем переопределите этот метод, чтобы сказать, что главный контроллер вида всегда должен сворачиваться в контроллер подробного вида:

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

При первом запуске приложения оно открывается в подробном представлении; Я хотел бы, чтобы это открылось в Master View

Предполагая, что вы хотите это только при первом запуске, но не всегда; например, в случае, когда в главном представлении отображается пустой набор данных; тогда решение так же, как показывает шаблон Master-Detail:

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
}

iOS 14

Из WWDC 2020 - Build for iPad вы можете добавить определенный контроллер представления для класса компактной ширины (например, iPhone в портретной ориентации , iPad в режиме Slide Over), установив флажок « Использовать отдельный контроллер представления» в инспекторе атрибутов SplitViewController.

Таким образом, вы можете установить любой контроллер представления в качестве исходного контроллера представления, как хотите, установив переход отношений.

iOS 14 - обновления двухколоночного режима

Некоторое время я боролся с этим, прежде чем в конце концов обнаружил, что контроллер Split View был переработан в iOS14, поэтому ни один из приведенных выше ответов больше не актуален.

Я бы рекомендовал начать с этой статьи:https://www.biteinteractive.com/split-view-controllers-done-right-in-ios-14/

Но если вы ищете быстрое решение:

  • Вам нужно будет установить отношение «компактный контроллер представления» на вашем контроллере разделения представления. Вы можете сделать это, щелкнув правой кнопкой мыши контроллер разделения представления и перетащив новое отношение на контроллер представления, который вы хотите отобразить в компактном режиме.
  • В моем приложении есть TableView, и в компактном режиме я хочу нажать на контроллер подробного представления при касании ячейки. В новом контроллере SplitView iOS 14 это нужно делать вручную. Я сделал это, добавив в свой didSelectRowAt функция:
              // If we are in compact mode, we need to push the detail view controller
        if let splitViewController = splitViewController {
            if splitViewController.isCollapsed {
                let shipmentDetailViewController = storyboard?.instantiateViewController(identifier: "shipmentDetailViewController") as! ShipmentDetailViewController
                shipmentDetailViewController.shipment = selectedShipment
                self.navigationController?.pushViewController(shipmentDetailViewController, animated: true)
            }
        }

Или просто наследовать от UISplitViewController и используйте этот новый класс в раскадровке (основываясь на ответе SwiftArchitect):

class MasterShowingSplitViewController :UISplitViewController, UISplitViewControllerDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()

        self.delegate = self
        self.preferredDisplayMode = .allVisible
    }

    func splitViewController(
        _ splitViewController: UISplitViewController,
        collapseSecondary secondaryViewController: UIViewController,
        onto primaryViewController: UIViewController) -> Bool {
        // Return true to prevent UIKit from applying its default behavior
        return true
    }
}

Это старый вопрос, и ни один из ответов не был для Задачи C, и даже когда я портировал ответы Swift, ни один из них не работал для меня. Один был близко, @SwiftArchitect.

Но он рекомендовал установить режим контента на .allVisible (UISplitViewControllerDisplayModeAllVisible в Задаче C) - это заставляет главный вид постоянно отображать, разделяя вид на главный с одной стороны, детализируя с другой. Это круто, но ОП попросил специально отобразить главное представление при первом запуске, что мне и нужно было сделать.

Изменение должно было использовать UISplitViewControllerDisplayModePrimaryOverlay для режима отображения.

Этот ответ для Xcode 9.4.1, цель развертывания 11.4.

Вот MasterViewController.h - вам нужно добавить UISplitViewControllerDelegate в объявлении протоколов:

#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#import "MasterDetailDemo+CoreDataModel.h"

@class DetailViewController;

@interface MasterViewController : UITableViewController
<UISplitViewControllerDelegate,
NSFetchedResultsControllerDelegate>

@property (strong, nonatomic) DetailViewController *detailViewController;

@property (strong, nonatomic) NSFetchedResultsController<Event *> *fetchedResultsController;
@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;

@end

А затем в вашем MasterViewController.m вам нужно установить делегат контроллера разделенного представления и режим содержимого в ViewDidLoad, а затем вместе с ответом @ SwiftArchitect также добавить метод делегата контроллера разделенного представления:

- (void)viewDidLoad {

    [super viewDidLoad];

    // needed to "slide out" MasterView on startup on iPad
    self.splitViewController.delegate = self;
    self.splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModePrimaryOverlay;

    self.navigationItem.leftBarButtonItem = self.editButtonItem;

    UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)];

    self.navigationItem.rightBarButtonItem = addButton;

    self.detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
}

// split view delegate method
- (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController {
    return true;
}

ПРИМЕЧАНИЕ. После некоторого тестирования я обнаружил, что метод делегирования с разделенным представлением и протокол с разделенным представлением не были необходимы. Без этого, похоже, работает точно так же. Возможно, это результат изменений в iOS, так как вопрос был первоначально задан и получен ответ.

Я получил это работает нормально, просто поместив эту строку в мой метод ViewDidLoad:

self.splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModePrimaryOverlay;

Swift 5, iOS 13

Я нашел другие ответы полезными, но не совсем в том смысле, что они производили то поведение, которое я хотел, на iPad или iPhone, но не на обоих сразу.

Приведенное ниже решение - это то, для чего я использовал:

iPhone: главное представление всегда отображается первым

iPad Portrait: детали появляются всегда, но с наложением мастера; детализация в полноэкранном режиме (а не только справа от мастера)

Пейзаж iPad: мастер всегда слева, детали всегда справа

class RootSplitViewController: UISplitViewController {
    override func viewDidLoad() {
        if UIDevice.current.userInterfaceIdiom == .pad {
            self.preferredDisplayMode = .automatic
        }
        else {
            self.preferredDisplayMode = .allVisible
        }
        self.delegate = self
    }
}

extension RootSplitViewController: UISplitViewControllerDelegate {
    func splitViewController(_ splitViewController: UISplitViewController,
                             collapseSecondary secondaryViewController:UIViewController,
                             onto primaryViewController:UIViewController)
        -> Bool
    {
        if AppState.instance.currentSelectedEvent == nil {
            // Return true to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
            return true
        }
        else {
            return false
        }
    }
}
Другие вопросы по тегам