İPhone'da portredeki UISplitViewController, ana yerine ayrıntı VC'sini gösterir


177

Xcode 6'da iOS 7 ve sonraki sürümleri hedefleyen bir Universal Storyboard kullanıyorum. UISplitViewControllerŞu anda iOS 8 çalıştıran iPhone'da yerel olarak desteklenen bir uygulama uyguladım ve Xcode, iOS 7 için otomatik olarak destekleyecek. Uygulamayı iPhone'da iOS 8 çalıştıran portre ile başlatmanız dışında, bölünmüş görünümün ayrıntı görünümü Ana görünüm kontrol cihazını ilk görmeyi beklediğimde kontrolör görüntülenir. Bunun iOS 8'de bir hata olduğuna inandım çünkü uygulamayı iOS 7'de çalıştırdığınızda ana görünüm denetleyicisini doğru bir şekilde gösteriyor. Ancak iOS 8 artık GM ve bu hala gerçekleşiyor. Bölünmüş görünüm denetleyicisi daraltılacaksa (ekranda yalnızca bir görünüm denetleyicisi), bölünmüş görünüm denetleyicisi görüntülendiğinde ana görünüm denetleyicisinin ayrıntıyı göstermeyeceği şekilde nasıl ayarlayabilirim?

Bu bölünmüş görünüm denetleyicisini Interface Builder'da oluşturdum. Bölünmüş görünüm denetleyicisi, sekme çubuğu denetleyicisindeki ilk görünüm denetleyicisidir. Hem ana hem de ayrıntılı VC'ler, içine gömülü tablo görünümü denetleyicilerine sahip navigasyon denetleyicileridir.

Yanıtlar:


238

Ah adamım, bu bana birkaç gün boyunca başım ağrıyor ve bunu nasıl yapacağımı anlayamıyordum. En kötü yanı, ana ayrıntı şablonuyla yeni bir Xcode iOS projesi oluşturmanın gayet iyi çalışmasıydı. Neyse ki, sonunda, çözümü bulabildim.

Çözümün yeni primaryViewControllerForCollapsingSplitViewController:yöntemi uygulamak olduğunu önerdiğim bazı yayınlar var UISplitViewControllerDelegate. Bunu boşuna denedim. Apple'ın işe yarar görünen ana detay şablonunda yaptığı şey, yeni (tüm bunları söylemek için derin bir nefes alın) splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:delege yöntemini (tekrar açık UISplitViewControllerDelegate) uygulamaktır. Dokümanlara göre , bu yöntem:

Temsilciden birincil görünüm denetleyicisini ayarlamasını ve ikincil görünüm denetleyicisini daraltılmış arabirime dahil etmesini ister.

Daha ayrıntılı bilgi için bu yöntemin tartışma bölümünü okuduğunuzdan emin olun.

Apple'ın bunu işleme şekli:

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

    }
}

Bu uygulama temel olarak aşağıdakileri yapar:

  1. Eğer secondaryViewControllerbeklediğimiz buysa (a UINavigationController) ve beklediğimizi gösteriyorsa (a DetailViewController- görünüm denetleyiciniz), ancak hiçbir modeli ( detailItem) yoksa o zaman " Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded."
  2. Aksi takdirde, " NObölünmüş görünüm denetleyicisinin ikincil görünüm denetleyicisinin içeriğini daraltılmış arabirime dahil etmesine izin vermek için geri dön "

Sonuçlar dikey olarak iPhone için aşağıdaki gibidir (dikey olarak başlayarak veya dikey olarak döndürerek - veya daha doğru bir şekilde kompakt boyut sınıfında):

  1. Görüşünüz doğruysa
    • ve bir modeli var, detay görünümü denetleyicisini göster
    • ancak modeli yok, ana görünüm denetleyicisini göster
  2. Görüşünüz doğru değilse
    • ana görünüm denetleyicisini göster

Çamur kadar temiz.


