Dostać się do UIViewController z UIView?


185

Czy istnieje wbudowany sposób na przejście od a UIViewdo jego UIViewController? Wiem, że możesz dostać się UIViewControllerdo jego UIViewpoprzez, [self view]ale zastanawiałem się, czy istnieje odwrotne odniesienie?

Odpowiedzi:


46

Ponieważ od dawna jest to odpowiedź zaakceptowana, uważam, że muszę ją poprawić, podając lepszą odpowiedź.

Kilka uwag na temat potrzeby:

  • Twój widok nie powinien wymagać bezpośredniego dostępu do kontrolera widoku.
  • Widok powinien zamiast tego być niezależny od kontrolera widoku i być zdolny do pracy w różnych kontekstach.
  • Jeśli potrzebujesz, aby widok współdziałał z kontrolerem widoku, zalecanym sposobem i tym, co Apple robi w Cocoa, jest użycie wzoru delegowanego.

Oto przykład jego wdrożenia:

@protocol MyViewDelegate < NSObject >

- (void)viewActionHappened;

@end

@interface MyView : UIView

@property (nonatomic, assign) MyViewDelegate delegate;

@end

@interface MyViewController < MyViewDelegate >

@end

Widok łączy się z jego delegatem (jak UITableViewna przykład) i nie obchodzi go, czy został zaimplementowany w kontrolerze widoku, czy w jakiejkolwiek innej klasie, której używasz.

Moja pierwotna odpowiedź brzmi: nie polecam tego, ani pozostałych odpowiedzi, w których uzyskano bezpośredni dostęp do kontrolera widoku

Nie ma wbudowanego sposobu, aby to zrobić. Chociaż można obejść przez dodanie IBOutletna UIViewi łączenia ich w konstruktorze Interface, nie jest to zalecane. Widok nie powinien wiedzieć o kontrolerze widoku. Zamiast tego należy postępować zgodnie z sugestią @Phil M. i utworzyć protokół, który będzie używany jako delegat.


26
To bardzo zła rada. Nie należy odwoływać się do kontrolera widoku z widoku
Philippe Leybaert,

6
@MattDiPasquale: tak, to zły projekt.
Philippe Leybaert

23
@Phillipe Leybaert Jestem ciekawy, jakie są Twoje przemyślenia na temat najlepszego projektu zdarzenia kliknięcia przycisku, które powinno wywołać pewne działanie na kontrolerze, bez widoku zawierającego odniesienie do kontrolera. Dzięki
Jonathon Horsman

3
@PhilippeLeybaert Przykładowe projekty Apple mają tendencję do demonstrowania użycia określonych funkcji API. Wiele wspomniałem o poświęceniu dobrego lub skalowalnego projektu w celu zwięzłego zademonstrowania tematu. Uświadomienie sobie tego zajęło mi dużo czasu i choć ma to sens, wydaje mi się, że jest to niefortunne. Myślę, że wielu programistów traktuje te pragmatyczne projekty jako przewodnik Apple po najlepszych praktykach projektowych, czego jestem pewien, że nie są.
Benjohn

11
Całe to bs na temat „nie powinieneś tego robić” jest po prostu tym. Jeśli widok chce wiedzieć o swoim kontrolerze widoku, programista musi podjąć decyzję. Kropka.
Daniel Kanaan,

203

Korzystając z przykładu opublikowanego przez Brocka, zmodyfikowałem go tak, aby był to kategoria UIView zamiast UIViewController i sprawiłem, że będzie rekurencyjny, aby każdy podview mógł (mam nadzieję) znaleźć nadrzędny UIViewController.

@interface UIView (FindUIViewController)
- (UIViewController *) firstAvailableUIViewController;
- (id) traverseResponderChainForUIViewController;
@end

@implementation UIView (FindUIViewController)
- (UIViewController *) firstAvailableUIViewController {
    // convenience function for casting and to "mask" the recursive function
    return (UIViewController *)[self traverseResponderChainForUIViewController];
}

- (id) traverseResponderChainForUIViewController {
    id nextResponder = [self nextResponder];
    if ([nextResponder isKindOfClass:[UIViewController class]]) {
        return nextResponder;
    } else if ([nextResponder isKindOfClass:[UIView class]]) {
        return [nextResponder traverseResponderChainForUIViewController];
    } else {
        return nil;
    }
}
@end

