UIView에서 UIViewController에 접근 하시겠습니까?


185

UIView에서 그것 으로 갈 수있는 내장 된 방법 이 UIViewController있습니까? 나는 당신이에서 얻을 수 알고 UIViewController그에게 UIView통한 [self view]하지만 역 참조가 있는지 궁금 해서요?

답변:


46

이것이 오랫동안 받아 들여진 대답이므로 더 나은 대답으로 수정해야한다고 생각합니다.

필요성에 대한 의견 :

  • 뷰가 뷰 컨트롤러에 직접 액세스 할 필요는 없습니다.
  • 대신 뷰는 뷰 컨트롤러와 독립적이어야하며 다른 컨텍스트에서 작동 할 수 있어야합니다.
  • 뷰 컨트롤러와 인터페이싱하기 위해 뷰가 필요한 경우 권장되는 방법과 Cocoa에서 Apple이하는 일은 델리게이트 패턴을 사용하는 것입니다.

구현 방법의 예는 다음과 같습니다.

@protocol MyViewDelegate < NSObject >

- (void)viewActionHappened;

@end

@interface MyView : UIView

@property (nonatomic, assign) MyViewDelegate delegate;

@end

@interface MyViewController < MyViewDelegate >

@end

뷰는 델리게이트와 인터페이스하고 ( UITableView예를 들어) 뷰 컨트롤러 또는 다른 클래스에서 구현되었는지는 중요하지 않습니다.

내 원래 답변은 다음과 같습니다. 권장하지 않습니다. 뷰 컨트롤러에 직접 액세스 할 수있는 나머지 답변도 없습니다.

기본 제공 방법이 없습니다. 당신이를 추가하여 주위를 얻을 수 있지만 IBOutlet상의 UIView와 인터페이스 빌더에서 이러한 연결이 권장되지 않습니다. 뷰는 뷰 컨트롤러에 대해 알 수 없습니다. 대신 @Phil M이 제안한대로 대리자로 사용할 프로토콜을 만들어야합니다.


26
그것은 매우 나쁜 조언입니다. 뷰에서 뷰 컨트롤러를 참조하면 안됩니다.
Philippe Leybaert

6
@MattDiPasquale : 예, 디자인이 좋지 않습니다.
Philippe Leybaert 2016 년

23
@Phillipe Leybaert 컨트롤러에 대한 참조를 보유하지 않고 컨트롤러에서 조치를 호출 해야하는 버튼 클릭 이벤트에 대한 최상의 디자인에 대한 귀하의 생각을 알고 싶습니다. 감사합니다
조나단 Horsman

3
@PhilippeLeybaert Apple의 예제 프로젝트는 특정 API 기능의 사용법을 보여주는 경향이 있습니다. 많은 사람들이 주제에 대한 간결한 설명을 제공하기 위해 우수하거나 확장 가능한 디자인을 희생한다고 언급했습니다. 이것을 깨닫는 데 오랜 시간이 걸렸고 말이 되긴하지만 불행한 일입니다. 많은 개발자들이 이러한 실용적인 프로젝트를 Apple의 베스트 디자인 실습 가이드로 생각합니다.
Benjohn

11
"당신은 이것을해서는 안됩니다"에 대한이 모든 BS는 단지 그 것입니다. 뷰가 뷰 컨트롤러에 대해 알고 싶다면 프로그래머가 결정해야합니다. 기간.
Daniel Kanaan

203

Brock이 게시 한 예제를 사용하여 UIViewController 대신 UIView 범주로 변경하고 하위 뷰가 부모 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

이 코드를 사용하려면 새 클래스 파일 (내 이름은 "UIKit 카테고리")에 추가하고 클래스 데이터를 제거하십시오. @interface를 헤더에 복사하고 @implementation을 .m 파일에 복사하십시오. 그런 다음 프로젝트에서 #import "UIKitCategories.h"를 가져 와서 UIView 코드 내에서 사용하십시오.

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

47
그리고 UIView가 UIViewController를 인식하도록해야하는 한 가지 이유는 모달 뷰 / 대화를 푸시해야하는 사용자 정의 UIView 서브 클래스가있을 때입니다.
Phil M


2
UIView가 모달 뷰를 푸시하는 것이 나쁜 습관이 아닙니까? 나는 지금 이것을하고 있지만 그것이 옳지 않은 일이라고 느낀다 ..
Van Du Tran

6
Phil, 커스텀 뷰는 뷰 컨트롤러가 청취 한 다음 거기에서 푸시하는 delegate 메소드를 호출해야합니다.
malhal