8
Harika bir cevap! Ben sadece alt sınıfta UISplitViewControllerve her zaman YESbu yöntemden dönün , sonra her zaman iPhone'da ustayı dikey olarak göstermek istediğim için Storyboard'daki bölünmüş görünüm sınıfını değiştirdim. :)
Jordan H

2
Varsayılan ayrıntı görünümü denetleyicisi kurulumum olduğundan "iPhone" "Portre" modundaysa ana görünüm denetleyicimin gizlenmesini istiyorum. Bunu nasıl yapabilirim. Efendim ve detayım her ikisi de VC tipindedir. Özellikle benim detayım MMDrawerController. Lütfen yardım
Harshit Gupta

3
Joey'nin alt sınıf önerisini denedim UISplitViewControllerama işe yaramadığını gördüm : splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:asla çağrılmadı. Bunun yerine Apple'ın şablonunu kopyaladım ve AppDelagate'e koydum. Bu, altında application didFinishLaunchingWithOptions:da Apple şablonunu kopyaladığım UISplitViewController'ı oluşturmak için birkaç değişiklik yapılmasını gerektiriyordu .
Nick

7
@ joey'nin yorumu self.delegate = self; viewdidload'da! Ve .h Teşekkür ederim <UISplitViewControllerDelegate>!
fellowworldcitizen

2
Aynı problemi yaşadığım için bu benim için doğru cevap gibi görünüyor. Ancak, nedense splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:asla çağrılmam. Temsilcinin uygulama temsilcimin applicationDidFinishLaunchingWithOptions:yöntemini doğru şekilde ayarladığı anlaşılıyor . Başka kimse bu sorunu gördü ve bu çözüm işe yaramadı mı?
Tim Dean

60

İşte Swift'te kabul edilen cevap. Bu alt sınıfı oluşturun ve film şeridinizdeki splitViewController'ınıza atayı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
Harika, bu çok yardımcı oluyor. Fakat yeni bir problem ortaya çıktı. Beni ustaya götüren backbton artık yok oluyor (asla göstermiyor). Nasıl geri alabilirim? EDIT: Boş ver, kendimi düşündüm :-). ? Diğer kullanıcıları için: DetailView bu ekleyin: self.navigationItem.leftBarButtonItem = self.splitViewController .displayModeButtonItem () self.navigationItem.leftItemsSupplementBackButton = true
Tom Tallak Solbu

3
Şimdi Swift'te her ne isefunc splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool {
Dan Rosenstark

2
Delege yönteminin yalnızca sınıf boyutu küçükken çağrıldığı anlaşılıyor. İPhone'da çağrılıyor, ancak iPad portresinde değil, iPad portresi de daraltılmış modda olduğu için sorunu çözmediği anlamına geliyor. İOS 12.1
Daniel

21

Mark S'ın hızlı cevabının hızlı versiyonu

Apple'ın Master-Detail şablonu tarafından sağlandığı gibi.

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
}

açıklama

(Mark S'nin söyledikleri biraz kafa karıştırıcıydı)

Bu delege yöntemi denir splitViewController: collapseSecondaryViewController: ontoPrimaryViewController:, çünkü yaptığı budur. Daha kompakt bir genişlik boyutuna geçerken (örneğin telefonu yataydan dikey konuma döndürürken), bölünmüş görünüm denetleyicisini bunlardan sadece birine daraltmanız gerekir.

Bu işlev, Ayrıntı'yı daraltıp Ana'yı gösterip göstermeyeceğine karar vermek için bir boole döndürür.

Bizim durumumuzda, seçilen bir ayrıntı olup olmadığına göre karar vereceğiz. Detayımızın seçilip seçilmediğini nasıl bilebiliriz? Apple'ın Master-Detail şablonunu takip edersek, ayrıntı görünümü denetleyicisinin ayrıntı bilgilerine sahip isteğe bağlı bir değişkeni olmalıdır, bu nedenle nil (.

Bu kadar.


Sadece @ sschale'ın düzenlemesinden neden geri döndüğümü açıklığa kavuşturmak için. Bu kod bir alıntıdır Apple's Master-Detail template, büyük ya da özlü olması amaçlanmamıştır, sadece olgusaldır. :)
NiñoScript