Aby użyć tego kodu, dodaj go do nowego pliku klasy (nazwałem mój „UIKitCategories”) i usuń dane klasy ... skopiuj interfejs @ do nagłówka, a @implementation do pliku .m. Następnie w swoim projekcie #import „UIKitCategories.h” i użyj w kodzie UIView:

// from a UIView subclass... returns nil if UIViewController not available
UIViewController * myController = [self firstAvailableUIViewController];

47
Jednym z powodów, dla których musisz pozwolić UIViewowi być świadomym jego UIViewController jest to, że masz niestandardowe podklasy UIView, które muszą przesuwać widok / okno modalne.
Phil M

Niesamowite, musiałem uzyskać dostęp do mojego ViewController, aby wyświetlić niestandardowe okienko wyskakujące, które jest tworzone przez widok podrzędny
aryaxt

2
Czy to nie jest zła praktyka dla UIView, aby popchnąć modalne spojrzenie? Robię to teraz, ale uważam, że nie jest to właściwe.
Van Du Tran,

6
Phil, twój widok niestandardowy powinien wywołać metodę delegowaną, której kontroler widoku nasłuchuje, a następnie wypycha stamtąd.
malhal

9
po prostu uwielbiam, ile pytań SO ma „mądry człowiek”, akademicki, zaakceptował odpowiedź z zaledwie kilkoma punktami i drugą odpowiedzią, która jest praktyczna, brudna i niezgodna z zasadami, z dziesięciokrotnością punktów :-)
hariseldon78

114

UIViewjest podklasą UIResponder. UIResponderokreśla metodę -nextResponderz implementacją, która zwraca nil. UIViewzastępuje tę metodę, jak udokumentowano w UIResponder(z jakiegoś powodu zamiast w UIView) w następujący sposób: jeśli widok ma kontroler widoku, jest zwracany przez -nextResponder. Jeśli nie ma kontrolera widoku, metoda zwróci podgląd.

Dodaj to do swojego projektu i możesz rozpocząć.

@interface UIView (APIFix)
- (UIViewController *)viewController;
@end

@implementation UIView (APIFix)

- (UIViewController *)viewController {
    if ([self.nextResponder isKindOfClass:UIViewController.class])
        return (UIViewController *)self.nextResponder;
    else
        return nil;
}
@end

Teraz UIViewma działającą metodę zwracania kontrolera widoku.


5
Działa to tylko wtedy, gdy w łańcuchu odpowiedzi nie ma nic między odbiorcą UIViewa odbiorcą UIViewController. Odpowiedź Phila M. obejmująca rekurencję to droga.
Olivier

33

Sugerowałbym bardziej lekkie podejście do przejścia przez cały łańcuch odpowiedzi bez konieczności dodawania kategorii w UIView:

@implementation MyUIViewSubclass

- (UIViewController *)viewController {
    UIResponder *responder = self;
    while (![responder isKindOfClass:[UIViewController class]]) {
        responder = [responder nextResponder];
        if (nil == responder) {
            break;
        }
    }
    return (UIViewController *)responder;
}

@end

22

Łącząc kilka już udzielonych odpowiedzi, wysyłam je również z moją implementacją:

@implementation UIView (AppNameAdditions)

- (UIViewController *)appName_viewController {
    /// Finds the view's view controller.

    // Take the view controller class object here and avoid sending the same message iteratively unnecessarily.
    Class vcc = [UIViewController class];

    // Traverse responder chain. Return first found view controller, which will be the view's view controller.
    UIResponder *responder = self;
    while ((responder = [responder nextResponder]))
        if ([responder isKindOfClass: vcc])
            return (UIViewController *)responder;

    // If the view controller isn't found, return nil.
    return nil;
}

@end

Kategoria jest częścią mojej biblioteki statycznej z obsługą ARC , którą wysyłam do każdej tworzonej aplikacji. Został przetestowany kilka razy i nie znalazłem żadnych problemów ani wycieków.

PS: Nie musisz używać kategorii takiej jak ja, jeśli dany widok jest twoją podklasą. W tym drugim przypadku po prostu umieść metodę w swojej podklasie i możesz zacząć.


1
To najlepsza odpowiedź. Nie ma potrzeby rekurencji, ta wersja jest ładnie zoptymalizowana
zeroimpl

