Jak utworzyć niestandardową klasę widoku iOS i utworzyć instancję wielu jej kopii (w IB)?
Obecnie robię aplikację, która będzie miała wiele timerów, które są w zasadzie wszystkie takie same.
Chcę stworzyć własną klasę, która używa całego kodu dla timerów, jak również układu / animacji, więc mogę mieć 5 identycznych timerów, które działają niezależnie od siebie.
Stworzyłem układ używając IB (xcode 4.2) i cały kod timerów jest obecnie tylko w klasie viewcontroller.
Mam problemy z owijaniem mózgu wokół tego, jak zamknąć wszystko w niestandardowej klasie, a następnie dodać go do viewcontroller, każda pomoc będzie mile widziana.
4 answers
Dobrze odpowiedzieć koncepcyjnie, Twój timer powinien być podklasą UIView
zamiast NSObject
.
Aby utworzyć instancję instancji timera w IB, po prostu przeciągnij UIView
upuść ją w widoku kontrolera widoku i ustaw klasę na nazwę klasy timera.
Pamiętaj o klasie timera w kontrolerze widoku.
Edit: for IB design (dla instancji kodu patrz historia wersji)
Nie znam się zbyt dobrze na wszystko z storyboard, ale wiem, że możesz zbudować swój interfejs w IB za pomocą pliku .xib
, który jest prawie identyczny z używaniem wersji storyboard; powinieneś nawet być w stanie skopiować i wkleić swoje widoki jako całość z istniejącego interfejsu do pliku .xib
.
Aby to przetestować utworzyłem nowy pusty .xib
o nazwie "MyCustomTimerView.xib". Następnie dodałem widok, A do tego dodałem etykietę i dwa przyciski. Tak:
Stworzyłem nową klasę objective-C podklasa UIView
o nazwie "MyCustomTimer". W mojej .xib
ustawiam klasę właściciela pliku na MyCustomTimer . Teraz mogę swobodnie łączyć akcje i wyjścia, tak jak każdy inny widok / kontroler. Plik wynikowy.h
wygląda następująco:
@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
Jedyną przeszkodą do skoku jest zdobycie tego .xib
na mojej podklasie UIView
. Użycie .xib
radykalnie zmniejsza wymaganą konfigurację. A ponieważ używasz storyboardów do ładowania timerów, o których wiemy, że -(id)initWithCoder:
jest jedynym inicjalizatorem, który zostanie wezwany. Oto jak wygląda plik implementacji:
#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
Metoda o nazwie loadNibNamed:owner:options:
robi dokładnie to, na co wygląda. Ładuje stalówkę i ustawia właściwość "File' s Owner " na self. Wyodrębniamy pierwszy obiekt w tablicy i jest to główny widok Stalówki. Dodajemy Widok jako subview i Voila jest na ekranie.
Oczywiście po prostu zmienia to kolor tła etykiety, gdy przyciski są wciśnięte, ale ten przykład powinien ci pomóc sposób.
Uwagi na podstawie komentarzy:
Warto zauważyć, że jeśli masz problemy z rekurencją nieskończoną, prawdopodobnie przegapiłeś subtelną sztuczkę tego rozwiązania. Nie robi tego, co myślisz. Widok umieszczony w storyboardzie nie jest widoczny, ale zamiast tego ładuje inny widok jako podgląd podrzędny. Widok, który ładuje, jest widokiem zdefiniowanym w stalówce. "Właściciel pliku" w stalówce jest tym niewidzialnym widokiem. Najfajniejsze jest to, że ten niewidoczny widok jest nadal Objective-Klasa C, która może być używana jako kontroler widoku dla widoku, który wnosi z końcówki. Na przykład metody IBAction
w klasie MyCustomTimer
są czymś, czego można oczekiwać bardziej w kontrolerze widoku niż w widoku.
na marginesie, niektórzy mogą twierdzić, że to łamie MVC
i trochę się Zgadzam. Z mojego punktu widzenia jest to bardziej związane z niestandardowym UITableViewCell
, który również czasami musi być kontrolerem części.
Warto również zauważyć, że ta odpowiedź miała zapewnić bardzo konkretne rozwiązanie; utworzyć jedną stalówkę, która może być instancjowana wiele razy na tym samym widoku, jak określono na storyboardzie. Na przykład możesz łatwo wyobrazić sobie sześć z tych liczników na ekranie iPada w jednym czasie. Jeśli potrzebujesz tylko określić widok kontrolera widoku, który ma być używany wiele razy w całej aplikacji, to rozwiązanie dostarczone przez jyavenard do tego pytania jest prawie na pewno lepszym rozwiązaniem dla Ciebie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:39
Przykład Swift
Aktualizacja dla Xcode 9 i Swift 4
Oto podstawowe Przejście. Wiele się z tego nauczyłem oglądając tę serię filmów na Youtube . Później zaktualizowałem swoją odpowiedź na podstawie tego artykułu .
Dodaj własne pliki widoku
Następujące dwa pliki utworzą Twój własny widok:
- .plik xib zawierający układ
- .plik swift jako
UIView
podklasa
Szczegóły do ich dodania są poniżej.
Plik Xib
Dodaj a .plik xib do twojego projektu (Plik > Nowy > plik... > Interfejs Użytkownika > Widok) . Nazywam swoją ReusableCustomView.xib
.
Utwórz układ, który chcesz mieć w widoku niestandardowym. Jako przykład zrobię układ z UILabel
i UIButton
. Dobrym pomysłem jest użycie układu automatycznego, aby rzeczy automatycznie zmieniały rozmiar bez względu na to, jaki rozmiar ustawisz później. (Użyłem Freeform dla rozmiaru xib w Inspektor atrybutów, żebym mógł dostosować symulowane metryki, ale nie jest to konieczne.)
Plik Swift
Dodaj a .plik swift do twojego projektu (Plik > Nowy > plik... > Source > Swift File) . Jest to podklasa UIView
i nazywam swoją ReusableCustomView.swift
.
import UIKit
class ResuableCustomView: UIView {
}
Uczyń plik Swift właścicielem
Wróć do swojego .plik xib i kliknij "właściciel pliku" w zarysie dokumentu. W tożsamości Inspektor napisz imię swojego .plik swift jako niestandardowa nazwa klasy.
Dodaj Własny Kod Widoku
Zastąp zawartość pliku ReusableCustomView.swift
następującym kodem:
import UIKit
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)
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
}
}
Upewnij się, że pisownia Twojego imienia jest prawidłowa .plik xib.
Podłącz gniazdka i akcje
Podłącz gniazda i akcje poprzez sterowanie przeciąganiem z etykiety i przycisku w układzie xib do swift custom view kod.
Użyj własnego widoku
Twój widok niestandardowy jest już skończony. Wszystko, co musisz zrobić, to dodać UIView
gdziekolwiek chcesz, w swoim głównym storyboardzie. Ustaw nazwę klasy widoku na ReusableCustomView
w Inspektorze tożsamości.
W momencie pisania tego tekstu miałem problem z dodaniem @IBDesignable do kodu, więc widoki nie pojawiają się w IB, ale robią to podczas uruchamiania aplikacji. (Na powyższym obrazku dałem widoki IB kolor tła tak, że będą widoczne w czasie projektowania.)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-23 19:01:05
odpowiedź dla kontrolerów widoku, a nie views:
Istnieje łatwiejszy sposób załadowania xib ze storyboardu.
Powiedzmy, że twój kontroler jest typu MyClassController, który dziedziczy od UIViewController
.
Dodajesz UIViewController
używając IB w storyboardzie; Zmień typ klasy na MyClassController. Usuń widok, który został automatycznie dodany w storyboardzie.
Upewnij się, że żądany XIB nazywa się MyClassController.xib.
Kiedy klasa będzie utworzone podczas ładowania storyboardu xib zostanie automatycznie załadowany.
Powodem tego jest domyślna implementacja UIViewController
, która wywołuje nazwę XIB z nazwą klasy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-04 05:58:55
To nie jest naprawdę odpowiedź, ale myślę, że warto podzielić się tym podejściem.
Objective-C
- Import CustomViewWithXib.h i CustomViewWithXib.m do twojego projekt
- Utwórz własne pliki widoków o tej samej nazwie (.h / .na / .xib)
- Dziedzicz swoją klasę z CustomViewWithXib
Swift
- Import CustomViewWithXib.swift do twojego projektu
- Utwórz własne pliki widoków o tej samej nazwie (.swift i .xib)
- Dziedzicz swoją klasę z CustomViewWithXib
Opcjonalnie:
- przejdź do pliku xib, Ustaw właściciela z niestandardową nazwą klasy, jeśli potrzeba podłączenia niektórych elementów (więcej szczegółów w części Make plik Swift właściciela @ Suragch answer ' S)
To wszystko, teraz możesz dodać swój niestandardowy widok do storyboardu i zostanie wyświetlony :)
CustomViewWithXib.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
CustomViewWithXib.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
CustomViewWithXib.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)
}
}
Kilka przykładów znajdziesz tutaj .
Mam nadzieję, że to pomoże.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-24 14:08:33