10

Gönderen belgeler , Anlatacak bir temsilci kullanmaya gerek UISplitViewController yok "çökmüş bir arayüz" (yani sizin durumda "Dikey mod") içine detay görünümü dahil etmek. Swift 4'te, bunun için uygulanacak temsilci yöntemi yeniden adlandırıldı:

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

Uygulamam Swift 2.x ile yazılmış ve iyi çalışabilir. Swift 3.0'a (XCode dönüştürücü kullanarak) dönüştürdükten sonra, portre modunda master yerine önce detay göstermeye başlar. Sorun splitViewController işlevinin adı yeni UISplitViewControllerDelegate ile eşleşecek şekilde değiştirilmemiştir.

Bu işlevin adını el ile değiştirdikten sonra, uygulamam artık doğru şekilde çalışabilir:

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
}

Seninle aynı problemi yaşıyorum ama çözümünü anlamıyorum. Burada gönderdiğiniz kodda herhangi bir değişiklik görmüyorum. Daha spesifik olabilir misiniz? Teşekkürler
bibscy

Birçok yöntem biraz yeniden adlandırılmıştır.
Dave

Tony'nin cevabı @ NiñoScript'in cevabına Swift 3 sözdizimidir (önceki Swift sürümleri için yazılmıştır)
Hellojeffy

2
Swift 3, koymak unutmayın self.delegate = selfüzerinde viewDidLoadyöntemle.
Fer

7

Ayrıntı görünümü denetleyicisinde gösterilecek varsayılan değerleriniz yoksa, SplitViewController ile hikaye panosundaki ayrıntı UIViewController arasındaki varsayılan segmenti silebilirsiniz. Bu, her zaman önce Ana Görünüm Denetleyicisine girmesini sağlayacaktır.

Bunun yan etkisi, manzarada iki görünüm görmek yerine, ana görünüm denetleyicisinde Ayrıntı Segueini Göster açılana kadar SplitViewController'da tam boyutta bir görünüm görmenizdir.


iyi numara. Uygulamam sadece portre modunda ve bunu yapabilirim.
Peacemoon

Bu, Yatay yönlendirme dışında, görünümün muhtemelen sağ tarafının gri dolu olduğunu göreceğiniz dışında doğrudur.
vedrano

4

Cs193p'nin cuma bölümünü bulamayan tüm insanlar için:

Swift 3.1.1'de bir UISplitViewController alt sınıfı oluşturmak ve temsilci yöntemlerinden birini uygulamak benim için bir cazibe gibi çalıştı:

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

Görsel senaryom


@Olito'nun belirttiği gibi, Swift 4'te bunun sözdizimi şu şekilde değişti: public func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool
Robuske

3

Bence bu sorunu daha genel bir şekilde çözmelisiniz. UISplitViewController alt sınıfını oluşturabilir ve katıştırılmış görünüm denetleyicilerine bir protokol uygulayabilirsiniz.

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'da örnek uygulama:

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

Umarım yardımcı olur. Yani bu sınıfı yeniden kullanabilirsiniz ve sadece bir protokol uygulamak gerekir.


Delege yöntemi asla çağrılmaz!
K_Mohit

iPad ve iPhone 6/7/8 Plus'da çağrılmaz. Bu senin sorunun mu? Şuna bir göz atın: stackoverflow.com/questions/29767614/…
Maik639

2

Master'dan başlatmanız gerektiğinde DetailViewController'ı SplitView denetleyicilerinden kaldırmanız yeterlidir.

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

Bu benim için iOS-11 ve Swift 4'te çalıştı:

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

İşlev, Swift'in yeni sürümlerinde yeniden adlandırılmıştır, bu nedenle bu kod Swift 4'te çalışır:

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 # Çözümü

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

Sadece set preferredDisplayModeözelliği UISplitViewControllerile.allVisible

class MySplitViewController: UISplitViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.preferredDisplayMode = .allVisible
    }

}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.