UISplitViewController в портрете на iPhone показывает детали VC вместо мастера

Я использую универсальную раскадровку в Xcode 6, ориентированную на iOS 7 и выше. Я реализовал UISplitViewController, который теперь поддерживается на iPhone под управлением iOS 8, и Xcode автоматически будет загружать его для iOS 7. Он работает очень хорошо, за исключением случаев, когда вы запускаете приложение на iPhone в портрете с iOS 8, разделение view detail view controller отображается, когда я ожидаю, что вы сначала увидите контроллер главного представления. Я считал, что это ошибка с iOS 8, потому что когда вы запускаете приложение на iOS 7, он правильно показывает контроллер главного представления. Но iOS 8 теперь GM, и это все еще происходит. Как я могу настроить его таким образом, что когда контроллер разделенного представления будет свернут (только один контроллер отображения отображается на экране), когда отображается диспетчер разделенного представления, он показывает контроллер главного представления не деталь?

Я создал этот контроллер разделенного вида в Interface Builder. Контроллер разделенного вида - это первый контроллер представления в контроллере панели вкладок. Как главный, так и подробный VCs являются навигационными контроллерами с встроенными встроенными контроллерами табличного представления.

Ответ 1

О, человек, это вызывало у меня головную боль в течение нескольких дней и не могло понять, как это сделать. Хуже всего то, что создание нового проекта iOS Xcode с шаблоном master-detail работало отлично. К счастью, в конце концов, этот маленький факт был тем, как я нашел решение.

Есть несколько сообщений, которые я нашел, которые предполагают, что решение заключается в реализации нового метода primaryViewControllerForCollapsingSplitViewController: на UISplitViewControllerDelegate. Я пробовал это безрезультатно. Что Apple делает в шаблоне master-detail, который, кажется, работает, это реализовать новый (сделать глубокий вдох, чтобы сказать все это) splitViewController:collapseSecondaryViewController:ontoPrimaryViewController: метод делегата (снова на UISplitViewControllerDelegate). Согласно docs, этот метод:

Попросит делегата отрегулировать основной контроллер представления и включить контроллер вторичного представления в свернутый интерфейс.

Обязательно прочитайте часть обсуждения этого метода для получения более подробной информации.

Способ, которым Apple управляет этим:

- (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;

    }
}

Эта реализация в основном делает следующее:

  • Если secondaryViewController - это то, что мы ожидаем (a UINavigationController), и оно показывает, что мы ожидаем (a DetailViewController - ваш контроллер представления), но не имеет модели (detailItem), затем "Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded."
  • В противном случае верните "NO, чтобы позволить контроллеру разделенного представления попытаться включить содержимое содержимого контроллеров вторичного представления в свернутый интерфейс"

Ниже приведены результаты для iPhone в портрете (начиная с портретного или вращающегося до портретного или более компактного класса):

  • Если ваше мнение верное
    • и имеет модель, показать контроллер подробного представления
    • но не имеет модели, покажите контроллер главного представления
  • Если ваше мнение неверно
    • показать главный контроллер представления

Очистить как ил.

Ответ 2

Вот признанный ответ в Swift. Просто создайте этот подкласс и назначьте его вашему splitViewController в своем раскадровке.

//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
  }

}

Ответ 3

Верный вариант правильного ответа Mark S

Как предусмотрено шаблоном Apple 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
}

Разъяснение

(То, что сказал Марк С, несколько сбивает с толку)

Этот метод делегата называется splitViewController: collapseSecondaryViewController: ontoPrimaryViewController:, потому что это то, что он делает. При переходе на более компактный размер (например, при повороте телефона от пейзажа к портрету) ему необходимо свернуть контроллер разделенного представления только на один из них.

Эта функция возвращает логическое значение, чтобы решить, должно ли оно свернуть деталь и показать мастер или нет.

Итак, в нашем случае мы будем решать, основываясь на том, была ли выбрана деталь или нет. Как узнать, выбрана ли наша деталь? Если мы следуем шаблону Apple Master-Detail, контроллер подробного представления должен иметь необязательную переменную, имеющую подробную информацию, поэтому, если это ниль (.None), пока ничего не выбрано, и мы должны показать Мастер, чтобы пользователь мог что-то выбрать.

Что это.

Ответ 4

Мое приложение было написано в Swift 2.x и могло работать хорошо. После преобразования в Swift 3.0 (с использованием конвертера XCode) он начинает сначала показывать детали, а не мастер в портретном режиме. Проблема заключается в том, что имя функции splitViewController не изменяется в соответствии с новой версией UISplitViewControllerDelegate.

После изменения имени этой функции мое приложение теперь может работать корректно:

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
}

Ответ 5

   #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

Ответ 6

Из documentation вам нужно использовать делегат, чтобы сообщить UISplitViewController не включать подробный вид в "свернутый интерфейс" (т.е. "Портретный режим" в вашем случае). В Swift 4 метод делегирования для этого был переименован:

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

Ответ 7

Если у вас нет значений по умолчанию для показа в контроллере подробного представления, вы можете просто удалить сегмент по умолчанию между SplitViewController и вашим подробным UIViewController в доске объявлений. Это приведет к тому, что он всегда переходит в Master View Controller.

Побочным эффектом этого является то, что вместо того, чтобы видеть два вида в ландшафте, вы увидите одно представление в полном размере в SplitViewController до тех пор, пока Show Detail Segue в главном контроллере не будет запущен.

Ответ 8

По-моему, вы должны решить эту проблему более общий. Вы можете подклассифицировать UISplitViewController и реализовать протокол во встроенных контроллерах представления.

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
}

Пример реализации в UITableViewController:

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

Надеюсь, это поможет. Таким образом, вы можете повторно использовать этот класс и просто нужно реализовать протокол.

Ответ 9

Для всех людей, которые не смогли найти раздел cs193p в пятницу:

В Swift 3.1.1 создание подкласса UISplitViewController и реализация одного из его методов делегирования работали для меня как шарм:

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
} }

Моя раскадровка

Ответ 10

Просто удалите DetailViewController из контроллеров SplitView, когда вам нужно, чтобы начать с мастера.

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;
}

Ответ 11

Это работало для меня на iOS-11 и 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
    }
}

Ответ 12

Функция переименована в новых версиях Swift, поэтому этот код работает на 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
    }

}

Ответ 13

Решение Xamarin/С#

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;
    }
}

Ответ 14

Просто установите для свойства UISplitViewController preferredDisplayMode свойствоDisplayMode UISplitViewController .allVisible

class MySplitViewController: UISplitViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.preferredDisplayMode = .allVisible
    }

}