iPhone에서 세로로 표시된 UISplitViewController는 마스터 대신 세부 VC를 보여줍니다.


177

iOS 7 이상을 대상으로 Xcode 6에서 Universal Storyboard를 사용하고 있습니다. UISplitViewControlleriOS 8을 실행하는 iPhone에서 기본적으로 지원되는을 구현 했으며 Xcode는 iOS 7을 위해 자동으로 백 포트합니다 .iOS 8을 실행하는 세로로 iOS에서 앱을 시작하고 분할보기의 상세보기를 제외하고는 실제로 잘 작동합니다. 마스터 뷰 컨트롤러를 처음 볼 것으로 예상되면 컨트롤러가 표시됩니다. iOS 7에서 앱을 실행할 때 마스터 뷰 컨트롤러가 올바르게 표시되기 때문에 이것이 iOS 8의 버그라고 생각했습니다. 그러나 iOS 8은 이제 GM이며 여전히 발생하고 있습니다. 분할보기 컨트롤러가 축소 될 때 (화면에 하나의보기 컨트롤러 만 표시) 분할보기 컨트롤러가 표시 될 때 마스터보기 컨트롤러가 세부 정보가 아닌 것을 표시하도록 어떻게 설정할 수 있습니까?

Interface Builder에서이 분할 뷰 컨트롤러를 만들었습니다. 분할 뷰 컨트롤러는 탭 막대 컨트롤러 내의 첫 번째 뷰 컨트롤러입니다. 마스터 및 세부 VC는 모두 테이블 뷰 컨트롤러가 내장 된 내비게이션 컨트롤러입니다.

답변:


238

오,이게 며칠 동안 두통을 일으켜서 어떻게해야할지 모르겠습니다. 최악의 부분은 master-detail 템플릿으로 새로운 Xcode iOS 프로젝트를 만드는 것이 잘 작동한다는 것입니다. 다행히도 그 작은 사실은 내가 해결책을 찾은 방법이었습니다.

해결책이에 새로운 primaryViewControllerForCollapsingSplitViewController:메소드 를 구현하는 것임을 제안하는 게시물이 있습니다 UISplitViewControllerDelegate. 나는 아무 소용이 없었습니다. Apple이 마스터-디테일 템플릿에서 작동하는 것처럼 보이는 것은 새로운 (심지어 숨을 쉬어) 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;

    }
}

이 구현은 기본적으로 다음을 수행합니다.

  1. 경우 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."
  2. 그렇지 않으면 " NO분할보기 컨트롤러가 보조보기 컨트롤러의 내용을 축소 된 인터페이스에 통합하도록하려면"

결과는 세로로 iPhone에 대해 다음과 같습니다 (세로로 시작하거나 세로로 회전 또는보다 컴팩트 한 크기의 클래스).

  1. 보기가 올바른 경우
    • 모델이 있으며, 상세 뷰 컨트롤러를 보여줍니다
    • 모델이 없지만 마스터 뷰 컨트롤러를 표시
  2. 보기가 정확하지 않은 경우
    • 마스터 뷰 컨트롤러 표시

진흙처럼 맑아


8
환상적인 답변! 나는 단순히 서브 클래스 화 UISplitViewController하고 항상 YES그 메소드에서 돌아온 다음 스토리 보드에서 분할보기 클래스를 변경했습니다. 항상 iPhone의 마스터를 세로로 표시하고 싶습니다. :)
Jordan H

2
기본 상세 뷰 컨트롤러 설정이 있기 때문에 "iPhone"이 "세로"모드 인 경우 마스터 뷰 컨트롤러를 숨기고 싶습니다. 어떻게해야합니까 내 마스터와 세부 사항은 모두 VC 유형입니다. 특히 내 세부 사항은 MMDrawerController입니다. 도와주세요
Harshit Gupta

3
나는 Joey의 서브 클래 싱 제안을 시도했지만 UISplitViewController그것이 효과가 없다는 것을 발견했다 splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:. 대신 Apple의 템플릿을 복사하여 AppDelagate에 넣었습니다. 이를 위해 UISplitViewController를 작성하는 데 약간의 변경이 필요했습니다 application didFinishLaunchingWithOptions:(Apple의 템플릿도 복사했습니다).
Nick

7
@joey의 의견은 self.delegate = self; viewdidload에서! 그리고 .h Thankyou!에 <UISplitViewControllerDelegate>를 추가하십시오!
fellowworldcitizen

2
나는 똑같은 문제가 있기 때문에 나에게 맞는 대답처럼 보입니다. 그러나 어떤 이유로 든 나는 splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:결코 부르지 않습니다. 델리게이트가 내 앱 델리게이트의 applicationDidFinishLaunchingWithOptions:방법을 올바르게 설정 한 것 같습니다 . 다른 사람 이이 문제를 보았고이 솔루션이 작동하지 않았습니까?
Tim Dean

60

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
큰 도움이됩니다. 그러나 새로운 문제가 생겼습니다. 나를 마스터로 데려가는 백 버튼이 사라집니다 (절대로 표시되지 않음). 다시 가져 오려면 어떻게해야합니까? 편집 : 신경 쓰지 마라. :-). ? 다른 사용자의 경우 : detailView이 추가 : self.navigationItem.leftBarButtonItem = self.splitViewController .displayModeButtonItem () self.navigationItem.leftItemsSupplementBackButton 사실 =
톰 Tallak Solbu