12

Chociaż technicznie można to rozwiązać zgodnie z zaleceniami pgb , IMHO, jest to jednak wada projektowa. Widok nie powinien być świadomy kontrolera.


Po prostu nie jestem pewien, w jaki sposób można powiedzieć viewController, że jeden z jego widok odchodzi i musi wywołać jedną z metod viewXXXAppear / viewXXXDisappear.
mahboudz

2
Taki jest pomysł wzorca Observer. Obserwowany (w tym przypadku Widok) nie powinien bezpośrednio wiedzieć o swoich Obserwatorach. Obserwator powinien otrzymywać tylko oddzwonienia, którymi są zainteresowani.
Ushox,

12

I zmodyfikowane de odpowiedź więc mogę przekazać wszelkie widok, przycisk, etykiety itd., Aby uzyskać jej jednostki UIViewController. Oto mój kod.

+(UIViewController *)viewController:(id)view {
    UIResponder *responder = view;
    while (![responder isKindOfClass:[UIViewController class]]) {
        responder = [responder nextResponder];
        if (nil == responder) {
            break;
        }
    }
    return (UIViewController *)responder;
}

Edytuj wersję Swift 3

class func viewController(_ view: UIView) -> UIViewController {
        var responder: UIResponder? = view
        while !(responder is UIViewController) {
            responder = responder?.next
            if nil == responder {
                break
            }
        }
        return (responder as? UIViewController)!
    }

Edycja 2: - Szybkie rozszerzenie

extension UIView
{
    //Get Parent View Controller from any view
    func parentViewController() -> UIViewController {
        var responder: UIResponder? = self
        while !(responder is UIViewController) {
            responder = responder?.next
            if nil == responder {
                break
            }
        }
        return (responder as? UIViewController)!
    }
}

7

Nie zapominaj, że możesz uzyskać dostęp do kontrolera widoku głównego dla okna, którego widok jest widokiem podrzędnym. Stamtąd, jeśli np. Używasz kontrolera widoku nawigacji i chcesz na niego nałożyć nowy widok:

    [[[[self window] rootViewController] navigationController] pushViewController:newController animated:YES];

Najpierw jednak musisz poprawnie ustawić właściwość rootViewController okna. Zrób to, gdy tworzysz kontroler po raz pierwszy, np. W aplikacji delegowanej:

-(void) applicationDidFinishLaunching:(UIApplication *)application {
    window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    RootViewController *controller = [[YourRootViewController] alloc] init];
    [window setRootViewController: controller];
    navigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
    [controller release];
    [window addSubview:[[self navigationController] view]];
    [window makeKeyAndVisible];
}

Wydaje mi się, że zgodnie z dokumentami Apple, ponieważ [[self navigationController] view]jest to „główny” (pod) widok okna, rootViewControllernależy ustawić właściwość okna, która natychmiast navigationControllerkontroluje widok „główny”.
adubr

6

Chociaż odpowiedzi te są technicznie poprawne, w tym Ushox, myślę, że zatwierdzonym sposobem jest wdrożenie nowego protokołu lub ponowne użycie istniejącego. Protokół izoluje obserwatora od obserwowanego, podobnie jak umieszczenie między nimi szczeliny na listy. W efekcie to właśnie robi Gabriel poprzez wywołanie metody pushViewController; widok „wie”, że właściwym protokołem jest grzeczne poproszenie twojego NavigationController o wypchnięcie widoku, ponieważ viewController jest zgodny z protokołem navigationController. Chociaż możesz stworzyć własny protokół, wystarczy skorzystać z przykładu Gabriela i ponownie użyć protokołu UINavigationController.


6

Natknąłem się na sytuację, w której mam mały komponent, którego chcę ponownie użyć, i dodałem trochę kodu w widoku wielokrotnego użytku (tak naprawdę to niewiele więcej niż przycisk otwierający a PopoverController).

Chociaż działa to dobrze na iPadzie ( UIPopoverControllersame prezenty, więc nie wymaga odniesienia do a UIViewController), uzyskanie tego samego kodu do działania oznacza nagle odwołanie się do twojego presentViewControllerz twojego UIViewController. Trochę niespójne, prawda?

