UISplitViewControll in Portrait trên iPhone hiển thị chi tiết VC thay vì chính


177

Tôi đang sử dụng Bảng phân cảnh chung trong Xcode 6, nhắm mục tiêu iOS 7 trở lên. Tôi đã triển khai một UISplitViewControllerứng dụng hiện được hỗ trợ trên iPhone chạy iOS 8 và Xcode sẽ tự động nhập lại cho iOS 7. Nó hoạt động rất tốt, ngoại trừ khi bạn khởi chạy ứng dụng trên iPhone chạy dọc iOS 8, chế độ xem chi tiết của chế độ xem tách Bộ điều khiển được hiển thị khi tôi dự kiến ​​lần đầu tiên nhìn thấy bộ điều khiển xem chính. Tôi tin rằng đây là một lỗi với iOS 8 vì khi bạn chạy ứng dụng trên iOS 7, nó sẽ hiển thị chính xác bộ điều khiển chế độ xem chính. Nhưng iOS 8 hiện là GM và điều này vẫn đang xảy ra. Làm cách nào tôi có thể thiết lập nó để khi bộ điều khiển xem tách được thu gọn (chỉ có một bộ điều khiển xem được hiển thị trên màn hình), khi bộ điều khiển xem tách được hiển thị, nó hiển thị bộ điều khiển xem chính không chi tiết?

Tôi đã tạo bộ điều khiển xem tách này trong Trình tạo giao diện. Bộ điều khiển xem tách là bộ điều khiển xem đầu tiên trong bộ điều khiển thanh tab. Cả VC chính và VC chi tiết đều là bộ điều khiển điều hướng với bộ điều khiển xem bảng được nhúng bên trong.

Câu trả lời:


238

Ôi trời, điều này khiến tôi đau đầu trong vài ngày và không thể tìm ra cách để làm điều này. Phần tồi tệ nhất là việc tạo một dự án Xcode iOS mới với mẫu chi tiết tổng thể hoạt động tốt. May mắn thay, cuối cùng, thực tế nhỏ đó là cách tôi tìm ra giải pháp.

Có một số bài viết tôi thấy rằng đề xuất rằng giải pháp là thực hiện primaryViewControllerForCollapsingSplitViewController:phương pháp mới trên UISplitViewControllerDelegate. Tôi đã cố gắng mà không có kết quả. Những gì Apple làm trong mẫu chi tiết tổng thể có vẻ hoạt động là triển khai phương thức mới (hít thở sâu để nói tất cả về splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:phương pháp này) (một lần nữa UISplitViewControllerDelegate). Theo các tài liệu , phương pháp này:

Yêu cầu đại biểu điều chỉnh bộ điều khiển khung nhìn chính và kết hợp bộ điều khiển khung nhìn phụ vào giao diện được thu gọn.

Hãy chắc chắn đọc lên phần thảo luận của phương pháp đó để biết thêm chi tiết cụ thể.

Cách mà Apple xử lý việc này là:

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

    }
}

Việc thực hiện này về cơ bản thực hiện như sau:

  1. Nếu secondaryViewControllerlà những gì chúng ta mong đợi (a UINavigationController) và nó hiển thị những gì chúng ta đang mong đợi (a DetailViewController- bộ điều khiển xem của bạn), nhưng không có mô hình ( detailItem), thì " Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded."
  2. Nếu không, hãy quay lại " NOđể cho trình điều khiển chế độ xem phân tách thử và kết hợp nội dung của trình điều khiển chế độ xem phụ vào giao diện bị thu gọn"

Các kết quả như sau đối với iPhone ở chế độ dọc (bắt đầu theo chiều dọc hoặc xoay sang dọc - hoặc lớp kích thước nhỏ gọn chính xác hơn):

  1. Nếu quan điểm của bạn là chính xác
    • và có một mô hình, hiển thị trình điều khiển xem chi tiết
    • nhưng không có mô hình, hiển thị trình điều khiển xem chính
  2. Nếu quan điểm của bạn không đúng
    • hiển thị trình điều khiển xem chính

Rõ như bùn.


8
Câu trả lời tuyệt vời! Tôi chỉ đơn giản là phân lớp UISplitViewControllervà luôn quay trở lại YEStừ phương thức đó, sau đó chỉ thay đổi lớp chế độ xem tách trong Storyboard, vì tôi luôn muốn hiển thị chính trên iPhone theo chiều dọc. :)
Jordan H