3
이제 Swift에서 무엇이든지func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool {
Dan Rosenstark

2
클래스 크기가 컴팩트 할 때만 대리자 메서드가 호출되는 것처럼 보입니다. iPhone에서는 호출되지만 iPad 인물 사진에서는 호출되지 않으므로 iPad 인물 사진도 축소 모드이므로 문제를 해결하지 못합니다. iOS 12.1에서 테스트
Daniel

21

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
}

설명

(마크 S가 말한 것은 약간 혼란 스럽다)

이 대리자 메서드는이라고 splitViewController: collapseSecondaryViewController: ontoPrimaryViewController:하는 것입니다. 더 작은 너비 크기로 변경하는 경우 (예 : 가로에서 세로로 전화를 회전 할 때) 분할보기 컨트롤러를 하나만 축소해야합니다.

이 함수는 세부 사항을 축소하고 마스터를 표시할지 여부를 결정하기 위해 부울을 리턴합니다.

우리의 경우에는 세부 사항이 선택되었는지 여부에 따라 결정됩니다. 세부 사항이 선택되었는지 어떻게 알 수 있습니까? Apple의 Master-Detail 템플릿을 따르는 경우, 상세도 컨트롤러에는 상세 정보를 갖는 선택적 변수가 있어야합니다. 따라서 nil (.None) 인 경우 아직 아무것도 선택하지 않았으며 사용자가 무언가를 선택할 수 있도록 마스터를 표시해야합니다.

그게 다야.


@sschale의 편집에서 롤백 한 이유를 분명히하기 위해. 이 코드는의 인용문으로 Apple's Master-Detail template, 위대하거나 간결한 것이 아니며 사실입니다. :)
NiñoScript 2016 년

10

로부터 문서 , 당신은 알려줄 수있는 대리자를 사용할 필요가 UISplitViewController 없습니다를 은 "붕괴 인터페이스"(즉, 귀하의 경우에는 "세로 모드")로 상세보기를 통합 할 수 있습니다. Swift 4에서 구현할 델리게이트 메소드의 이름이 다음과 같이 변경되었습니다.

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

9
   #import <UIKit/UIKit.h>

    @interface SplitProductView : UISplitViewController<UISplitViewControllerDelegate>




    @end

.미디엄:

#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

9

내 응용 프로그램은 Swift 2.x로 작성되었으며 제대로 실행될 수 있습니다. Xcode 변환기를 사용하여 Swift 3.0으로 변환 한 후에는 세로 모드에서 마스터 대신 세부 정보를 먼저 표시하기 시작합니다. 문제는 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
}

나는 당신과 같은 문제를 겪고 있지만 해결책을 이해하지 못합니다. 여기에 게시 한 코드가 변경되지 않았습니다. 좀 더 구체적으로 말씀해 주시겠습니까? 감사합니다
bibscy

많은 메소드의 이름이 약간 변경되었습니다.
Dave

Tony의 답변은 @ NiñoScript의 답변에 대한 Swift 3 구문입니다 (이전 Swift 버전 용으로 작성 됨)
Hellojeffy

2
신속한 3의 경우 방법 을 착용 self.delegate = self하는 것을 잊지 마십시오 viewDidLoad.
Fer

7

디테일 뷰 컨트롤러에 표시 할 기본값이없는 경우 스토리 보드에서 SplitViewController와 디테일 UIViewController 사이의 기본 세그를 삭제하기 만하면됩니다. 이렇게하면 항상 마스터 뷰 컨트롤러로 먼저 이동합니다.

이것의 부작용은 가로로 두 개의 뷰를 보는 대신 마스터 뷰 컨트롤러에서 Show Detail Segue가 시작될 때까지 SplitViewController에서 한 개의 뷰를 전체 크기로 볼 수 있다는 것입니다.


좋은 트릭. 내 앱이 세로 모드 일 뿐이므로이 작업을 수행 할 수 있습니다.
Peacemoon

가로 방향에서는 뷰의 오른쪽에 빈 부분이 회색으로 채워져있는 것을 제외하고는 마찬가지입니다.
vedrano

4

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

내 스토리 보드


@olito가 지적했듯이 Swift 4에서는 이에 대한 구문이 다음과 같이 변경되었습니다. public func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool
Robuske

3

내 의견으로는이 문제를보다 일반적으로 해결해야합니다. 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
    }
}

도움이 되길 바랍니다. 따라서이 클래스를 재사용 할 수 있으며 프로토콜 만 구현하면됩니다.


델리게이트 메소드는 절대 호출되지 않습니다!
K_Mohit 19:35에

iPad 및 iPhone 6/7/8 Plus에서는 호출되지 않습니다. 그게 문제 야? 보세요 : stackoverflow.com/questions/29767614/…
Maik639

2

Master에서 시작해야 할 때 SplitView 컨트롤러에서 DetailViewController를 제거하십시오.

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

이것은 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
    }
}

2

이 기능은 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
    }

}

0

Xamarin / C # 솔루션

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

preferredDisplayMode속성 UISplitViewController.allVisible

class MySplitViewController: UISplitViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.preferredDisplayMode = .allVisible
    }

}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.