Изменить ширину Master в UISplitViewController

В руководстве по программированию iPad говорится, что левая панель splitView имеет фиксированное значение 320 точек. Но 320 пикселей для моего основного контроллера представления — это слишком много. Я хотел бы уменьшить его и дать больше места для контроллера подробного представления. Это вообще возможно?

Ссылка на документ, который говорит о фиксированной ширине.


person Raj Pawan Gumdal    schedule 01.06.2010    source источник
comment
Почему бы не попробовать превосходный MGSplitViewController Мэтта Геммелла? Он с открытым исходным кодом и доступен на GitHub. Грегор, Швеция   -  person Gregor    schedule 25.10.2010


Ответы (15)


Если вы создадите подкласс UISplitViewController, вы можете реализовать -viewDidLayoutSubviews и настроить там ширину. Это чисто, без хаков или частных API, и работает даже с ротацией.

- (void)viewDidLayoutSubviews
{
    const CGFloat kMasterViewWidth = 240.0;

    UIViewController *masterViewController = [self.viewControllers objectAtIndex:0];
    UIViewController *detailViewController = [self.viewControllers objectAtIndex:1];

    if (detailViewController.view.frame.origin.x > 0.0) {
        // Adjust the width of the master view
        CGRect masterViewFrame = masterViewController.view.frame;
        CGFloat deltaX = masterViewFrame.size.width - kMasterViewWidth;
        masterViewFrame.size.width -= deltaX;
        masterViewController.view.frame = masterViewFrame;

        // Adjust the width of the detail view
        CGRect detailViewFrame = detailViewController.view.frame;
        detailViewFrame.origin.x -= deltaX;
        detailViewFrame.size.width += deltaX;
        detailViewController.view.frame = detailViewFrame;

        [masterViewController.view setNeedsLayout];
        [detailViewController.view setNeedsLayout];
    }
}
person jrc    schedule 03.10.2013
comment
Приведет ли это к отклонению приложения в AppStore? - person Cutetare; 27.11.2013
comment
Я не понимаю, почему это так, поскольку частные API не используются. - person jrc; 01.12.2013
comment
Это отлично сработало для меня в пейзаже, но когда я вернулся к портрету, вид был смещен влево. Любые идеи, как это решить? - person Charlie Seligman; 14.12.2013
comment
Хороший улов. Отредактировал мой ответ. - person jrc; 15.12.2013
comment
Я могу правильно изменить размер основного представления, однако, когда detailViewController.view.frame получает новое значение, приложение просто становится черным. Любая идея? - person nigong; 07.02.2014
comment
Это должен быть принятый ответ. Смешно, что Apple заставляет вас прыгать через обручи, когда они могут легко раскрыть настройки, но я рад, что вы поделились этим. - person d512; 14.04.2014
comment
@nigong У меня та же проблема. Это каким-то образом вызывает бесконечный цикл, из которого я не могу выйти. Если я не устанавливаю detailViewController.view.frame = detailViewFrame, то masterViewController.view.frame обновляется правильно, а детали — нет. Если раскомментировать, то получится бесконечный цикл :( - person lordB8r; 25.07.2014
comment
у вас есть решение для бесконечного цикла? @AndréCytryn - person DaSilva; 30.09.2014
comment
да. на моем сабклассе я добавил: self.preferredPrimaryColumnWidthFraction = 0.3 но дело в том что у меня больше 0.4 не идет не знаю почему - person Andre Cytryn; 30.09.2014
comment
Черный экран появляется только на iOS 8. В iOS 8 этот код не нужен — используйте вместо него maximumPrimaryColumnWidth. В iOS 7 черный экран не появляется, и решение @jrc работает. - person Bill; 17.10.2014
comment
ПРЕДОТВРАЩЕНИЕ БЕСКОНЕЧНОГО ЦИКЛА: Некоторые изменения в iOS8 вызывают бесконечный цикл. Чтобы избежать этого, мы использовали приведенный выше код, но обернули его в блок if(), который проверял версию iOS. Если он меньше 8.0, используйте приведенный выше код, в противном случае просто установите: self.maximumPrimaryColumnWidth = masterViewWidth; Пожалуйста, проголосуйте за ответы @SJTriggs и Twan для этого решения. - person Kenny Wyland; 25.11.2014
comment
Бесконечного цикла, очевидно, очень легко избежать (проверьте, есть ли у вас уже нужная ширина, и измените ее только в том случае, если вы этого не сделаете), но, как указал Билл выше: используйте maximumPrimaryColumnWidth на iOS8, и если вы все еще поддерживаете iOS7, тогда следуйте решению jrc (хотя я бы все же улучшил его, проверив, нужно ли вам настраивать ширину или нет). - person Erik van der Neut; 15.04.2015
comment
Этот подход в основном работает (+1), но создает второстепенную проблему. Существует прозрачное представление, используемое для обнаружения касаний за пределами мастер-области и инициирования закрытия мастер-зоны. Если фрейм этого вида не обновляется, чтобы он совпадал с фреймом подробного вида, пользователь может взаимодействовать с деталями, пока мастер открыт. Чтобы исправить это, я сделал следующее: this.View.Subviews[2].Frame = detailFrame; сразу после установки рамки детали. - person Kent Boogaart; 26.06.2015

В IOS 8.0 вы можете легко сделать это, выполнив следующие действия:

<сильный>1. В вашем MasterSplitViewController.h добавьте

@property(nonatomic, assign) CGFloat maximumPrimaryColumnWidth NS_AVAILABLE_IOS(8_0);

<сильный>2. В вашем методе MasterSplitViewController.m viewDidLoad добавьте

 self.maximumPrimaryColumnWidth = 100;
 self.splitViewController.maximumPrimaryColumnWidth = self.maximumPrimaryColumnWidth;

Это действительно хорошая, простая и удобная функция IOS 8.

person SJTriggs    schedule 26.09.2014
comment
Может ли кто-нибудь предоставить мне Swift-эквивалент этого кода, пожалуйста? Кажется, я не могу заставить это работать в проекте Swift. - person romiem; 27.11.2014
comment
Ничего, мне удалось найти решение (хотя оно немного отличается) stackoverflow.com/questions/2949067/ - person romiem; 28.11.2014
comment
Используется с iOS 10 GM, xCode 8 и Swift 2.3. Работает как часы! :) - person JJack_; 09.09.2016

