Oryginalne rozwiązanie
- Stworzyłem XIB i klasę o nazwie SomeView (dla wygody i czytelności użyłem tej samej nazwy). Oparłem oba na UIView.
- W XIB zmieniłem klasę „Właściciel pliku” na SomeView (w inspektorze tożsamości).
- Utworzyłem punkt sprzedaży UIView w SomeView.swift, łącząc go z widokiem najwyższego poziomu w pliku XIB (dla wygody nazwałem go „widokiem”). Następnie w razie potrzeby dodałem inne wyjścia do innych formantów w pliku XIB.
- w SomeView.swift załadowałem XIB wewnątrz inicjatora "init with code". Nie ma potrzeby przypisywania czegokolwiek do „siebie”. Zaraz po załadowaniu XIB wszystkie gniazda są podłączone, w tym widok z góry. Jedyne, czego brakuje, to dodać widok z góry do hierarchii widoków:
.
class SomeView: UIView {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
NSBundle.mainBundle().loadNibNamed("SomeView", owner: self, options: nil)
self.addSubview(self.view); // adding the top level view to the view hierarchy
}
...
}
Zauważ, że w ten sposób otrzymuję klasę, która ładuje się ze stalówki. Mógłbym wtedy użyć SomeView jako klasy, ilekroć UIView może być użyty w projekcie (w konstruktorze interfejsu lub programowo).
Aktualizacja - przy użyciu składni Swift 3
Ładowanie xib w następującym rozszerzeniu jest zapisywane jako metoda instancji, która może być następnie używana przez inicjator, taki jak powyższy:
extension UIView {
@discardableResult // 1
func fromNib<T : UIView>() -> T? { // 2
guard let contentView = Bundle(for: type(of: self)).loadNibNamed(String(describing: type(of: self)), owner: self, options: nil)?.first as? T else { // 3
// xib not loaded, or its top view is of the wrong type
return nil
}
self.addSubview(contentView) // 4
contentView.translatesAutoresizingMaskIntoConstraints = false // 5
contentView.layoutAttachAll(to: self) // 6
return contentView // 7
}
}
- Używanie odrzucalnej wartości zwracanej, ponieważ zwracany widok nie jest w większości interesujący dla dzwoniącego, gdy wszystkie gniazda są już połączone.
- Jest to metoda ogólna, która zwraca opcjonalny obiekt typu UIView. Jeśli załadowanie widoku nie powiedzie się, zwraca nil.
- Próba załadowania pliku XIB o tej samej nazwie, co bieżąca instancja klasy. Jeśli to się nie powiedzie, zwracane jest nil.
- Dodawanie widoku najwyższego poziomu do hierarchii widoków.
- Ta linia zakłada, że używamy ograniczeń do rozmieszczenia widoku.
- Ta metoda dodaje górne, dolne, wiodące i końcowe ograniczenia - dołączanie widoku do „siebie” ze wszystkich stron (szczegóły: https://stackoverflow.com/a/46279424/2274829 )
- Wracam do widoku najwyższego poziomu
A metoda wywołująca może wyglądać następująco:
final class SomeView: UIView { // 1.
required init?(coder aDecoder: NSCoder) { // 2 - storyboard initializer
super.init(coder: aDecoder)
fromNib() // 5.
}
init() { // 3 - programmatic initializer
super.init(frame: CGRect.zero) // 4.
fromNib() // 6.
}
// other methods ...
}
- SomeClass to podklasa UIView, która ładuje swoją zawartość z pliku SomeClass.xib. Słowo kluczowe „końcowe” jest opcjonalne.
- Inicjator, gdy widok jest używany w scenorysie (pamiętaj, aby użyć SomeClass jako niestandardowej klasy widoku storyboardu).
- Inicjator, gdy widok jest tworzony programowo (np. „Let myView = SomeView ()”).
- Używanie ramki zawierającej same zera, ponieważ ten widok jest układany przy użyciu automatycznego układu. Zauważ, że metoda "init (frame: CGRect) {..}" nie jest tworzona niezależnie, ponieważ auto-layout jest używany wyłącznie w naszym projekcie.
- & 6. Ładowanie pliku xib przy użyciu rozszerzenia.
Kredyt: Użycie ogólnego rozszerzenia w tym rozwiązaniu zostało zainspirowane poniższą odpowiedzią Roberta.
Edytuj
Zmieniając „widok” na „contentView”, aby uniknąć nieporozumień. Zmieniono także indeks tablicy na „.first”.
File's Owner
trafiona ... Dzięki!fromNib()
od wewnątrzinit(coder aDecoder: NSCoder)
tworzy nieskończoną pętlę, ponieważ ładowanie Nib wewnątrzfromNib()
metody powoduje wywołanie:init(coder aDecoder: NSCoder)
Mój wkład:
Następnie nazwij to tak:
..lub nawet:
źródło
Teraz możliwość
-> Self
szybkiego powrotu pomaga nieco to uprościć. Ostatnio potwierdzone w Swift 5.Jeśli twój
.xib
plik i podklasa mają tę samą nazwę, możesz użyć:Jeśli masz własną nazwę, użyj:
UWAGA:
Jeśli otrzymujesz komunikat o błędzie „nie znaleziono widoku typu YourType w…”, oznacza to, że klasa widoku nie została ustawiona w pliku
.xib
plikuWybierz widok w
.xib
pliku i naciśnijcmd + opt + 4
i weclass
wpisie wprowadź swoją klasęźródło
let myCustomView = UIView.fromNib() as? CustomView
. W tym przypadku,T.self
postanawiaUIView
zamiastCustomView
i nie udaje mu się znaleźć stalówkę. Nie jestem pewien, dlaczego tak jest - może wywnioskowany typ dlalet
środków, które funkcja jest nazywanaUIView
?spróbuj poniższego kodu.
Edytować:
źródło
Rozszerzenia protokołu Swift 4
Przyjęcie
Ta implementacja zakłada, że Nib ma taką samą nazwę jak klasa UIView. Dawny. MyView.xib. Możesz zmodyfikować to zachowanie, implementując nibName () w MyView, aby zwrócić inną nazwę niż domyślna implementacja rozszerzenia protokołu.
W xib właścicielem plików jest MyView, a główną klasą widoku jest MyView.
Stosowanie
źródło
Osiągnąłem to dzięki Swift za pomocą następującego kodu:
Nie zapomnij podłączyć gniazdka widoku XIB, aby szybko wyświetlić gniazdo zdefiniowane. Możesz także ustawić dla pierwszej pomocy nazwę swojej klasy, aby rozpocząć podłączanie dodatkowych gniazd.
Mam nadzieję że to pomoże!
źródło
Testowane w Xcode 7 beta 4, Swift 2.0 i iOS9 SDK. Poniższy kod przypisze xib do uiview. Możesz użyć tego niestandardowego widoku XIB w scenorysie i uzyskać dostęp do obiektu IBOutlet.
Uzyskaj programowy dostęp do widoku niestandardowego
Kod źródłowy - https://github.com/karthikprabhuA/CustomXIBSwift
źródło
Jeśli masz wiele niestandardowych widoków w swoim projekcie, możesz utworzyć klasę, taką jak
UIViewFromNib
Swift 2.3
Szybki 3
W każdej klasie dziedziczącej po prostu
UIViewFromNib
możesz również nadpisaćnibName
właściwość, jeśli.xib
plik ma inną nazwę:źródło
Opierając się na powyższych rozwiązaniach.
Będzie to działać we wszystkich paczkach projektów i nie będzie potrzeby stosowania typów ogólnych podczas wywoływania fromNib ().
Szybki 2
Szybki 3
Może być używany w ten sposób:
Lub tak:
źródło
Szybki 4
Nie zapomnij napisać „.first as? CustomView”.
Jeśli chcesz używać w dowolnym miejscu
Najlepszym rozwiązaniem jest odpowiedź Roberta Gummessona .
Następnie nazwij to tak:
źródło
źródło
self
jest niejawnie zwracany ze wszystkichinit
metod ...Dobrym sposobem na zrobienie tego w Swift jest użycie enum.
Następnie w swoim kodzie możesz po prostu użyć:
źródło
Wolę to rozwiązanie (na podstawie odpowiedzi jeśli @ GK100):
W SomeView.swift załadowałem XIB w inicjatorze
init
lubinit:frame: CGRect
. Nie ma potrzeby przypisywania czegokolwiek do „siebie”. Zaraz po załadowaniu XIB wszystkie gniazda są podłączone, w tym widok z góry. Jedyne, czego brakuje, to dodać widok z góry do hierarchii widoków:źródło
Szybka 3 wersja odpowiedzi Logana
źródło
Oto czysty i deklaratywny sposób programowego ładowania widoku przy użyciu protokołu i rozszerzenia protokołu (Swift 4.2):
Możesz użyć tego w następujący sposób:
Kilka dodatkowych uwag :
Custom Class
zestaw widoku (i stamtąd ustawienia wyjść / działań), a nie właściciela pliku.źródło
Po prostu robię w ten sposób:
Ten przykład używa pierwszego widoku w końcówce „MyView.xib” w pakiecie głównym. Możesz jednak zmienić indeks, nazwę końcówki lub pakiet (domyślnie główny).
Kiedyś budziłem widoki do metody init widoku lub tworzyłem metody ogólne, jak w rozwiązaniach powyżej (które są sprytne), ale już tego nie robię.
W ten sposób mogę używać różnych układów lub cech, zachowując tę samą logikę widoku i kod.
Łatwiej jest pozwolić obiektowi fabrycznemu (zwykle viewController, który będzie używać widoku), aby utworzył go tak, jak tego potrzebuje. Czasami potrzebujesz właściciela (zwykle gdy utworzony widok ma gniazdko podłączone do twórcy), czasami nie.
Prawdopodobnie dlatego Apple nie uwzględnił
initFromNib
metody w swojej klasie UIView ...Aby wziąć przykład z poziomu gruntu, nie wiesz, jak się urodziłeś. Po prostu się urodziłeś. Tak są widoki;)
źródło
Wszystko, co musisz zrobić, to wywołać metodę init w swojej
UIView
klasie.Zrób to w ten sposób:
Teraz, jeśli chcesz dodać ten widok jako widok podrzędny w kontrolerze widoku, zrób to w ten sposób w pliku view controller.swift:
źródło
Podobne do niektórych odpowiedzi powyżej, ale bardziej spójne rozszerzenie Swift3 UIView:
Co zapewnia wygodę wczytywania klasy z końcówki o tej samej nazwie, ale także z innych końcówek / pakietów.
źródło
Możesz to zrobić za pomocą scenorysu, po prostu dodaj odpowiednie ograniczenia dla widoku. Możesz to łatwo zrobić, podklasując dowolny widok z własnego, powiedzmy
BaseView
:Cel C
Szybki 4
Podaję 2 warianty dodawania ograniczeń - wspólne i w ramach języka formatu wizualnego - wybierz dowolne :)
Domyślnie przyjęto również, że
xib
nazwa ma taką samą nazwę jak nazwa klasy implementacji. Jeśli nie - po prostu zmieńxibName
parametr.Jeśli podklasujesz swój widok z
BaseView
- możesz łatwo umieścić dowolny widok i określić klasę w IB.źródło
źródło
Mocniejsza wersja oparta na odpowiedzi Logana
I możesz użyć podobnego
źródło
Najwygodniejsza realizacja. Tutaj potrzebujesz dwóch metod, aby powrócić bezpośrednio do obiektu swojej klasy, a nie UIView.
Przykład:
źródło
Jeśli chcesz, aby podklasa Swift UIView była całkowicie samowystarczalna i miała możliwość tworzenia instancji za pomocą init lub init (ramka :) bez ujawniania szczegółów implementacji używania Nib, możesz użyć rozszerzenia protokołu, aby to osiągnąć. To rozwiązanie pozwala uniknąć zagnieżdżonej hierarchii UIView, zgodnie z sugestią wielu innych rozwiązań.
źródło
źródło
Wolę poniższe rozszerzenie
Różnica między tym rozszerzeniem a rozszerzeniem z najczęściej odpowiedziami polega na tym, że nie musisz zapisywać go jako stałej ani zmiennej.
źródło
źródło