9
나는 단지 몇 점으로 "현명한 사람", 학문적, 인정 된 대답, 실용적이고 더럽고 규칙에 반하여 두 번째 답을 얻는 몇 개의 SO 질문을 좋아합니다 :-)
hariseldon78

114

UIView의 하위 클래스입니다 UIResponder. 를 돌려주는 구현으로 UIResponder메소드 -nextResponder를 배치합니다 nil. 다음과 같이 (어떤 이유로에 대신) UIView문서화되어있는 것처럼이 메소드를 대체합니다 . 뷰 컨트롤러가없는 경우이 메서드는 수퍼 뷰를 반환합니다.UIResponderUIView-nextResponder

이것을 프로젝트에 추가하면 롤 준비가 된 것입니다.

@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

이제 UIView뷰 컨트롤러를 반환하는 작업 방법이 있습니다.


5
응답자와 체인 사이에 응답자 체인에 아무것도없는 경우에만 작동 UIView합니다 UIViewController. 재귀를 특징으로하는 Phil M의 대답은 갈 길입니다.
Olivier

33

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

이미 주어진 여러 답변을 결합하여 구현과 함께 제공합니다.

@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

이 카테고리는 내 ARC 지원의 일부입니다 내가 만드는 모든 응용 프로그램에서 제공하는 정적 라이브러리의 . 여러 번 테스트를 받았으며 문제 나 누수를 찾지 못했습니다.

추신 : 관련보기가 귀하의 하위 클래스 인 경우처럼 카테고리를 사용할 필요가 없습니다. 후자의 경우 메소드를 서브 클래스에 넣으면됩니다.


1
이것이 가장 좋은 대답입니다. 재귀에 대한 필요,이 버전은 잘 최적화되지 않습니다
zeroimpl

12

비록 이것이 pgb가 권장하는 IMHO에 의해 기술적으로 해결 될 수 있지만 , 이것은 디자인 결함입니다. 보기는 컨트롤러를 인식하지 않아도됩니다.


viewController가 뷰 중 하나가 사라지고 viewXXXAppear / viewXXXDisappear 메소드 중 하나를 호출해야한다는 것을 어떻게 알 수 있는지 잘 모르겠습니다.
mahboudz

2
이것이 관찰자 패턴의 기본 개념입니다. 관찰자 (이 경우보기)는 관찰자를 직접 인식하지 않아야합니다. 관찰자는 관심있는 콜백 만 수신해야합니다.
Ushox

12

나는 수정 내가 어떤보기 버튼을 통과 그것의 부모를 얻을 수 등의 레이블을 지정할 수 있도록 답을 UIViewController. 여기 내 코드가 있습니다.

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

스위프트 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)!
    }

편집 2 :-신속한 확장

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

뷰가 하위 뷰인 창에 대한 루트 뷰 컨트롤러에 액세스 할 수 있다는 것을 잊지 마십시오. 거기에서, 예를 들어 내비게이션 뷰 컨트롤러를 사용하고 있고 새 뷰를 푸시하려는 경우 :

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

그러나 먼저 창의 rootViewController 속성을 올바르게 설정해야합니다. 예를 들어 앱 대리자에서 컨트롤러를 처음 만들 때이 작업을 수행하십시오.

-(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];
}

이에 따라 나에게 보인다 애플 문서 때문에 [[self navigationController] view]윈도우의 "주"(하위)보기가되면, rootViewController윈도우의 속성을 설정해야 navigationController하는 제어 "주"는 즉시 볼 수 있습니다.
adubr

6

Ushox를 포함한 이러한 답변은 기술적으로 정확하지만 승인 된 방법은 새로운 프로토콜을 구현하거나 기존 프로토콜을 재사용하는 것입니다. 프로토콜은 관찰자와 관찰자를 격리 시키며, 그 사이에 메일 슬롯을 넣는 것과 같습니다. 실제로, 그것은 pushViewController 메소드 호출을 통해 Gabriel이하는 일입니다. viewController는 navigationController 프로토콜을 준수하기 때문에, view는 navigationController에게 예의 바르게 푸시를 요청하는 것이 적절한 프로토콜임을 "알고있다". 가브리엘의 예제를 사용하고 UINavigationController 프로토콜을 재사용하는 것만으로도 고유 한 프로토콜을 만들 수 있습니다.


6

재사용하려는 작은 구성 요소가있는 상황을 우연히 발견하고 재사용 가능한보기 자체에 코드를 추가했습니다 (실제로 버튼을 여는 버튼 이상은 아닙니다 PopoverController).