Jak wspomniano wcześniej, nie jest to najlepsze podejście do posiadania logiki w UIView. Ale nie było sensu pakować kilku wierszy kodu potrzebnych w osobny kontroler.

Tak czy inaczej, oto szybkie rozwiązanie, które dodaje nową właściwość do dowolnego UIView:

extension UIView {

    var viewController: UIViewController? {

        var responder: UIResponder? = self

        while responder != nil {

            if let responder = responder as? UIViewController {
                return responder
            }
            responder = responder?.nextResponder()
        }
        return nil
    }
}

5

Nie wydaje mi się, aby „złym” pomysłem było ustalenie, kto jest kontrolerem widoku w niektórych przypadkach. Nieodpowiednim pomysłem może być zapisanie odwołania do tego kontrolera, ponieważ może on ulec zmianie, podobnie jak zmiany superviews. W moim przypadku mam moduł pobierający, który przechodzi przez łańcuch odpowiedzi.

//.h

@property (nonatomic, readonly) UIViewController * viewController;

//.m

- (UIViewController *)viewController
{
    for (UIResponder * nextResponder = self.nextResponder;
         nextResponder;
         nextResponder = nextResponder.nextResponder)
    {
        if ([nextResponder isKindOfClass:[UIViewController class]])
            return (UIViewController *)nextResponder;
    }

    // Not found
    NSLog(@"%@ doesn't seem to have a viewController". self);
    return nil;
}

4

Najprostsza pętla do while, aby znaleźć viewController.

-(UIViewController*)viewController
{
    UIResponder *nextResponder =  self;

    do
    {
        nextResponder = [nextResponder nextResponder];

        if ([nextResponder isKindOfClass:[UIViewController class]])
            return (UIViewController*)nextResponder;

    } while (nextResponder != nil);

    return nil;
}

4

Szybki 4

(bardziej zwięzłe niż inne odpowiedzi)

fileprivate extension UIView {

  var firstViewController: UIViewController? {
    let firstViewController = sequence(first: self, next: { $0.next }).first(where: { $0 is UIViewController })
    return firstViewController as? UIViewController
  }

}

Mój przypadek użycia, dla których trzeba uzyskać dostęp do widoku pierwszy UIViewController: Mam obiekt, który owija się wokół AVPlayer/ AVPlayerViewControlleri chcę, aby zapewnić prostą show(in view: UIView)metodę, która osadzi AVPlayerViewControllersię view. Do tego muszę dostępu view„s UIViewController.


3

To nie odpowiada bezpośrednio na pytanie, ale raczej zakłada założenie dotyczące pytania.

Jeśli masz widok i w tym widoku musisz wywołać metodę na innym obiekcie, np. Kontrolerze widoku, możesz zamiast tego użyć NSNotificationCenter.

Najpierw utwórz ciąg powiadomień w pliku nagłówkowym

#define SLCopyStringNotification @"ShaoloCopyStringNotification"

Twoim zdaniem zadzwoń do postNotificationName:

- (IBAction) copyString:(id)sender
{
    [[NSNotificationCenter defaultCenter] postNotificationName:SLCopyStringNotification object:nil];
}

Następnie w kontrolerze widoku dodajesz obserwatora. Robię to w viewDidLoad

- (void)viewDidLoad
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(copyString:)
                                                 name:SLCopyStringNotification
                                               object:nil];
}

Teraz (także w tym samym kontrolerze widoku) zaimplementuj metodę copyString: jak pokazano na @selector powyżej.

- (IBAction) copyString:(id)sender
{
    CalculatorResult* result = (CalculatorResult*)[[PercentCalculator sharedInstance].arrayTableDS objectAtIndex:([self.viewTableResults indexPathForSelectedRow].row)];
    UIPasteboard *gpBoard = [UIPasteboard generalPasteboard];
    [gpBoard setString:result.stringResult];
}

Nie twierdzę, że jest to właściwy sposób, aby to zrobić, wydaje się po prostu czystsze niż uruchamianie pierwszego łańcucha odpowiedzi. Użyłem tego kodu, aby zaimplementować UIMenuController na UITableView i przekazać zdarzenie z powrotem do UIViewController, aby móc coś zrobić z danymi.


3

To z pewnością zły pomysł i zły projekt, ale jestem pewien, że wszyscy możemy cieszyć się szybkim rozwiązaniem najlepszej odpowiedzi zaproponowanej przez @Phil_M:

static func firstAvailableUIViewController(fromResponder responder: UIResponder) -> UIViewController? {
    func traverseResponderChainForUIViewController(responder: UIResponder) -> UIViewController? {
        if let nextResponder = responder.nextResponder() {
            if let nextResp = nextResponder as? UIViewController {
                return nextResp
            } else {
                return traverseResponderChainForUIViewController(nextResponder)
            }
        }
        return nil
    }

    return traverseResponderChainForUIViewController(responder)
}

Jeśli masz zamiar robić proste rzeczy, takie jak wyświetlanie modalnego okna dialogowego lub danych śledzenia, nie uzasadnia to użycia protokołu. Osobiście przechowuję tę funkcję w obiekcie użytkowym, możesz użyć jej ze wszystkiego, co implementuje protokół UIResponder jako:

if let viewController = MyUtilityClass.firstAvailableUIViewController(self) {}

Wszystkie podziękowania dla @Phil_M


3

Może się tu spóźniam. Ale w tej sytuacji nie lubię kategorii (zanieczyszczenia). Kocham w ten sposób:

#define UIViewParentController(__view) ({ \
UIResponder *__responder = __view; \
while ([__responder isKindOfClass:[UIView class]]) \
__responder = [__responder nextResponder]; \
(UIViewController *)__responder; \
})

3

Szybsze rozwiązanie

extension UIView {
    var parentViewController: UIViewController? {
        for responder in sequence(first: self, next: { $0.next }) {
            if let viewController = responder as? UIViewController {
                return viewController
            }
        }
        return nil
    }
}

Podoba mi się ta odpowiedź. Nie jestem jednak pewien, czy to jest szybsze. Swift to jeszcze jeden inny język, który nie może zdecydować, który z wielu paradygmatów chce się ożenić z językami. W tym przypadku masz fajną demonstrację bardziej funkcjonalnego podejścia ( sequencenieco). Więc jeśli „szybki” oznacza „bardziej funkcjonalny”, to myślę, że jest bardziej szybki.
Travis Griggs

2

Zaktualizowana wersja dla swift 4: Dzięki za @Phil_M i @ paul-slm

static func firstAvailableUIViewController(fromResponder responder: UIResponder) -> UIViewController? {
    func traverseResponderChainForUIViewController(responder: UIResponder) -> UIViewController? {
        if let nextResponder = responder.next {
            if let nextResp = nextResponder as? UIViewController {
                return nextResp
            } else {
                return traverseResponderChainForUIViewController(responder: nextResponder)
            }
        }
        return nil
    }

    return traverseResponderChainForUIViewController(responder: responder)
}

2

Wersja Swift 4

extension UIView {
var parentViewController: UIViewController? {
    var parentResponder: UIResponder? = self
    while parentResponder != nil {
        parentResponder = parentResponder!.next
        if let viewController = parentResponder as? UIViewController {
            return viewController
        }
    }
    return nil
}

Przykład użycia

 if let parent = self.view.parentViewController{

 }

2

Dwa rozwiązania od Swift 5.2 :