этот код работает для меня

[splitViewController setValue:[NSNumber numberWithFloat:200.0] forKey:@"_masterColumnWidth"];
person priyanka    schedule 26.10.2010
comment
Я боюсь, что в этом случае мы будем менять частные свойства. Лучше не делай этого. Кто знает, может Apple решит изменить свою иерархию классов? - person Raj Pawan Gumdal; 26.10.2010
comment
@Raj @pryiyanka, также вам гарантирован отказ в магазине приложений. - person Dan Rosenstark; 03.07.2011
comment
Да, и именно по этой причине я реализовал собственный SplitViewController, тогда splitViewController Мэтта Геймелла не существовало, но теперь вы можете использовать MGSplitViewController, это хорошо. - person Raj Pawan Gumdal; 04.07.2011
comment
В iOS7 у меня это сработало: [self.splitViewController setValue:@78 forKey:@_masterColumnWidth]; - person kmiklas; 10.04.2014
comment
Работал еще под iOS 9, теперь под iOS 10 вылетает! - person AppsolutEinfach; 23.09.2016
comment
Он выйдет из строя для iOS10 - person Himanshu jamnani; 12.10.2016

No.


Есть два частных свойства.

@property(access,nonatomic) CGFloat masterColumnWidth;
@property(access,nonatomic) CGFloat leftColumnWidth; // both are the same!

но то, что они частные, означает, что их нельзя использовать для приложений AppStore.

person kennytm    schedule 01.06.2010
comment
Спасибо, но я не собираюсь использовать приватные API, скорее я бы реализовал собственный контроллер разделенного представления. . . - person Raj Pawan Gumdal; 01.06.2010
comment
@Raj, как я могу создать подкласс UISplitViewController и переопределить только masterColumnWidth? - person Kyle Clegg; 18.12.2012
comment
@Kyle - Невозможно использовать описанный выше подход, если вы хотите отправить приложение в магазин приложений. В противном случае, если вы все еще настаиваете на его использовании, вам не нужно создавать подклассы. См. этот ответ ниже: stackoverflow.com/a/4022406/260665 - person Raj Pawan Gumdal; 19.12.2012
comment
@Raj - мне нужно отправить в App Store. Есть ли способ использовать предложенный вами ответ способом, который примет Apple? Я предполагаю, что ответ - подкласс, или это тоже плохо? - person Kyle Clegg; 20.12.2012
comment
Нет-нет, я меняю принятый ответ ради потомков. Вы не можете использовать этот подход или даже подкласс и изменить ширину, если хотите отправить в магазин приложений. От него точно откажут. Используйте контроллер разделенного представления Мэтта Геммелла. Смотрите мой новый принятый ответ. - person Raj Pawan Gumdal; 20.12.2012

iOS 8 представила новое свойство:

