Powiedzmy, że mam scenorys, który zawiera UINavigationController
jako początkowy kontroler widoku. Jego główny kontroler widoku jest podklasą UITableViewController
, czyli BasicViewController
. Ma to, IBAction
co jest połączone z prawym przyciskiem nawigacyjnym paska nawigacji.
Stamtąd chciałbym użyć storyboardu jako szablonu dla innych widoków bez konieczności tworzenia dodatkowych scenorysów. Powiedzmy, że te widoki będą miały dokładnie ten sam interfejs, ale z głównym kontrolerem widoku klasy SpecificViewController1
i SpecificViewController2
które są podklasami BasicViewController
.
Te dwa kontrolery widoku miałyby taką samą funkcjonalność i interfejs, z wyjątkiem IBAction
metody.
Wyglądałoby to tak:
@interface BasicViewController : UITableViewController
@interface SpecificViewController1 : BasicViewController
@interface SpecificViewController2 : BasicViewController
Czy mogę coś takiego zrobić?
Czy mogę po prostu utworzyć instancję storyboardu, BasicViewController
ale mam główny kontroler widoku do podklasy SpecificViewController1
i SpecificViewController2
?
Dzięki.
Odpowiedzi:
świetne pytanie - ale niestety tylko kiepska odpowiedź. Nie wierzę, że obecnie jest możliwe zrobienie tego, co proponujesz, ponieważ w UIStoryboard nie ma inicjatorów, które pozwalają na zastąpienie kontrolera widoku powiązanego ze scenorysem, jak określono w szczegółach obiektu w scenorysie podczas inicjalizacji. Podczas inicjalizacji wszystkie elementy interfejsu użytkownika w stoarybicy są połączone z ich właściwościami w kontrolerze widoku.
Domyślnie zainicjuje się za pomocą kontrolera widoku określonego w definicji scenorysu.
Jeśli próbujesz ponownie wykorzystać elementy interfejsu użytkownika, które utworzyłeś w scenorysie, nadal muszą być one połączone lub powiązane z właściwościami, w których kontroler widoku kiedykolwiek używa ich, aby móc „powiedzieć” kontrolerowi widoku o zdarzeniach.
Kopiowanie układu scenorysu nie jest aż tak wielkim problemem, zwłaszcza jeśli potrzebujesz podobnego projektu tylko dla 3 widoków, jednak jeśli to zrobisz, musisz upewnić się, że wszystkie poprzednie skojarzenia są wyczyszczone, w przeciwnym razie wystąpi awaria podczas próby komunikować się z poprzednim kontrolerem widoku. Będziesz mógł rozpoznać je jako komunikaty o błędach KVO w danych wyjściowych dziennika.
Kilka podejść, które możesz zastosować:
przechowuj elementy interfejsu użytkownika w UIView - w pliku xib i utwórz jego wystąpienie z klasy bazowej i dodaj jako widok podrzędny w widoku głównym, zwykle self.view. Następnie po prostu użyjesz układu scenorysu z zasadniczo pustymi kontrolerami widoku utrzymującymi swoje miejsce w serii ujęć, ale z przypisaną do nich poprawną podklasą kontrolera widoku. Ponieważ odziedziczyliby po bazie, uzyskaliby ten pogląd.
utwórz układ w kodzie i zainstaluj go z kontrolera widoku podstawowego. Oczywiście takie podejście jest sprzeczne z celem korzystania ze scenorysu, ale może być dobrym rozwiązaniem w Twoim przypadku. Jeśli masz inne części aplikacji, które skorzystałyby na podejściu do scenorysu, możesz w razie potrzeby odejść tu i tam. W takim przypadku, podobnie jak powyżej, wystarczy użyć kontrolerów widoku banku z przypisaną podklasą i pozwolić kontrolerowi widoku podstawowego zainstalować interfejs użytkownika.
Byłoby miło, gdyby Apple wymyślił sposób na zrobienie tego, co proponujesz, ale kwestia posiadania elementów graficznych wstępnie połączonych z podklasą kontrolera nadal byłaby problemem.
Miłego Nowego Roku !! Bywaj
źródło
Kod szukanej linii to:
W Storyboard -> dodaj UIViewController, nadaj mu nazwę klasy ParentVC.
źródło
class func instantiate() -> SubClass { let instance = (UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("SuperClass") as? SuperClass)! object_setClass(instance, SubClass.self) return (instance as? SubClass)! }
self
nie zmienia się. Zatem inspekcja obiektu (np. Odczyt wartości _ivar / property) późniejobject_setClass
może spowodować awarie.Zgodnie z przyjętą odpowiedzią nie wygląda na to, aby można było zrobić z storyboardami.
Moim rozwiązaniem jest użycie końcówek - tak jak deweloperzy używali ich przed storyboardami. Jeśli chcesz mieć kontroler widoku wielokrotnego użytku, z możliwością podklasy (lub nawet widok), zalecam użycie końcówek.
Kiedy łączysz wszystkie swoje gniazda z "Właścicielem pliku" w polu
MyViewController.xib
NIE określasz, w jakiej klasie powinien być ładowany Nib, po prostu określasz pary klucz-wartość: " ten widok powinien być połączony z nazwą zmiennej instancji ." Podczas wywoływania[SubclassMyViewController alloc] initWithNibName:
procesu inicjalizacji określa, jaki kontroler widoku będzie używany do „ sterowania ” widokiem utworzonym w końcówce.źródło
Możliwe jest, aby scenorys tworzył wystąpienie różnych podklas niestandardowego kontrolera widoku, chociaż wymaga to nieco niekonwencjonalnej techniki: przesłaniania
alloc
metody kontrolera widoku. Po utworzeniu kontrolera widoku niestandardowego, zastąpiona metoda alokacji w rzeczywistości zwraca wynik działaniaalloc
w podklasie.Odpowiedź powinienem poprzedzić z zastrzeżeniem, że chociaż testowałem go w różnych scenariuszach i nie otrzymałem żadnych błędów, nie mogę zapewnić, że poradzi sobie z bardziej złożonymi konfiguracjami (ale nie widzę powodu, dla którego nie miałoby działać) . Ponadto nie przesłałem żadnych aplikacji przy użyciu tej metody, więc istnieje zewnętrzna szansa, że może ona zostać odrzucona w procesie recenzji Apple (chociaż znowu nie widzę powodu, dla którego miałoby to robić).
Dla celów demonstracyjnych mam podklasę
UIViewController
nazwanąTestViewController
, która ma UILabel IBOutlet i IBAction. W moim storyboardzie dodałem kontroler widoku i zmieniłem jego klasę naTestViewController
oraz podłączyłem IBOutlet do UILabel, a IBAction do UIButton. Przedstawiam TestViewController za pomocą modalnego segue wyzwalanego przez UIButton na poprzednim viewController.Aby kontrolować, która klasa jest tworzona, dodałem zmienną statyczną i powiązane metody klas, więc pobierz / ustaw podklasę, która ma być używana (myślę, że można przyjąć inne sposoby określania, która podklasa ma zostać utworzona):
TestViewController.m:
Do mojego testu mam dwie podklasy
TestViewController
:RedTestViewController
iGreenTestViewController
. Każda z podklas ma dodatkowe właściwości i każda nadpisanie,viewDidLoad
aby zmienić kolor tła widoku i zaktualizować tekst UILabel IBOutlet:RedTestViewController.m:
GreenTestViewController.m:
Czasami mogę chcieć utworzyć instancję
TestViewController
, w innych przypadkachRedTestViewController
lubGreenTestViewController
. W powyższym kontrolerze widoku robię to losowo w następujący sposób:Zwróć uwagę, że
setClassForStoryBoard
metoda sprawdza, czy żądana nazwa klasy jest rzeczywiście podklasą TestViewController, aby uniknąć wszelkich pomyłek. Powyższe odniesienieBlueTestViewController
służy do testowania tej funkcji.źródło
spróbuj tego, po wystąpieniu instantiateViewControllerWithIdentifier.
lubić :
źródło
EXC_BAD_ACCESS
, więc nie polecam tego.init
też nie zostanie wezwane. Takie ograniczenia sprawiają, że każde podejście jest bezużyteczne.Bazując w szczególności na odpowiedziach nickgzzjr i Jiří Zahálka oraz komentarzu pod drugim z CocoaBob, przygotowałem krótką metodę generyczną, która robi dokładnie to, czego potrzebuje OP. Wystarczy sprawdzić nazwę serii ujęć i wyświetlić identyfikator serii ujęć kontrolerów
Dodawane są opcje, aby uniknąć wymuszonego rozwijania (ostrzeżenia swiftlint), ale metoda zwraca poprawne obiekty.
źródło
Chociaż nie jest to ściśle podklasa, możesz:
Oto przykład z samouczka Bloc, który napisałem, podklasy
ViewController
zWhiskeyViewController
:Pozwala to na tworzenie podklas podklas kontrolera widoku w scenorysie. Możesz następnie użyć
instantiateViewControllerWithIdentifier:
do utworzenia określonych podklas.To podejście jest nieco nieelastyczne: późniejsze modyfikacje w scenorysie do kontrolera klasy bazowej nie są propagowane do podklasy. Jeśli masz wiele podklas, być może lepiej będzie, jeśli skorzystasz z jednego z pozostałych rozwiązań, ale to wystarczy.
źródło
initWithCoder:
, nie mają odziedziczonej relacji. Ten typ relacji nie jest obsługiwany przez pliki scenorysu.Metoda Objc_setclass nie tworzy instancji childvc. Ale podczas wyskakiwania z childvc, wywoływane jest deinit of childvc. Ponieważ nie ma pamięci przydzielonej oddzielnie dla childvc, aplikacja ulega awarii. Basecontroller ma instancję, podczas gdy podrzędny vc nie ma.
źródło
Jeśli nie jesteś zbyt uzależniony od scenorysów, możesz utworzyć osobny plik .xib dla kontrolera.
Ustaw odpowiedniego właściciela pliku i wyjścia na
MainViewController
i nadpiszinit(nibName:bundle:)
w głównym VC, aby jego elementy podrzędne miały dostęp do tej samej stalówki i gniazd.Twój kod powinien wyglądać następująco:
A Twoje dziecko VC będzie mogło ponownie użyć stalówki swojego rodzica:
źródło
Biorąc odpowiedzi stąd i ówdzie, znalazłem to zgrabne rozwiązanie.
Utwórz nadrzędny kontroler widoku z tą funkcją.
Dzięki temu kompilator może upewnić się, że podrzędny kontroler widoku dziedziczy po nadrzędnym kontrolerze widoku.
Następnie, gdy chcesz przejść do tego kontrolera za pomocą podklasy, możesz zrobić:
Fajne jest to, że możesz dodać do siebie odniesienie do scenorysu, a następnie nadal wywoływać „następny” podrzędny kontroler widoku.
źródło
Prawdopodobnie najbardziej elastycznym sposobem jest użycie widoków wielokrotnego użytku.
(Utwórz widok w oddzielnym pliku XIB lub
Container view
i dodaj go do każdej sceny kontrolera widoku podklasy w serii ujęć)źródło
Jest proste, oczywiste, codzienne rozwiązanie.
Po prostu umieść istniejącą planszę / kontroler wewnątrz nowej planszy / kontrolera. IE jako widok kontenera.
Jest to dokładnie analogiczne pojęcie do „podklasy” dla kontrolerów widoku.
Wszystko działa dokładnie tak, jak w podklasie.
Tak jak zwykle umieszcza się widok podrzędny w innym widoku , naturalnie zwykle umieszcza się kontroler widoku w innym kontrolerze widoku .
Jak inaczej możesz to zrobić?
To podstawowa część iOS, tak prosta jak koncepcja „podwidoku”.
To takie proste ...
Teraz oczywiście musisz
list
robić, co chceszitd itd.
Widoki kontenerów są „tak jak” podklasy w taki sam sposób, jak „podklasy” są „tak samo jak” podklasy.
Oczywiście nie można „podklasować układu” - co to w ogóle oznacza?
(„Podklasy” odnoszą się do oprogramowania obiektowego i nie mają związku z „układami”).
Oczywiście, gdy chcesz ponownie użyć widoku, po prostu wyświetlasz go w innym widoku.
Jeśli chcesz ponownie użyć układu kontrolera, po prostu kontener przeglądaj go wewnątrz innego kontrolera.
To jest jak najbardziej podstawowy mechanizm iOS !!
Uwaga - od lat trywialne jest dynamiczne ładowanie innego kontrolera widoku jako widoku kontenera. Wyjaśniono w ostatniej sekcji: https://stackoverflow.com/a/23403979/294884
Uwaga - „_sb” to po prostu oczywiste makro, którego używamy, aby zaoszczędzić pisanie,
źródło
Dzięki za inspirującą odpowiedź @ Jiří Zahálka, odpowiedziałem na moje rozwiązanie 4 lata temu tutaj , ale @Sayka zasugerował, żebym opublikował to jako odpowiedź, więc oto jest.
W moich projektach zwykle, jeśli używam Storyboard dla podklasy UIViewController, zawsze przygotowuję statyczną metodę wywoływaną
instantiate()
w tej podklasie, aby łatwo utworzyć wystąpienie z Storyboard. Aby rozwiązać pytanie OP, jeśli chcemy udostępnić ten sam Storyboard dla różnych podklas, możemy po prostusetClass()
przejść do tego wystąpienia przed jego zwróceniem.źródło
Komentarz Cocoabob od odpowiedzi Jiříego Zahálki pomógł mi w znalezieniu tego rozwiązania i działa dobrze.
źródło