Как создать пользовательский класс представления iOS и создать несколько экземпляров его (в IB)?
В настоящее время я создаю приложение, которое будет иметь несколько таймеров, которые в основном все одинаковы.
Я хочу создать пользовательский класс, который использует весь код для таймеров, а также макет/анимацию, поэтому я могу иметь 5 одинаковых таймеров, которые работают независимо друг от друга.
Я создал макет с использованием IB (xcode 4.2), и весь код для таймеров в настоящее время находится только в классе viewcontroller.
У меня возникли трудности с переносом моего мозга вокруг того, как инкапсулировать все в пользовательский класс, а затем добавить его в viewcontroller, любая помощь будет высоко оценена.
Ответы
Ответ 1
Чтобы ответить концептуально, ваш таймер должен скорее быть подклассом UIView
вместо NSObject
.
Чтобы создать экземпляр вашего таймера в IB, просто перетащите его UIView
на его представление контроллера просмотра и установите его классом в имя своего таймера.
Помните #import
свой класс таймера в вашем контроллере представления.
Изменить: для дизайна IB (для создания экземпляра кода см. историю изменений)
Я не очень хорошо разбираюсь в раскадровке, но я знаю, что вы можете построить свой интерфейс в IB, используя файл .xib
, который почти идентичен использованию версии раскадровки; Вы даже сможете копировать и вставлять свои представления в целом из существующего интерфейса в файл .xib
.
Чтобы проверить это, я создал новый пустой .xib
с именем "MyCustomTimerView.xib". Затем я добавил представление, и к нему добавлена метка и две кнопки. Как и так:
Я создал новый класс objective-C подкласса UIView
с именем "MyCustomTimer". В моем .xib
я установил класс File Owner MyCustomTimer. Теперь я могу подключать действия и выходы, как и любой другой вид/контроллер. Полученный файл .h
выглядит следующим образом:
@interface MyCustomTimer : UIView
@property (strong, nonatomic) IBOutlet UILabel *displayLabel;
@property (strong, nonatomic) IBOutlet UIButton *startButton;
@property (strong, nonatomic) IBOutlet UIButton *stopButton;
- (IBAction)startButtonPush:(id)sender;
- (IBAction)stopButtonPush:(id)sender;
@end
Единственное препятствие для перехода - получить этот .xib
в моем подклассе UIView
. Использование .xib
резко сокращает требуемую настройку. И поскольку вы используете раскадровку для загрузки таймеров, мы знаем, что -(id)initWithCoder:
- единственный инициализатор, который будет вызываться. Итак, вот как выглядит файл реализации:
#import "MyCustomTimer.h"
@implementation MyCustomTimer
@synthesize displayLabel;
@synthesize startButton;
@synthesize stopButton;
-(id)initWithCoder:(NSCoder *)aDecoder{
if ((self = [super initWithCoder:aDecoder])){
[self addSubview:
[[[NSBundle mainBundle] loadNibNamed:@"MyCustomTimerView"
owner:self
options:nil] objectAtIndex:0]];
}
return self;
}
- (IBAction)startButtonPush:(id)sender {
self.displayLabel.backgroundColor = [UIColor greenColor];
}
- (IBAction)stopButtonPush:(id)sender {
self.displayLabel.backgroundColor = [UIColor redColor];
}
@end
Метод с именем loadNibNamed:owner:options:
делает именно то, что он звучит так, как будто он. Он загружает Nib и устанавливает свойство "File Owner" для себя. Мы извлекаем первый объект в массиве и являемся корневым представлением Nib. Мы добавляем представление как подвью и Voila на экран.
Очевидно, что это просто меняет цвет фона метки при нажатии кнопок, но этот пример должен хорошо вам помочь.
Примечания, основанные на комментариях:
Стоит отметить, что если вы получаете бесконечные проблемы с рекурсией, вы, вероятно, пропустили тонкий трюк этого решения. Это не делает то, что вы думаете, что это делает. Представление, которое помещается в раскадровку, не видно, но вместо этого загружает другое представление в качестве подвью. Это представление, которое он загружает, представляет собой представление, которое определено в nib. "Владелец файла" в банке - это невидимое представление. Классная часть состоит в том, что это невидимое представление все еще является классом objective-C, который может использоваться как контроллер вида для вида, который он вносит из ниба. Например, методы IBAction
в классе MyCustomTimer
- это то, чего вы ожидаете больше в контроллере представления, чем в представлении.
В качестве побочного примечания некоторые могут утверждать, что это нарушает MVC
, и я согласен несколько. С моей точки зрения, он более тесно связан с пользовательским UITableViewCell
, который также иногда должен быть контроллером части.
Также стоит отметить, что этот ответ должен был дать очень конкретное решение; создайте один наконечник, который может быть создан несколько раз на том же изображении, что и на раскадровке. Например, вы можете легко представить шесть из этих таймеров на экране iPad за один раз. Если вам нужно только указать представление для контроллера вида, который будет использоваться несколько раз в вашем приложении, тогда решение, предоставленное jyavenard для этого вопроса, почти наверняка будет лучше решение для вас.
Ответ 2
Пример Swift
Обновлено для Xcode 10 и Swift 4
Вот базовая прогулка. Я изначально узнал об этом, посмотрев эту серию видео Youtube. Позже я обновил свой ответ на основе этой статьи.
Добавить пользовательские файлы просмотра
Следующие два файла формируют ваш пользовательский вид:
- .xib файл для размещения макета
- .swift как подкласс
UIView
Подробности для их добавления приведены ниже.
Файл Xib
Добавьте в проект файл.xib (Файл> Создать> Файл...> Пользовательский интерфейс> Вид). Я вызываю мой ReusableCustomView.xib
.
Создайте макет, который требуется для вашего пользовательского представления. В качестве примера я сделаю макет с UILabel
и UIButton
. Рекомендуется использовать автоматическую компоновку, чтобы вещи автоматически изменялись независимо от того, какой размер вы задали позже. (Я использовал Freeform для размера xib в инспекторе атрибутов, чтобы я мог настраивать моделируемые показатели, но это необязательно.)
Быстрый файл
Добавьте в проект файл.swift (Файл> Создать> Файл...> Источник> Файл Swift). Это подкласс UIView
и я UIView
ReusableCustomView.swift
.
import UIKit
class ResuableCustomView: UIView {
}
Сделать файл Swift владельцем
Вернитесь в ваш .xib файл и нажмите "Владелец файла" в документе. В Identity Inspector напишите имя вашего .swift файла как имя пользовательского класса.
Добавить пользовательский код просмотра
Замените содержимое файла ReusableCustomView.swift
следующим кодом:
import UIKit
@IBDesignable
class ResuableCustomView: UIView {
let nibName = "ReusableCustomView"
var contentView:UIView?
@IBOutlet weak var label: UILabel!
@IBAction func buttonTap(_ sender: UIButton) {
label.text = "Hi"
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
func commonInit() {
guard let view = loadViewFromNib() else { return }
view.frame = self.bounds
self.addSubview(view)
contentView = view
}
func loadViewFromNib() -> UIView? {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: nibName, bundle: bundle)
return nib.instantiate(withOwner: self, options: nil).first as? UIView
}
}
Обязательно получите правописание для имени вашего.xib файла.
Подключить выходные и действия
Подключите выходы и действия, перетащив управление с метки и кнопки в маке xib на быстрый код пользовательского вида.
Использование пользовательского вида
Теперь ваш пользовательский вид завершен. Все, что вам нужно сделать, это добавить UIView
везде, где вы хотите, в свою основную раскадровку. Задайте имя класса вида ReusableCustomView
в Identity Inspector.
Ответ 3
Ответ для диспетчеров просмотров, не просмотры:
Существует более простой способ загрузить xib из раскадровки.
Скажем, ваш контроллер имеет тип MyClassController, который наследует от UIViewController
.
Вы добавляете UIViewController
с помощью IB в свою раскадровку; измените тип класса на MyClassController. Удалите представление, которое было автоматически добавлено в раскадровку.
Убедитесь, что XIB, который вы хотите вызвать, называется MyClassController.xib.
Когда класс будет создан при загрузке раскадровки, xib будет автоматически загружен.
Причина этого связана с реализацией по умолчанию UIViewController
, которая вызывает XIB с именем класса.
Ответ 4
На самом деле это не ответ, но я думаю, что полезно использовать этот подход.
Objective-C
Swift
- Импортируйте CustomViewWithXib.swift в свой проект
- Создайте пользовательские файлы с тем же именем (.swift и .xib)
- Наследовать свой пользовательский класс из CustomViewWiспасибоib
Дополнительно:
- Перейдите к вашему xib файлу, задайте владельцу имя вашего класса, если вы
необходимо подключить некоторые элементы (подробнее см. в разделе Make
файл Swift - владелец ответа @Suragch)
Все, теперь вы можете добавить свой собственный вид в свою раскадровку, и это будет показано:)
CustomViewWiспасибоib.h:
#import <UIKit/UIKit.h>
/**
* All classes inherit from CustomViewWithXib should have the same xib file name and class name (.h and .m)
MyCustomView.h
MyCustomView.m
MyCustomView.xib
*/
// This allows seeing how your custom views will appear without building and running your app after each change.
IB_DESIGNABLE
@interface CustomViewWithXib : UIView
@end
CustomViewWiспасибоib.m:
#import "CustomViewWithXib.h"
@implementation CustomViewWithXib
#pragma mark - init methods
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// load view frame XIB
[self commonSetup];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
// load view frame XIB
[self commonSetup];
}
return self;
}
#pragma mark - setup view
- (UIView *)loadViewFromNib {
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
// An exception will be thrown if the xib file with this class name not found,
UIView *view = [[bundle loadNibNamed:NSStringFromClass([self class]) owner:self options:nil] firstObject];
return view;
}
- (void)commonSetup {
UIView *nibView = [self loadViewFromNib];
nibView.frame = self.bounds;
// the autoresizingMask will be converted to constraints, the frame will match the parent view frame
nibView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
// Adding nibView on the top of our view
[self addSubview:nibView];
}
@end
CustomViewWiспасибоib.swift:
import UIKit
@IBDesignable
class CustomViewWithXib: UIView {
// MARK: init methods
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonSetup()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonSetup()
}
// MARK: setup view
private func loadViewFromNib() -> UIView {
let viewBundle = NSBundle(forClass: self.dynamicType)
// An exception will be thrown if the xib file with this class name not found,
let view = viewBundle.loadNibNamed(String(self.dynamicType), owner: self, options: nil)[0]
return view as! UIView
}
private func commonSetup() {
let nibView = loadViewFromNib()
nibView.frame = bounds
// the autoresizingMask will be converted to constraints, the frame will match the parent view frame
nibView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
// Adding nibView on the top of our view
addSubview(nibView)
}
}
Здесь вы можете найти несколько примеров .
Надеюсь, что это поможет.