// An animatable property that can be used to adjust the maximum absolute width of the primary view controller in the split view controller.
@property(nonatomic, assign) CGFloat maximumPrimaryColumnWidth NS_AVAILABLE_IOS(8_0); // default: UISplitViewControllerAutomaticDimension

Используйте это свойство, чтобы настроить главный контроллер представления на желаемую ширину.

person Antoine    schedule 21.09.2014
comment
Поскольку существует много разных размеров экрана, вам лучше использовать предпочитаемыйPrimaryColumnWidthFraction - person malhal; 03.01.2016

Вот как я сделал это в iOS8 со Swift.

class MainSplitViewController: UISplitViewController {

    override func viewDidLoad() {

        self.preferredDisplayMode = UISplitViewControllerDisplayMode.AllVisible
        self.maximumPrimaryColumnWidth = 100 // specify your width here
    }
}

Если вам нужно динамически изменить ширину из основного/детального представления в разделенном представлении, сделайте что-то вроде этого:

var splitViewController = self.splitViewController as MainSplitViewController
splitViewController.maximumPrimaryColumnWidth = 400
person romiem    schedule 28.11.2014
comment
добавьте splitViewController.maximumPrimaryColumnWidth = ### в ваш appdelegate.swift - person Jason G; 29.12.2014

Ни один из ответов не сработал для меня на iOS7, поэтому я провел собственное исследование и создал рабочее решение. Это потребует создания подкласса UISplitViewController для полной функциональности.

Я представлю ответ так, как если бы мы только что создали новый проект для iPad со всеми ориентациями устройства и установили пользовательский UISplitViewController в качестве основного контроллера представления.

Создайте свой UISplitViewController. В этом примере мой называется MySplitViewController. Весь код будет основан на MySplitViewController.m.

Нам понадобится доступ к методу из UISplitViewControllerDelegate, поэтому добавьте его и установите делегат. Мы также настроим пересылку делегатов на тот случай, если вам понадобится вызывать методы делегатов из другого класса.

@interface MySplitViewController () <UISplitViewControllerDelegate>
@property (nonatomic, weak) id<UISplitViewControllerDelegate> realDelegate;
@end

@implementation MySplitViewController

- (instancetype)init {
    self = [super init];
    if (self) {
        self.delegate = self;
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        self.delegate = self;
    }
    return self;
}

- (void)setDelegate:(id<UISplitViewControllerDelegate>)delegate {
    [super setDelegate:nil];
    self.realDelegate = (delegate != self) ? delegate : nil;
    [super setDelegate:delegate ? self : nil];
}

- (BOOL)respondsToSelector:(SEL)aSelector {
    id delegate = self.realDelegate;
    return [super respondsToSelector:aSelector] || [delegate respondsToSelector:aSelector];
}

- (id)forwardingTargetForSelector:(SEL)aSelector {
    id delegate = self.realDelegate;
    return [delegate respondsToSelector:aSelector] ? delegate : [super forwardingTargetForSelector:aSelector];
}

Настройте контроллеры главного и подробного видов.

- (void)viewDidLoad {
    [super viewDidLoad];

    UIViewController* masterViewController = [[UIViewController alloc] init];
    masterViewController.view.backgroundColor = [UIColor yellowColor];

    UIViewController* detailViewController = [[UIViewController alloc] init];
    detailViewController.view.backgroundColor = [UIColor cyanColor];

    self.viewControllers = @[masterViewController, detailViewController];
}

Давайте добавим желаемую ширину к методу для удобства.

- (CGFloat)desiredWidth {
    return 200.0f;
}

Мы будем манипулировать основным контроллером представления перед его представлением.

- (void)splitViewController:(UISplitViewController *)svc popoverController:(UIPopoverController *)pc willPresentViewController:(UIViewController *)aViewController {
    id realDelegate = self.realDelegate;

    if ([realDelegate respondsToSelector:@selector(splitViewController:popoverController:willPresentViewController:)]) {
        [realDelegate splitViewController:svc popoverController:pc willPresentViewController:aViewController];
    }

    CGRect rect = aViewController.view.frame;
    rect.size.width = [self desiredWidth];
    aViewController.view.frame = rect;

    aViewController.view.superview.clipsToBounds = NO;
}

Однако теперь у нас остался вот такой дисплей. введите здесь описание изображения

Так что собирались переопределить частный метод. Да, частный метод, он по-прежнему будет приемлем в App Store, поскольку это не частный метод подчеркивания.

- (CGFloat)leftColumnWidth {
    return [self desiredWidth];
}

Это касается портретного режима. Итак, аналогичная вещь для -splitViewController:willShowViewController:invalidatingBarButtonItem:, и вы должны установить альбомную ориентацию.

