Использовать автозапуск для установки динамического UIView для соответствия представлению контейнера
У меня есть UIView в IB, в котором есть еще один UIView, который я использую в виде контейнера. В коде я создаю три разных представления, а затем анимируем соответствующий вид контейнера в зависимости от состояния приложения. Только одно из трех разных видов будет действительным в любой момент времени. Проблема в том, что когда я запускаю на другом iPhone в симуляторе, мои новые объекты не масштабируются в соответствии с представлением контейнера. Я использую автозапуск. Для целей тестирования я настроил свои подзаголовки просто как большую кнопку, у которой все ее края ограничены супервидом. И в представлении контейнера также есть края, ограниченные этим супервидом. Я хочу, чтобы subview соответствовал представлению контейнера. т.е. кнопка растягивает весь размер представления контейнера. При запуске на разных iPhones размер представления контейнера и, следовательно, subview должен масштабироваться пропорционально различным размерам экрана iPhone.
Ниже приведен код, который я использую для инициализации моего подсмотра и установки его ограничений относительно представления контейнера.
UIView *containerView = self.subView;
UIView *newSubview = [[mySubview alloc] init];
[newSubview setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.containerView addSubview:newSubview];
[self.containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]];
[self.containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0]];
[self.containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0]];
[self.containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.containerView attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0]];
Я просто не могу заставить это работать. Я новичок в автозапуске и не уверен, что я делаю неправильно, и хотел бы перестать ударить головой об эту стену. Любая помощь будет потрясающей.:)
************* ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ **************
Извините, я не заявлял о своей проблеме так четко, как мог. Итак, вот еще несколько снимков экрана. Во-первых, я опишу, что я сделал по коду.
В didFinishLaunchingWithOptions в AppDelegate.m я создаю MyViewController, как это,
self.myViewController = [[MyViewController alloc] initWithNibName:@"MyViewController" bundle:nil];
В viewDidLoad в MyViewController.m я создаю mySubview и добавляю его в свой контейнерный вид и создаю для него такие ограничения,
UIView *containerView = self.containerView;
UIView *mySubview = [[MySubview alloc] init];
[mySubview setTranslatesAutoresizingMaskIntoConstraints:NO];
[containerView addSubview:mySubview];
NSDictionary *views = NSDictionaryOfVariableBindings(mySubview);
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[mySubview]|"
options:0
metrics:nil
views:views]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[mySubview]|"
options:0
metrics:nil
views:views]];
И, наконец, в init в MySubview.h я добавляю nib как подзаголовок, подобный этому,
- (id)init
{
if(self = [super init])
{
NSArray *nibArray = [[NSBundle mainBundle]loadNibNamed:@"MySubview" owner:self options:nil];
[self addSubview:[nibArray objectAtIndex:0]];
}
return self;
}
Несколько замечаний, которые могут помочь,
В MyViewController.xib у меня есть UIView, который я использую в качестве контейнера. Он имеет IBOutlet для UIView * containerView и является ссылкой, указанной выше. Единственные ограничения, которые у меня есть для containerView в IB, - это ведущее, конечное и нижнее пространство Superview и верхнее пространство для Superview = 44.
Для MySubview.xib высота и ширина 300, (ограничения не используются для высоты или ширины). Я чувствую, что эти размеры не должны иметь значения, поскольку mySubview должен ограничиваться контейнером.
В MySubview.xib у меня есть 3 объекта, topButton: height = 29, middleView: height = 242 и bottomButton: height = 29. (см. прикрепленное изображение) TopButton имеет ведущие, конечные и верхние ограничения Superview, а нижнее - среднее ограничение. Средство MiddleView имеет ведущие и завершающие ограничения Superview и ограничение topButton и ограничения нижнего уровня к нижнему баттону. И, наконец, bottomButton имеет ведущие, конечные и нижние ограничения Superview и ограничение top to middleView.
Я хочу, чтобы mySubview масштабировался, чтобы соответствовать контейнеру, поскольку ограничения были созданы и добавлены, но вместо этого mySubview получает очень большой и клип containerView.
Вот несколько снимков экрана:
MyViewController.xib, синий прямоугольник под заголовком - мой контейнерный вид и имеет контейнер контейнера IBOutlet.
![http://i33.tinypic.com/14o3lmd.png]()
MySubview.xib
![http://i37.tinypic.com/344ukk3.png]()
И, наконец, результат, который неверен.
![http://i34.tinypic.com/nbyqua.png]()
Вместо этого я хотел бы этого, что я подделал, чтобы получить снимок экрана.
На iPhone 4,
![http://tinypic.com/r/w9fp5e/4]()
На iPhone 5,
![http://tinypic.com/r/2z8nldv/4]()
Как вы можете видеть в фальшивых снимках экрана, mySubview масштабируется, чтобы соответствовать контейнеру, даже если контейнерView немного масштабируется для настройки различных размеров экрана телефона.
Надеюсь, я не перешел за борт с информацией. Любая помощь будет потрясающей. Я чувствую, что я рядом, но должен упустить один ключевой шаг. Grrrrr.
Ответы
Ответ 1
Несколько мыслей:
-
Это в основном выглядит отлично, за исключением некоторой странности переменных: ваша локальная переменная containerView
равна self.subView
, но все ваши другие строки относятся к другой переменной, свойству класса, self.containerView
. Вы опустили строку, в которой вы установили свойство класса containerView
? Но когда я исправил это, ваш код работал отлично для меня.
-
Убедитесь, что вы не пытаетесь взглянуть на frame
сразу после установки ограничений, так как изменения еще не будут отображаться в настройках frame
. Вы можете сделать [containerView layoutIfNeeded];
, если вы хотите заставить его передать все, основываясь на ограничениях. Кроме того, если вы хотите подтвердить настройки frame
, лучше отложить просмотр этих значений до тех пор, пока viewDidAppear
(т.е. viewDidLoad
не будет слишком рано в процессе построения представления).
-
Небольшая настройка вашего кода (и не связанная с вашей проблемой), но когда я устанавливаю ограничения в представлении контейнера, я часто устанавливаю не только NSLayoutAttributeTop
и NSLayoutAttributeLeading
, как вы это делали, но также NSLayoutAttributeBottom
и NSLayoutAttributeTrailing
(а не NSLayoutAttributeWidth
и NSLayoutAttributeHeight
). Он выполняет то же самое, что и ваш код, но при использовании ненулевых значений это еще более интуитивно понятная часть.
Во всяком случае, я просто сделал следующий код, перестановку вашего, и он отлично работает:
- (IBAction)didTouchUpInsideAddView:(id)sender
{
UIView *containerView = self.containerView;
UIView *newSubview = [[UIView alloc] initWithFrame:CGRectZero]; // initializing with CGRectZero so we can see it change
newSubview.translatesAutoresizingMaskIntoConstraints = NO;
newSubview.backgroundColor = [UIColor lightGrayColor];
[containerView addSubview:newSubview];
[containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0.0]];
[containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0.0]];
[containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0]];
[containerView addConstraint:[NSLayoutConstraint constraintWithItem:newSubview
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:0.0]];
// the frame is still `CGRectZero` at this point
NSLog(@"newSubview.frame before = %@", NSStringFromCGRect(newSubview.frame));
// if not doing this in `viewDidLoad` (e.g. you're doing this in
// `viewDidAppear` or later), you can force `layoutIfNeeded` if you want
// to look at `frame` values. Generally you don't need to do this unless
// manually inspecting `frame` values or when changing constraints in a
// `animations` block of `animateWithDuration`.
[containerView layoutIfNeeded];
// everything is ok here
NSLog(@"containerView.bounds after = %@", NSStringFromCGRect(containerView.bounds));
NSLog(@"newSubview.frame after = %@", NSStringFromCGRect(newSubview.frame));
}
-
Вы можете немного упростить этот код, используя язык визуальных форматов, например:
NSDictionary *views = NSDictionaryOfVariableBindings(newSubview);
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[newSubview]|"
options:0
metrics:nil
views:views]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[newSubview]|"
options:0
metrics:nil
views:views]];
Мне легче получить ограничения с использованием языка визуального формата. Немного меньше подвержено ошибкам (для меня, по крайней мере). Есть, однако, некоторые ограничения, которые не могут быть представлены на языке визуального формата, и в этом случае я возвращаюсь к синтаксису, который вы начертите.
-
В вашем пересмотренном вопросе вы показываете нам метод init
для вашего подсмотра, который выполняет другой addSubview
. Вы также должны установить ограничения. В нижней строке, где бы вы ни делали addSubview
, вы должны установить свои ограничения, например.
- (id)init
{
if(self = [super init])
{
NSArray *nibArray = [[NSBundle mainBundle]loadNibNamed:@"MySubview" owner:self options:nil];
UIView *subview = [nibArray objectAtIndex:0];
subview.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:subview];
NSDictionary *views = NSDictionaryOfVariableBindings(subview);
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[subview]|"
options:0
metrics:nil
views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[subview]|"
options:0
metrics:nil
views:views]];
}
return self;
}
Ответ 2
В этой проблеме вы указываете, что "мои новые объекты не масштабируются в соответствии с представлением контейнера", но из описания, которое, как я думаю, они есть, проблема заключается в том, что в вашем представлении контейнера нет фиксированного размера.
Я думаю, что если вы установите для своего вида контейнера фиксированную ширину и высоту, которые должны это сделать, вам также может потребоваться позвонить
[self.containerView layoutSubviews]
чтобы принудительно изменить размер после обновления ограничений.
Я также предлагаю вам обмен текстовым форматированием, вы можете поменять свои строки выше на что-то вроде "H: | [newSubview] |" и "V: | [newSubview] |"