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.