2
Tôi muốn ẩn bộ điều khiển chế độ xem chính của mình nếu "iPhone" ở chế độ "Chân dung" vì tôi có cài đặt bộ điều khiển xem chi tiết mặc định. Làm thế nào tôi có thể làm điều đó. Cả chủ và chi tiết của tôi đều thuộc loại VC. Cụ thể chi tiết của tôi là MMDrawerControll. Xin hãy giúp đỡ
Harshit Gupta

3
Tôi đã thử đề xuất phân lớp của Joey UISplitViewControllernhưng thấy rằng nó không hoạt động: splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:không bao giờ được gọi. Thay vào đó, tôi đã sao chép mẫu của Apple và đưa nó vào AppDelagate. Điều này cũng cần một vài thay đổi để tạo UISplitViewControll bên dưới application didFinishLaunchingWithOptions:(nơi tôi cũng đã sao chép mẫu của Apple).
Nick

7
Nhận xét của @ joey hoạt động với cài đặt self.delegate = self; trong lượt xem! Và thêm <UISplitViewControllDelegate> trong .h Thankyou!
bào thế giới

2
Đây có vẻ như là câu trả lời đúng cho tôi, vì tôi đang có cùng một vấn đề. Tuy nhiên, vì một số lý do, tôi splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:không bao giờ được gọi. Có vẻ như đại biểu đang được đặt đúng applicationDidFinishLaunchingWithOptions:phương thức của đại biểu ứng dụng của tôi . Có ai khác nhìn thấy vấn đề này và KHÔNG có giải pháp này hoạt động?
Tim Dean

60

Đây là câu trả lời được chấp nhận trong Swift. Chỉ cần tạo lớp con này và gán nó cho splitViewControll của bạn trong bảng phân cảnh của bạn.

//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
Tuyệt vời, điều này giúp rất nhiều. Nhưng một vấn đề mới nảy sinh. Nút thắt đưa tôi đến chủ bây giờ biến mất (không bao giờ hiển thị). Làm thế nào để tôi nhận được nó trở lại? EDIT: Đừng bận tâm, tự tìm ra :-). ? Đối với những người dùng khác: thêm này trong DetailView: self.navigationItem.leftBarButtonItem = self.splitViewController .displayModeButtonItem () self.navigationItem.leftItemsSupplementBackButton = true
Tom Tallak Solbu

3
Bây giờ trong Swift bất cứ điều gìfunc splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool {
Dan Rosenstark

2
Có vẻ như phương thức ủy nhiệm đó chỉ được gọi khi kích thước lớp nhỏ gọn. Nó đang được gọi trên iPhone, nhưng không phải trên chân dung iPad, điều đó có nghĩa là nó không giải quyết được vấn đề, vì chân dung iPad cũng ở chế độ thu gọn. Đã thử nghiệm với iOS 12.1
Daniel

21

Phiên bản Swift của câu trả lời đúng của Mark S

Như được cung cấp bởi mẫu Master-Chi tiết của Apple.

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
}

Làm rõ

(Những gì Mark S nói hơi khó hiểu)

Phương thức đại biểu này được gọi splitViewController: collapseSecondaryViewController: ontoPrimaryViewController:, bởi vì đó là những gì nó làm. Khi thay đổi thành kích thước chiều rộng nhỏ gọn hơn (ví dụ: khi xoay điện thoại từ ngang sang dọc), nó cần thu gọn bộ điều khiển chế độ xem tách thành một trong số chúng.

Hàm này trả về một boolean để quyết định xem nó có thu gọn Chi tiết và hiển thị Master hay không.

Vì vậy, trong trường hợp của chúng tôi, chúng tôi sẽ quyết định dựa trên việc có một chi tiết được chọn hay không. Làm thế nào để chúng ta biết nếu chi tiết của chúng tôi được chọn? Nếu chúng tôi theo mẫu Chi tiết tổng thể của Apple, bộ điều khiển xem chi tiết sẽ có một biến tùy chọn có thông tin chi tiết, vì vậy nếu không (không có), chúng tôi chưa chọn gì và chúng tôi sẽ hiển thị Master để người dùng có thể chọn thứ gì đó.

Đó là nó.


Chỉ để làm rõ lý do tại sao tôi quay lại từ chỉnh sửa của @ sschale. Mã đó là một trích dẫn từ Apple's Master-Detail template, nó không có ý định tuyệt vời hay súc tích, chỉ là thực tế. :)
NiñoScript