이것은 iPad에서 잘 작동하지만 ( UIPopoverController선물 자체는 참조 할 필요가 없습니다 UIViewController) 동일한 코드를 작동시키는 것은 갑자기에서를 참조하는 것을 의미 presentViewController합니다 UIViewController. 일관성이 없습니까?

앞에서 언급했듯이 UIView에 논리를 사용하는 것이 가장 좋은 방법은 아닙니다. 그러나 별도의 컨트롤러에 필요한 몇 줄의 코드를 포장하는 것은 실제로 쓸모가 없다고 생각했습니다.

어느 쪽이든, 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

어떤 경우에 뷰 컨트롤러가 누구인지 알아내는 것이 "나쁜"생각은 아닙니다. 나쁜 생각이 될 수있는 것은이 컨트롤러에 대한 참조를 저장하면 감독이 변경되는대로 변경 될 수 있기 때문입니다. 제 경우에는 응답자 체인을 가로 지르는 게터가 있습니다.

//.h

@property (nonatomic, readonly) UIViewController * viewController;

//.미디엄

- (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

viewController를 찾기위한 가장 간단한 while 루프입니다.

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

    do
    {
        nextResponder = [nextResponder nextResponder];

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

    } while (nextResponder != nil);

    return nil;
}

4

스위프트 4

(다른 답변보다 더 간결합니다)

fileprivate extension UIView {

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

}

내 유스 케이스는 내가 먼저보기에 액세스 할 필요가 UIViewController: 그 랩 주위 물체를 AVPlayer/ AVPlayerViewController나는 간단한 제공 할 show(in view: UIView)포함됩니다 방법 AVPlayerViewController에를 view. 이를 위해, 내가 액세스해야 view'이야 UIViewController.


3

이것은 질문에 직접 대답하는 것이 아니라 질문의 의도에 대한 가정을합니다.

뷰가 있고 해당 뷰에서 뷰 컨트롤러와 같은 다른 객체에서 메소드를 호출해야하는 경우 NSNotificationCenter를 대신 사용할 수 있습니다.

먼저 헤더 파일에 알림 문자열을 작성하십시오.

#define SLCopyStringNotification @"ShaoloCopyStringNotification"

viewcall postNotificationName에서 :

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

그런 다음 뷰 컨트롤러에서 관찰자를 추가합니다. viewDidLoad 에서이 작업을 수행합니다.

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

이제 동일한 뷰 컨트롤러에서도 위의 @selector에 표시된 것처럼 copyString : 메소드를 구현하십시오.

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

나는 이것이 올바른 방법이라고 말하지 않고 첫 번째 응답자 체인을 실행하는 것보다 더 깨끗해 보입니다. 이 코드를 사용하여 UITableView에서 UIMenuController를 구현하고 이벤트를 다시 UIViewController로 전달하여 데이터로 무언가를 할 수 있습니다.


3

분명히 나쁜 생각과 잘못된 디자인이지만 @Phil_M이 제안한 최고의 답변의 Swift 솔루션을 모두 즐길 수 있다고 확신합니다.

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)
}

모달 대화 상자를 표시하거나 데이터를 추적하는 것처럼 간단한 작업을 수행하려는 경우 프로토콜 사용이 정당화되지 않습니다. 개인적 으로이 함수를 유틸리티 객체에 저장하면 UIResponder 프로토콜을 구현하는 모든 항목에서 다음과 같이 사용할 수 있습니다.

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

@Phil_M의 모든 크레딧


3

내가 늦었을지도 몰라 그러나이 상황에서 나는 카테고리 (오염)를 좋아하지 않습니다. 나는이 방법을 좋아한다 :

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

3

스위프 티어 솔루션

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

나는이 답변을 좋아한다. 그래도 더 빠르지는 않습니다. 스위프트는 아직 다른 패러다임으로 결정될 수없는 또 하나의 언어이다. 이 경우보다 기능적인 접근 방식 ( sequence비트)에 대한 훌륭한 데모가 제공 됩니다. "swifty"가 "더 기능적"을 의미한다면 더 빠르다고 생각합니다.
Travis Griggs

2

swift 4 업데이트 버전 : @Phil_M 및 @ 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

스위프트 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
}

사용 예

 if let parent = self.view.parentViewController{

 }

2

Swift 5.2 기준으로 두 가지 솔루션 :

  • 기능적 측면에 대한 추가 정보
  • return키워드 필요 없음 🤓

