Использование объекта XIB внутри другого XIB
Я проектирую, используя IB конкретную кнопку (с некоторыми метками, imageView и другими вещами).
Я хотел бы затем использовать этот объект XIB (кнопка) в другом XIB, где у меня есть 6 объектов этой кнопки.
Я знаю, что могу сделать это программно, используя Class Identifier, но затем я должен расположить свои метки и вид изображения, а также установить значения по умолчанию и все остальное в коде.
Мне интересно, возможно ли сделать то же самое, используя только IB? (конечно, я все равно установил бы значения в коде, но я хочу, чтобы позиции lables/imageView и все остальные были установлены в xib)
Спасибо
редактировать
Итак, я понимаю, что я просил сначала не представляется возможным. То, что я пытаюсь сейчас, таково:
Я создал ButtonTest.xib
, Внутри Xib у меня есть UIView и 3 подпредставления (2 ярлыка и кнопка). В инспекторе я установил класс UIView ButtonTest
, Внутри ButtonTest.m у меня есть 3 выхода для каждого из подпредставлений, которые я подключаю в IB.
Далее у меня есть ButtonTestViewController.xib
, Внутри него я поместил один вид и установил его класс в инспекторе, чтобы ButtonTest
, Я связываю эту точку зрения с myTextView
розетка внутри ButtonTestViewController.m
класса ButtonTest
Теперь это код внутри ButtonTestViewController.m viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"ButtonTest" owner:nil options:nil];
ButtonTest *mainView = (ButtonTest*)[subviewArray objectAtIndex:0];
self.myTextView = mainView;
}
Я надеялся, что это произойдет, так как представление в ButtonTestViewController.xib станет представлением, которое я разработал в ButtonTest.xib. Это просто не происходит. происходит то, что представление внутри ButtonTestViewController.xib остается прежним.
Стоит упомянуть, что если я добавлю:
[self.view addSubview:mainView];
Это добавляет новый вид, помимо существующего.
Есть идеи, как делать то, что я хочу?
В конце концов я хотел бы иметь 6 просмотров в ButtonTestViewController.xib
, все будет выглядеть как шаблон ButtonTest.xib, а значения lables будут установлены в коде.
Edit2
Хорошо, ребята, я сделал все, что вы сказали, и это сработало как шарм. Единственная проблема, с которой я сейчас сталкиваюсь, это когда ButtonTestViewController.xib
немного больше, чем вид в ButtonTest.xib
, Когда это происходит, ярлык на кнопке выглядит очень размытым. Когда они одинакового размера, все хорошо.
@ Нед - я использовал точный код, который вы разместили в моем методе InitWithCoder, за исключением того, что я переключил предложение кадра на это:
self.bounds = mainView.frame;
Я пытался поиграть с режимом контента в IB, пытаясь это исправить, но безрезультатно.
Когда я использую его, как вы опубликовали, это означает:
mainView.frame = self.bounds;
Это не работает вообще, и размеры обоих видов остаются неизменными. Здесь я попытался поиграть с resizeMask, но все равно не получилось.
И идея, как исправить эти 2 проблемы? Спасибо, парни!
Редактировать 3
Мне удалось решить одну из проблем, изменив размеры ButtonTestViewController.xib
к ButtonTest.xib
посмотреть размер. Это то, что я сделал (используя код для решения размытой проблемы, взятый отсюда)
self.bounds = CGRectMake(0, 0, mainView.frame.size.width, mainView.frame.size.height);
CGRect overlay2Frame = self.frame;
overlay2Frame.origin.x = round(overlay2Frame.origin.x);
overlay2Frame.origin.y = round(overlay2Frame.origin.y);
self.frame = overlay2Frame;
Другая проблема, которую я до сих пор не могу решить. представление в ButtonTest.xib просто не станет больше, чтобы соответствовать другому представлению.
4 ответа
Вы можете сделать это, и довольно легко. Для этого я создаю подкласс UIView (скажем, он называется "MyView.m" и "MyView.h"), а затем перо "MyView.xib".
Во-первых, в кончике щелкните "Владелец файла" и установите для класса "MyView". Это сделает так, чтобы IBOutlets/Actions из вашего класса появлялись в IB. Добавьте одно представление верхнего уровня (если оно еще не было) в качестве основного представления и добавьте другие пользовательские элементы (UILabels и UIImageViews) в качестве подпредставлений основного UIView.
Затем добавьте следующий код, чтобы он вызывался при инициализации MyView (помните, что если вы инициализируете из пера, он будет инициализирован через - (id)initWithCoder:(NSCoder *)aDecoder).
NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
UIView *mainView = [subviewArray objectAtIndex:0];
//Just in case the size is different (you may or may not want this)
mainView.frame = self.bounds;
[self addSubview:mainView];
Это загружает иерархию представления из вашего пера в NSArray, и, поскольку у вас был только один UIView верхнего уровня, вы просто добавляете это как подпредставление вашего пользовательского UIView.
Это позволяет вам проектировать UIViews (и другие подклассы UIView) в Интерфейсном Разработчике.
РЕДАКТИРОВАТЬ: Обновлено для редактирования OP.
Я всегда так делал, следуя моим указаниям выше. Одно из отличий состоит в том, что в вашем перте ButtonTest вы меняете класс UIView на ButtonTest, но я изменяю класс владельца файла на ButtonTest. Тогда внутри ButtonTest.m
сделать loadNibNamed
вещи. Теперь, чтобы добавить этот объект к ButtonTestViewController.xib
добавьте UIView (или UIButton, независимо от подкласса ButtonTest) и измените класс на ButtonTest.
Это немного сбивает с толку, поэтому я постараюсь разбить его по файлам.
ButtonTestViewController.xib
: Добавьте UIButton (независимо от наследования ButtonTest) и измените класс на ButtonTest
ButtonTest.m
: Внутри метода "initWithCoder" сделайте все "loadNibNamed", что у меня есть выше
ButtonTest.xib
: Измените класс владельца файла на ButtonTest и свяжите все IBOutlets и IBActions
Некоторые из вас спрашивают в комментариях, не создает ли это решение бесконечный цикл, и у меня недостаточно репутации, чтобы отвечать там, поэтому вот мой ответ:
Цикл существует, если для корневого представления в вашей XIB "Пользовательский класс" установлен на MyView. Это вызывает ситуацию, когда представление, загруженное из XIB, снова вызывает инициализатор MyView и, следовательно, создает бесконечный цикл. Решение состоит в том, что "Пользовательский класс" должен быть UIView, и только класс Владельца файла должен быть установлен в MyView (чтобы иметь возможность назначать IBOutlets и IBActions).
Swift 3.0
создать файл swift с классом
class ViewXIBfileClassView: UIView {
.....
}
создать файл Xib с представлением:ViewXIBfileClassView
создать класс представления оболочки
class ViewWrapper: UIView {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let view = UINib(nibName: "ViewXIBfileClassView_file", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! viewXIBfileClassView
self.addSubview(view)
view.bindEdgesToSuperview()
}
}
добавить расширение к UIView
extension UIView {
/// Adds constraints to the superview so that this view has same size and position.
/// Note: This fails the build if the `superview` is `nil` – add it as a subview before calling this.
func bindEdgesToSuperview() {
guard let superview = superview else {
preconditionFailure("`superview` was nil – call `addSubview(view: UIView)` before calling `bindEdgesToSuperview()` to fix this.")
}
translatesAutoresizingMaskIntoConstraints = false
["H:|-0-[subview]-0-|", "V:|-0-[subview]-0-|"].forEach { visualFormat in
superview.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: visualFormat, options: .directionLeadingToTrailing, metrics: nil, views: ["subview": self]))
}
}
}
Используйте UIView с ViewWrapper внутри всех файлов Xib, которые вы хотите
Если ваш класс, например, является подклассом UIButton, вы можете открыть библиотеку в Интерфейсном Разработчике, и она будет находиться на вкладке классов при ее поиске, и вы сможете перетаскивать ее.
Вы также можете просто перетащить UIB-кнопку и установить ее класс на панели инспектора.
Однако, потому что у вас есть свой собственный.xib, который может испортить его, в этом случае просто игнорируйте меня.