10

Từ tài liệu này , bạn cần sử dụng một đại biểu để nói UISplitViewController không kết hợp chế độ xem chi tiết vào "giao diện bị sập" (tức là "Chế độ chân dung" trong trường hợp của bạn). Trong Swift 4, phương thức ủy nhiệm để thực hiện cho điều đó đã được đổi tên:

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

9
   #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

9

Ứng dụng của tôi được viết bằng Swift 2.x và có thể chạy tốt. Sau khi chuyển đổi nó thành Swift 3.0 (sử dụng trình chuyển đổi XCode), nó bắt đầu hiển thị chi tiết trước thay vì chính ở chế độ dọc. Vấn đề là tên của hàm splitViewControll không được thay đổi để khớp với cái mới của UISplitViewControllDelegate.

Sau khi thay đổi tên chức năng đó theo cách thủ công, giờ đây ứng dụng của tôi có thể hoạt động chính xác:

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
}

Tôi đang gặp vấn đề tương tự như bạn, nhưng tôi không hiểu giải pháp của bạn. Tôi không thấy bất kỳ thay đổi trong mã bạn đã đăng ở đây. Bạn có thể đặc sắc hơn không. Cảm ơn
bibscy

Nhiều phương pháp được đổi tên một chút.
Dave

Câu trả lời của Tony là cú pháp Swift 3 cho câu trả lời của @ NiñoScript (được viết cho các phiên bản Swift trước đó)
Hellojeffy

2
cho swift 3, đừng quên đưa self.delegate = selfvào viewDidLoadphương thức.
Fer

7

Nếu bạn không có các giá trị mặc định để hiển thị trong trình điều khiển xem chi tiết, bạn có thể chỉ cần xóa khoảng cách mặc định giữa SplitViewContaptor và UIViewControll chi tiết của bạn trong bảng câu chuyện. Điều này sẽ làm cho nó luôn đi vào Master View Controller trước.

Tác dụng phụ của việc này là thay vì nhìn thấy hai chế độ xem theo chiều ngang, bạn sẽ thấy một chế độ xem ở kích thước đầy đủ trong SplitViewCont kiểm cho đến khi Hiển thị chi tiết phân đoạn trong trình điều khiển chế độ xem chính.


lừa tốt. Ứng dụng của tôi chỉ ở chế độ dọc và tôi có thể làm điều này.
Peacemoon

Điều này đúng ngoại trừ theo hướng Cảnh, bạn sẽ thấy phần bên phải trống của chế độ xem có thể có màu xám.
vedrano

4

Đối với tất cả những người không thể tìm thấy phần thứ sáu của cs193p:

Trong Swift 3.1.1, tạo một lớp con của UISplitViewCont kiểm soát và triển khai một trong các phương thức ủy nhiệm của nó làm việc cho tôi như một cơ duyên:

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

Bảng phân cảnh của tôi


Như @olito đã chỉ ra, trong Swift 4, cú pháp cho việc này đã thay đổi thành: public func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool
Robuske

3

Theo tôi bạn nên giải quyết vấn đề này chung chung hơn. Bạn có thể phân lớp UISplitViewCont kiểm soát và thực hiện một giao thức trong các bộ điều khiển xem được nhúng.

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
}

Ví dụ triển khai trong UITableViewCont kiểm soát:

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

Hy vọng nó giúp. Vì vậy, bạn có thể sử dụng lại lớp này và chỉ cần thực hiện một giao thức.


Phương thức đại biểu không bao giờ được gọi!
K_Mohit

Nó không được gọi trên iPad và iPhone 6/7/8 Plus. Có phải đó là vấn đề của bạn? Có một cái nhìn tại: stackoverflow.com/questions/29767614/ từ
Maik639

2

Chỉ cần xóa Chi tiếtViewContaptor khỏi bộ điều khiển SplitView khi bạn cần nó để bắt đầu từ Master.

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

Điều này làm việc cho tôi trên iOS-11 và 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

Chức năng được đổi tên trong các phiên bản mới của Swift, vì vậy mã này hoạt động trên 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

Giải pháp 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

Chỉ cần thiết lập các preferredDisplayModetài sản của UISplitViewControllerđể.allVisible

class MySplitViewController: UISplitViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.preferredDisplayMode = .allVisible
    }

}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.