해결책 1 :

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

해결책 2 :

extension UIView {
    var parentViewController: UIViewController? {
        sequence(first: self) { $0.next }
            .compactMap{ $0 as? UIViewController }
            .first
    }
}
  • 이 솔루션은 각 응답자를 먼저 반복해야하므로 가장 성능이 좋지 않을 수 있습니다.

1

Phil의 답변 :

줄에 : id nextResponder = [self nextResponder]; self (UIView)가 ViewController 뷰의 하위 뷰가 아닌 경우 self (UIView)의 계층 구조를 알고 있으면 다음을 사용할 수도 있습니다. id nextResponder = [[self superview] nextResponder];...


0

내 솔루션은 일종의 가짜로 간주되지만 mayoneez (EAGLView의 제스처에 응답하여보기를 전환하고 싶었습니다)와 비슷한 상황이 있었고 EAGL의 뷰 컨트롤러를 다음과 같이 얻었습니다.

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

2
다음과 같이 코드를 다시 작성하십시오 EAGLViewController *vc = [(EAGLAppDelegate *)[UIApplication sharedApplication].delegate viewController];.
Jonathan Sterling

1
문제는 도트 구문이 아니라 유형입니다. Objective C에서 ClassName *object별표를 사용하여 작성하는 오브젝트를 선언하십시오 .
adubr

죄송합니다. 실제로는 가지고 있지만 StackOverflow HTML 위젯은 별표가 이탤릭체를 의미한다고 생각하는 것 같습니다 ... 코드 블록으로 변경했는데 이제 올바르게 표시됩니다. 감사!
gulchrider

0

관찰자가 관찰자에게 알려야 할 경우가 있다고 생각합니다.

UIViewController의 UIView가 상황에 응답하는 비슷한 문제가 있으며 먼저 상위 뷰 컨트롤러에 뒤로 버튼을 숨기라고 지시 한 다음 완료되면 상위 뷰 컨트롤러에 스택에서 자신을 팝해야한다고 알려 주어야합니다.

나는 성공하지 못한 대표자들과 함께 이것을 시도 해왔다.

왜 이것이 나쁜 생각인지 이해하지 못합니까?


0

또 다른 쉬운 방법은 자신의 뷰 클래스를 가지고 뷰 클래스에 뷰 컨트롤러의 속성을 추가하는 것입니다. 일반적으로 뷰 컨트롤러는 뷰를 생성하며 컨트롤러가 속성을 설정할 수있는 곳입니다. 기본적으로 컨트롤러를 검색하는 대신 (약간의 해킹으로) 컨트롤러를보기로 설정하는 것이 간단하지만보기를 "제어"하는 컨트롤러이기 때문에 의미가 있습니다.


0

이것을 App Store에 업로드하지 않을 경우 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
}

이 코드가 질문에 대답 할 수 있지만 좋은 대답은 코드의 기능과 문제를 해결하는 방법도 설명해야합니다.
BDL

0

주어진 뷰의 컨트롤러를 얻기 위해 UIFirstResponder 체인을 사용할 수 있습니다.

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

-1

rootViewController가 AppDelegate 클래스에서 설정된 UINavigationViewController 인 경우

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

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

return nil;}

c 필수 뷰 컨트롤러 클래스입니다.

용법:

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

-5

방법이 없다.

내가하는 일은 UIViewController 포인터를 UIView (또는 적절한 상속)에 전달하는 것입니다. IB를 믿지 않기 때문에 문제에 대한 IB 접근 방식을 도울 수 없어서 죄송합니다.

첫 번째 해설자에게 답하기 : 때때로 당신이 할 수있는 일을 결정하기 때문에 누가 당신을 불렀는지 알아야합니다. 예를 들어 데이터베이스를 사용하면 읽기 권한 만 있거나 읽기 / 쓰기가 가능할 수 있습니다.


10
"IB를 믿지 않는다"는 것은 무엇을 의미합니까? 나는 그것을 시작했다, 그것은 확실히 존재한다.
marcc

5
특히 영어와 관련하여 재미와 추상화를 더 잘 이해해야합니다. 내가 싫어한다는 것을 의미합니다.
John Smith

4
나는 아보카도를 좋아하지 않는다. 그러나 나는 누군가가 아보카도 소스를 만들도록 도울 수 있습니다. IB에서 수행 할 수 있으므로 "방법이 없습니다"라는 대답이 잘못되었습니다. 당신이 IB를 좋아하든 아니든 상관 없습니다.
Feloneous Cat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.