Однако ничего из этого не понадобится в iOS8. Вы сможете просто вызвать свойства минимальной и максимальной ширины!

person cnotethegr8    schedule 28.08.2014

Способ раскадровки будет таким, упомянутым @Tim:

введите описание изображения здесь

Кроме того, если вы хотите, чтобы основное представление всегда занимало определенный процент экрана, вы можете вместо этого использовать Key Path = "preferredPrimaryColumnWidthFraction" и установить значение 0,2 (для размера экрана 20%).

Обратите внимание, что для параметра «maximumPrimaryColumnWidth» установлено значение 320, поэтому, если вы попробуете процентное значение экрана, равное 0,5 (50%), оно не превысит 320. Вы можете добавить ключевой путь для maximaryColumnWidth, если вам нужно переопределить это.

person Travis M.    schedule 29.11.2016

используйте следующий код перед назначением rootviewcontroller. У меня работает с ios7

[self.splitViewController setValue:[NSNumber numberWithFloat:256.0] forKey:@"_masterColumnWidth"]; self.window.rootViewController = self.splitViewController;

person xevser    schedule 25.11.2014

Поскольку никто не упомянул, что это можно сделать из IB, я хочу добавить этот ответ. По-видимому, вы можете установить «Определяемые пользователем атрибуты времени выполнения» для UISplitViewContorller со следующими данными: Путь к ключу: masterColumnWidth Тип: числовое значение: 250

person Tim    schedule 18.07.2015

В моем случае мне пришлось установить как максимум, так и минимум, чтобы это сработало.

mySplitViewController.preferredDisplayMode = .allVisible;
mySplitViewController.maximumPrimaryColumnWidth = UIScreen.main.bounds.width/2;
mySplitViewController.minimumPrimaryColumnWidth = UIScreen.main.bounds.width/2; 
person grep    schedule 12.06.2017

Вы можете использовать GSSplitViewController. Этот будет работать на iOS 7 и 8

splitView = [[GSSplitViewController alloc] init];
splitView.masterPaneWidth = 180;

Вы также можете включить его, добавив pod 'GSSplitViewController' к Podfile.

person o.akrout    schedule 17.06.2015

ViewController.h @property(nonatomic, assign) CGFloat maximumPrimaryColumnWidth NS_AVAILABLE_IOS(8_0);

ViewController.m

#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)

    if (SYSTEM_VERSION_LESS_THAN(@"10.0")) {

    [self setValue:[NSNumber numberWithFloat:200.0]forKey:@"_masterColumnWidth"];

    }else{

    self.maximumPrimaryColumnWidth = 200;
    self.splitViewController.maximumPrimaryColumnWidth = self.maximumPrimaryColumnWidth;

     }
person Himanshu jamnani    schedule 12.10.2016

Swift 3.0 вы используете как

let widthfraction = 2.0 //Your desired value for me 2.0    
splitViewController?.preferredPrimaryColumnWidthFraction = 0.40
let minimumWidth = min((splitViewController?.view.bounds.size.width)!,(splitViewController?.view.bounds.height)!)
splitViewController?.minimumPrimaryColumnWidth = minimumWidth / widthFraction
splitViewController?.maximumPrimaryColumnWidth = minimumWidth / widthFraction
let leftNavController = splitViewController?.viewControllers.first as! UINavigationController
leftNavController.view.frame = CGRect(x: leftNavController.view.frame.origin.x, y: leftNavController.view.frame.origin.y, width: (minimumWidth / widthFraction), height: leftNavController.view.frame.height)
person Madasamy    schedule 15.03.2017

Этот код работает для меня :)

@interface UISplitViewController(myExt) 
- (void)setNewMasterSize:(float)size; 
@end

@implementation UISplitViewController(myExt) - (void)setNewMasterSize:(float)size { _masterColumnWidth = size; } @end

и используйте его для каждой операции с видом (например, вращение)

person Kames    schedule 24.08.2010
comment
Я знаю, что приватные можно изменить с помощью таких категорий, но это второй раз, когда я пытаюсь это сделать и получаю ту же ошибку: _OBJC_IVAR_$_UISplitViewController._masterColumnWidth, на которую ссылается: _OBJC_IVAR_$_UISplitViewController._masterColumnWidth$non_lazy_ptr в UISplitViewController+myExt.o (возможно, вы имели в виду: _OBJC_IVAR_$_UISplitViewController._masterColumnWidth$non_lazy_ptr) Символы не найдены. Collect2: ld вернул 1 статус выхода - person nacho4d; 26.08.2010