  • Więcej na stronie funkcjonalnej
  • returnTeraz słowo kluczowe nie jest potrzebne 🤓

Rozwiązanie 1:

extension UIView {
    var parentViewController: UIViewController? {
        sequence(first: self) { $0.next }
            .first(where: { $0 is UIViewController })
            .flatMap { $0 as? UIViewController }
    }
}

Rozwiązanie 2:

extension UIView {
    var parentViewController: UIViewController? {
        sequence(first: self) { $0.next }
            .compactMap{ $0 as? UIViewController }
            .first
    }
}
  • To rozwiązanie wymaga iteracji najpierw przez każdego respondera, więc może nie być najbardziej wydajne.

1

Na odpowiedź Phila:

W linii: id nextResponder = [self nextResponder]; jeśli self (UIView) nie jest widokiem podrzędnym widoku ViewController, jeśli znasz hierarchię self (UIView), możesz również użyć: id nextResponder = [[self superview] nextResponder];...


0

Moje rozwiązanie prawdopodobnie byłoby uważane za fałszywe, ale miałem podobną sytuację jak majonez (chciałem przełączać widoki w odpowiedzi na gest w EAGLView) i dostałem w ten sposób kontroler widoku EAGL:

EAGLViewController *vc = ((EAGLAppDelegate*)[[UIApplication sharedApplication] delegate]).viewController;

2
Proszę przepisać kod w następujący sposób: EAGLViewController *vc = [(EAGLAppDelegate *)[UIApplication sharedApplication].delegate viewController];.
Jonathan Sterling

1
Problem nie dotyczy składni kropek, ale typów. W celu C zadeklaruj obiekt, który piszesz ClassName *object- gwiazdką.
adubr

Ups ... tak właśnie miałem, ale widżet HTML StackOverflow wygląda tak, jakby myślał, że gwiazdka oznacza kursywę ... Zmieniłem go na blok kodu, teraz wyświetla się poprawnie. Dzięki!
gulchrider

0

Myślę, że zdarza się, że obserwowany musi poinformować obserwatora.

Widzę podobny problem, w którym UIView w UIViewController reaguje na sytuację i musi najpierw powiedzieć swojemu kontrolerowi widoku nadrzędnego, aby ukrył przycisk Wstecz, a następnie po zakończeniu powiedzieć nadrzędnemu kontrolerowi widoku, że musi wyskoczyć ze stosu.

Próbowałem tego z delegatami bez powodzenia.

Nie rozumiem, dlaczego to zły pomysł?


0

Innym łatwym sposobem jest posiadanie własnej klasy widoku i dodanie właściwości kontrolera widoku do klasy widoku. Zwykle kontroler widoku tworzy widok i tam kontroler może ustawić się na właściwość. Zasadniczo jest to zamiast przeszukiwania kontrolera (przy odrobinie włamania), zmuszania kontrolera do ustawienia się na widok - jest to proste, ale ma sens, ponieważ to kontroler „kontroluje” widok.


0

Jeśli nie zamierzasz przesłać tego do App Store, możesz również użyć prywatnej metody UIView.

@interface UIView(Private)
- (UIViewController *)_viewControllerForAncestor;
@end

// Later in the code
UIViewController *vc = [myView _viewControllerForAncestor];

0
var parentViewController: UIViewController? {
    let s = sequence(first: self) { $0.next }
    return s.compactMap { $0 as? UIViewController }.first
}

Chociaż ten kod może odpowiedzieć na pytanie, dobra odpowiedź powinna również wyjaśnić, co robi kod i jak rozwiązuje problem.
BDL

0

Aby uzyskać kontroler danego widoku, można użyć łańcucha UIFirstResponder.

customView.target(forAction: Selector("viewDidLoad"), withSender: nil)

-1

Jeśli Twoim rootViewController jest UINavigationViewController, który został skonfigurowany w klasie AppDelegate, to

    + (UIViewController *) getNearestViewController:(Class) c {
NSArray *arrVc = [[[[UIApplication sharedApplication] keyWindow] rootViewController] childViewControllers];

for (UIViewController *v in arrVc)
{
    if ([v isKindOfClass:c])
    {
        return v;
    }
}

return nil;}

Gdzie c wymagało obejrzenia klasy kontrolerów.

STOSOWANIE:

     RequiredViewController* rvc = [Utilities getNearestViewController:[RequiredViewController class]];

-5

Nie ma mowy.

To, co robię, to przekazanie wskaźnika UIViewController do UIView (lub odpowiedniego dziedziczenia). Przepraszam, że nie mogę pomóc w podejściu IB do problemu, ponieważ nie wierzę w IB.

Aby odpowiedzieć na pierwszy komentator: czasami musisz wiedzieć, kto do ciebie zadzwonił, ponieważ określa, co możesz zrobić. Na przykład z bazą danych możesz mieć dostęp tylko do odczytu lub odczyt / zapis ...


10
Co to znaczy - „nie wierzę w IB”? Uruchomiłem go, na pewno istnieje.
marcc

5
Potrzebujesz lepszego zrozumienia zabawy i abstrakcji, szczególnie w odniesieniu do języka angielskiego. To znaczy, że mi się nie podoba.
John Smith

4
Nie lubię awokado. Ale założę się, że mogę pomóc komuś zrobić guacamole. Można to zrobić w IB, więc oczywiście twoja odpowiedź „nie ma mowy” jest nieprawidłowa. To, czy lubisz IB, czy nie, nie ma znaczenia.
Feloneous Cat
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.