질문자 :Matt Price
저는 iOS 및 Objective-C와 전체 MVC 패러다임을 처음 접했고 다음과 같은 문제에 봉착했습니다.
데이터 입력 양식 역할을 하는 보기가 있고 사용자에게 여러 제품을 선택할 수 있는 옵션을 제공하고 싶습니다. UITableViewController
사용하여 다른 보기에 나열되며 다중 선택을 활성화했습니다.
한 보기에서 다른 보기로 데이터를 전송하려면 어떻게 합니까? UITableView
에서 선택 항목을 유지하지만 양식 제출 시 다른 데이터와 함께 Core Data에 저장할 수 있도록 이전 데이터 입력 양식 보기로 다시 전달하려면 어떻게 해야 합니까?
나는 주변을 서핑했고 일부 사람들이 앱 대리자에서 배열을 선언하는 것을 보았습니다. 싱글톤 에 대한 내용을 읽었지만 이것이 무엇인지 이해하지 못하고 데이터 모델 생성에 대한 내용을 읽었습니다.
이를 수행하는 올바른 방법은 무엇이며 어떻게 해야 합니까?
이 질문은 여기 스택 오버플로에서 매우 인기가 있는 것 같아서 저와 같은 iOS의 세계를 시작하는 사람들을 돕기 위해 더 나은 답변을 제공하려고 노력했습니다.
이 답변이 사람들이 이해할 수 있을 만큼 명확하고 제가 놓친 것이 없기를 바랍니다.
데이터 전달
다른 뷰 컨트롤러에서 뷰 컨트롤러로 데이터를 전달합니다. 하나의 뷰 컨트롤러에서 탐색 스택으로 푸시할 수 있는 다른 뷰 컨트롤러로 객체/값을 전달하려는 경우 이 방법을 사용합니다.
이 예에서는 ViewControllerA
및 ViewControllerB
패스하려면 BOOL
에서 값 ViewControllerA
에 ViewControllerB
우리는 다음을 할 것입니다.
ViewControllerB.h
BOOL
대한 속성을 만듭니다.
@property (nonatomic, assign) BOOL isSomethingEnabled;
에 ViewControllerA
당신은 그것에 대해 말할 필요 ViewControllerB
사용 SO를
#import "ViewControllerB.h"
그런 다음 뷰를 로드하려는 위치(예: didSelectRowAtIndex
또는 일부 IBAction
)를 탐색 스택에 푸시하기 전에 ViewControllerB
에서 속성을 설정해야 합니다.
ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil]; viewControllerB.isSomethingEnabled = YES; [self pushViewController:viewControllerB animated:YES];
이것은 ViewControllerB
isSomethingEnabled
를 BOOL
값 YES
합니다.
Segues를 사용하여 데이터 전달하기
Storyboard를 사용하는 경우 segue를 사용할 가능성이 가장 높으며 데이터를 전달하려면 이 절차가 필요합니다. 위와 비슷하지만 뷰 컨트롤러를 푸시하기 전에 데이터를 전달하는 대신
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
그래서 전달하는 BOOL
에서 ViewControllerA
에 ViewControllerB
우리는 다음을 수행 할을 :
ViewControllerB.h
BOOL
대한 속성을 만듭니다.
@property (nonatomic, assign) BOOL isSomethingEnabled;
에 ViewControllerA
당신은 그것에 대해 말할 필요 ViewControllerB
를 사용 SO를
#import "ViewControllerB.h"
에서 SEGUE 만들기 ViewControllerA
에 ViewControllerB
스토리 보드를하고 그것을 식별자를 제공합니다. "showDetailSegue"
부를 것입니다.
다음으로 Segue가 수행될 때 호출 ViewControllerA
메서드를 추가해야 합니다. 이 때문에 우리는 어떤 segue가 호출되었는지 감지한 다음 조치를 취해야 합니다. 이 예에서는 "showDetailSegue"
확인하고 이것이 수행되면 BOOL
값을 ViewControllerB
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{ if([segue.identifier isEqualToString:@"showDetailSegue"]){ ViewControllerB *controller = (ViewControllerB *)segue.destinationViewController; controller.isSomethingEnabled = YES; } }
탐색 컨트롤러에 뷰가 포함되어 있는 경우 위의 방법을 다음과 같이 약간 변경해야 합니다.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{ if([segue.identifier isEqualToString:@"showDetailSegue"]){ UINavigationController *navController = (UINavigationController *)segue.destinationViewController; ViewControllerB *controller = (ViewControllerB *)navController.topViewController; controller.isSomethingEnabled = YES; } }
이것은 ViewControllerB
isSomethingEnabled
를 BOOL
값 YES
합니다.
데이터 다시 전달
에서 데이터를 다시 전달하기 위해 ViewControllerB
에 ViewControllerA
당신이 프로토콜 및 대표자 또는 블록을 사용할 필요를, 후자는 콜백 느슨하게 결합 메커니즘으로 사용할 수 있습니다.
이를 위해 우리는 할 것입니다 ViewControllerA
의 위임 ViewControllerB
. 이를 통해 ViewControllerB
에 메시지를 다시 보낼 ViewControllerA
데이터를 다시 보내 우리를 가능하게한다.
들어 ViewControllerA
의 대리자로 ViewControllerB
가 준수해야 ViewControllerB
우리가 지정해야의 프로토콜입니다. ViewControllerA
가 구현해야 하는 메소드를 알려줍니다.
ViewControllerB.h
에서 #import
아래에 있지만 @interface
위에는 프로토콜을 지정합니다.
@class ViewControllerB; @protocol ViewControllerBDelegate <NSObject> - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item; @end
다음으로 여전히 ViewControllerB.h
delegate
속성을 설정하고 ViewControllerB.m에서 합성해야 ViewControllerB.m
@property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
ViewControllerB
에서 뷰 컨트롤러를 delegate
때 대리자에 대한 메시지를 호출합니다.
NSString *itemToPassBack = @"Pass this value back to ViewControllerA"; [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
ViewControllerB
대한 것입니다. 지금에 ViewControllerA.h
, 말 ViewControllerA
수입에 ViewControllerB
과 프로토콜을 준수합니다.
#import "ViewControllerB.h" @interface ViewControllerA : UIViewController <ViewControllerBDelegate>
ViewControllerA.m
에서 프로토콜에서 다음 메서드를 구현합니다.
- (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item { NSLog(@"This was returned from ViewControllerB %@", item); }
viewControllerB
를 탐색 스택에 푸시하기 전에 ViewControllerB
에 ViewControllerA
가 대리자임을 알려야 합니다. 그렇지 않으면 오류가 발생합니다.
ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil]; viewControllerB.delegate = self [[self navigationController] pushViewController:viewControllerB animated:YES];
참고문헌
- View Controller 프로그래밍 가이드 에서 위임을 사용하여 다른 View Controller와 통신
- 대리자 패턴
NS알림센터
데이터를 전달하는 또 다른 방법입니다.
// Add an observer in controller(s) where you want to receive data [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeepLinking:) name:@"handleDeepLinking" object:nil]; -(void) handleDeepLinking:(NSNotification *) notification { id someObject = notification.object // Some custom object that was passed with notification fire. } // Post notification id someObject; [NSNotificationCenter.defaultCenter postNotificationName:@"handleDeepLinking" object:someObject];
한 클래스에서 다른 클래스로 데이터를 다시 전달 (클래스는 컨트롤러, 네트워크/세션 관리자, UIView 하위 클래스 또는 기타 클래스일 수 있음)
블록은 익명 함수입니다.
이 예에서는 컨트롤러 B 에서 컨트롤러 A로 데이터를 전달합니다.
블록 정의
@property void(^selectedVoucherBlock)(NSString *); // in ContollerA.h
블록 처리기(리스너) 추가
값이 필요한 경우(예: ControllerA에 API 응답이 필요하거나 A에 ContorllerB 데이터가 필요한 경우)
// In ContollerA.m - (void)viewDidLoad { [super viewDidLoad]; __unsafe_unretained typeof(self) weakSelf = self; self.selectedVoucherBlock = ^(NSString *voucher) { weakSelf->someLabel.text = voucher; }; }
컨트롤러 B로 이동
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil]; ControllerB *vc = [storyboard instantiateViewControllerWithIdentifier:@"ControllerB"]; vc.sourceVC = self; [self.navigationController pushViewController:vc animated:NO];
파이어 블록
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath { NSString *voucher = vouchersArray[indexPath.row]; if (sourceVC.selectVoucherBlock) { sourceVC.selectVoucherBlock(voucher); } [self.navigationController popToViewController:sourceVC animated:YES]; }
블록에 대한 또 다른 작업 예
Matt Price빠른
여기와 Stack Overflow에 대한 설명이 수없이 많지만, 기본적인 작업을 수행하려는 초보자라면 이 YouTube 튜토리얼을 시청해 보세요.
다음 View Controller로 데이터 전달
다음은 비디오를 기반으로 한 예입니다. 아이디어는 첫 번째 보기 컨트롤러의 텍스트 필드에서 두 번째 보기 컨트롤러의 레이블로 문자열을 전달하는 것입니다.
Interface Builder에서 스토리보드 레이아웃을 만듭니다. segue를 만들려면 버튼을 Control 클릭하고 Second View Controller로 드래그하면 됩니다.
첫 번째 보기 컨트롤러
첫 번째 보기 컨트롤러의 코드는 다음과 같습니다.
import UIKit class FirstViewController: UIViewController { @IBOutlet weak var textField: UITextField! // This function is called before the segue override func prepare(for segue: UIStoryboardSegue, sender: Any?) { // Get a reference to the second view controller let secondViewController = segue.destination as! SecondViewController // Set a variable in the second view controller with the String to pass secondViewController.receivedString = textField.text! } }
두 번째 보기 컨트롤러
그리고 두 번째 보기 컨트롤러의 코드는 다음과 같습니다.
import UIKit class SecondViewController: UIViewController { @IBOutlet weak var label: UILabel! // This variable will hold the data being passed from the First View Controller var receivedString = "" override func viewDidLoad() { super.viewDidLoad() // Used the text from the First View Controller to set the label label.text = receivedString } }
잊지마
-
UITextField
및 UILabel
대한 콘센트를 연결합니다. - Interface Builder 에서 첫 번째 및 두 번째 View Controller를 적절한 Swift 파일로 설정합니다.
데이터를 이전 View Controller로 다시 전달
두 번째 뷰 컨트롤러에서 첫 번째 뷰 컨트롤러로 데이터를 다시 전달하려면 프로토콜과 대리자 를 사용합니다. 이 비디오는 그 과정을 매우 명확하게 보여줍니다.
다음은 비디오를 기반으로 한 예입니다(몇 가지 수정 포함).
Interface Builder에서 스토리보드 레이아웃을 만듭니다. 다시 말하지만, segue를 만들려면 버튼에서 Second View Controller로 Control 끌기만 하면 됩니다. segue 식별자를 showSecondViewController
설정합니다. 또한 다음 코드의 이름을 사용하여 콘센트와 작업을 연결하는 것을 잊지 마십시오.
첫 번째 보기 컨트롤러
첫 번째 보기 컨트롤러의 코드는 다음과 같습니다.
import UIKit class FirstViewController: UIViewController, DataEnteredDelegate { @IBOutlet weak var label: UILabel! override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "showSecondViewController" { let secondViewController = segue.destination as! SecondViewController secondViewController.delegate = self } } func userDidEnterInformation(info: String) { label.text = info } }
DataEnteredDelegate
프로토콜의 사용에 유의하십시오.
두 번째 보기 컨트롤러 및 프로토콜
두 번째 뷰 컨트롤러의 코드는 다음과 같습니다.
import UIKit // Protocol used for sending data back protocol DataEnteredDelegate: AnyObject { func userDidEnterInformation(info: String) } class SecondViewController: UIViewController { // Making this a weak variable, so that it won't create a strong reference cycle weak var delegate: DataEnteredDelegate? = nil @IBOutlet weak var textField: UITextField! @IBAction func sendTextBackButton(sender: AnyObject) { // Call this method on whichever class implements our delegate protocol delegate?.userDidEnterInformation(info: textField.text!) // Go back to the previous view controller _ = self.navigationController?.popViewController(animated: true) } }
protocol
은 View Controller 클래스 외부에 있습니다.
그게 다야 지금 앱을 실행하면 두 번째 뷰 컨트롤러에서 첫 번째 뷰 컨트롤러로 데이터를 다시 보낼 수 있습니다.
SuragchMVC의 M은 "모델"을 위한 것이고 MVC 패러다임에서 모델 클래스의 역할은 프로그램의 데이터를 관리하는 것입니다. 모델은 뷰의 반대입니다. 뷰는 데이터를 표시하는 방법을 알고 있지만 데이터로 무엇을 해야 하는지에 대해서는 아무것도 알지 못하는 반면, 모델은 데이터를 사용하는 방법에 대해서는 모든 것을 알고 있지만 표시하는 방법에 대해서는 아무 것도 모릅니다. 모델은 복잡할 수 있지만 반드시 그럴 필요는 없습니다. 앱의 모델은 문자열이나 사전의 배열만큼 간단할 수 있습니다.
컨트롤러의 역할은 뷰와 모델 사이를 중재하는 것입니다. 따라서 하나 이상의 뷰 개체와 하나 이상의 모델 개체에 대한 참조가 필요합니다. 모델이 사전의 배열이며 각 사전은 테이블의 한 행을 나타냅니다. 앱의 루트 보기는 해당 테이블을 표시하며 파일에서 배열을 로드하는 역할을 할 수 있습니다. 사용자가 테이블에 새 행을 추가하기로 결정하면 일부 버튼을 탭하면 컨트롤러가 새(변경 가능한) 사전을 생성하여 배열에 추가합니다. 행을 채우기 위해 컨트롤러는 디테일 뷰 컨트롤러를 생성하고 새로운 딕셔너리를 제공합니다. 디테일 뷰 컨트롤러는 사전을 채우고 반환합니다. 사전은 이미 모델의 일부이므로 다른 작업이 필요하지 않습니다.
CalebiOS의 다른 클래스에서 데이터를 수신할 수 있는 다양한 방법이 있습니다. 예를 들어 -
- 다른 클래스 할당 후 직접 초기화.
- 위임 - 데이터를 다시 전달하기 위해
- 알림 - 한 번에 여러 클래스에 데이터 브로드캐스트
-
NSUserDefaults
저장 - 나중에 액세스하기 위해 - 싱글톤 클래스
- p-list 파일 등과 같은 데이터베이스 및 기타 저장 메커니즘
그러나 현재 클래스에서 할당이 수행되는 다른 클래스에 값을 전달하는 간단한 시나리오의 경우 가장 일반적이고 선호되는 방법은 할당 후 값을 직접 설정하는 것입니다. 이것은 다음과 같이 수행됩니다.
Controller1과 Controller2의 두 컨트롤러를 사용하여 이해할 수 있습니다.
Controller1 클래스에서 Controller2 개체를 만들고 전달되는 String 값으로 푸시하려고 한다고 가정합니다. 다음과 같이 할 수 있습니다.
- (void)pushToController2 { Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil]; [obj passValue:@"String"]; [self pushViewController:obj animated:YES]; }
Controller2 클래스의 구현에는 다음과 같은 기능이 있습니다.
@interface Controller2 : NSObject @property (nonatomic, strong) NSString* stringPassed; @end @implementation Controller2 @synthesize stringPassed = _stringPassed; - (void) passValue:(NSString *)value { _stringPassed = value; // Or self.stringPassed = value } @end
다음과 유사한 방식으로 Controller2 클래스의 속성을 직접 설정할 수도 있습니다.
- (void)pushToController2 { Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil]; [obj setStringPassed:@"String"]; [self pushViewController:obj animated:YES]; }
여러 값을 전달하려면 다음과 같은 여러 매개변수를 사용할 수 있습니다.
Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil]; [obj passValue:@“String1” andValues:objArray withDate:date];
또는 공통 기능과 관련된 매개변수를 세 개 이상 전달해야 하는 경우 모델 클래스에 값을 저장하고 해당 modelObject를 다음 클래스에 전달할 수 있습니다.
ModelClass *modelObject = [[ModelClass alloc] init]; modelObject.property1 = _property1; modelObject.property2 = _property2; modelObject.property3 = _property3; Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil]; [obj passmodel: modelObject];
간단히 말해서 원하는 경우 -
- 두 번째 클래스의 개인 변수를 설정하면 사용자 정의 함수를 호출하고 값을 전달하여 값을 초기화합니다.
- setProperties는 setter 메서드를 사용하여 directInitialize하여 이를 수행합니다.
- 어떤 식으로든 서로 관련된 3-4개 이상의 값을 전달한 다음 모델 클래스를 만들고 해당 개체에 값을 설정하고 위의 프로세스 중 하나를 사용하여 개체를 전달합니다.
borncrazy더 많은 연구 후에 프로토콜 과 대리인 이 올바른/Apple이 선호하는 방법인 것으로 나타났습니다.
나는 (iPhone 개발 SDK에서) 이 예제를 사용하게 되었습니다.
뷰 컨트롤러와 다른 객체 간의 데이터 공유
그것은 잘 작동했고 내 보기 사이에서 문자열과 배열을 앞뒤로 전달할 수 있었습니다.
Matt Price나는 블록을 통과하는 가장 간단하고 우아한 버전을 찾습니다. 반환된 데이터를 기다리는 뷰 컨트롤러의 이름을 "A"로 지정하고 반환되는 뷰 컨트롤러의 이름을 "B"로 지정하겠습니다. 이 예에서 우리는 Type1의 첫 번째 값과 Type2의 두 번째 값이라는 2개의 값을 얻고자 합니다.
Storyboard를 사용한다고 가정하면 첫 번째 컨트롤러는 예를 들어 segue 준비 중에 콜백 블록을 설정합니다.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([segue.destinationViewController isKindOfClass:[BViewController class]]) { BViewController *viewController = segue.destinationViewController; viewController.callback = ^(Type1 *value1, Type2 *value2) { // optionally, close B //[self.navigationController popViewControllerAnimated:YES]; // let's do some action after with returned values action1(value1); action2(value2); }; } }
및 "B" 뷰 컨트롤러는 BViewController.h라는 콜백 속성을 선언해야 합니다.
// it is important to use "copy" @property (copy) void(^callback)(Type1 *value1, Type2 *value2);
콜백을 반환하기 위해 원하는 값을 얻은 후 구현 파일 BViewController.m에서 다음을 호출해야 합니다.
if (self.callback) self.callback(value1, value2);
한 가지 기억해야 할 점은 블록을 사용하면 여기에 설명된 것처럼 강력하고 __약한 참조를 관리해야 하는 경우가 많다는 것입니다.
Leszek Zarna주어진 많은 답변에 좋은 정보가 있지만 질문을 완전히 해결하는 것은 없습니다.
이 질문은 뷰 컨트롤러 간에 정보를 전달하는 방법에 대해 묻습니다. 주어진 특정 예는 뷰 간에 정보를 전달하는 것에 대해 묻지만 iOS에 대한 자체적으로 새로움을 감안할 때 원본 포스터는 뷰 사이가 아니라 뷰 컨트롤러 사이를 의미했을 가능성이 높습니다(ViewController의 개입 없이). 모든 답변이 두 개의 보기 컨트롤러에 초점을 맞추는 것처럼 보이지만 앱이 정보 교환에 두 개 이상의 보기 컨트롤러를 포함해야 하도록 진화한다면 어떻게 될까요?
원본 포스터는 싱글톤 과 AppDelegate 사용에 대해서도 질문했습니다. 이러한 질문에 답해야 합니다.
완전한 답변을 원하는 이 질문을 보고 있는 다른 사람을 돕기 위해 제공하려고 합니다.
애플리케이션 시나리오
매우 가설적이고 추상적인 토론을 하는 것보다 구체적인 적용을 염두에 두는 것이 도움이 됩니다. 보기 컨트롤러가 2개인 상황과 보기 컨트롤러가 2개 이상인 상황을 정의하는 데 도움이 되도록 두 가지 구체적인 응용 프로그램 시나리오를 정의하겠습니다.
시나리오 1: 최대 두 개의 보기 컨트롤러가 정보를 공유해야 합니다.
다이어그램 1을 참조하십시오.
응용 프로그램에는 두 개의 보기 컨트롤러가 있습니다. ViewControllerA(데이터 입력 양식)와 View Controller B(제품 목록)가 있습니다. 제품 목록에서 선택한 항목은 데이터 입력 양식의 텍스트 상자에 표시된 항목과 일치해야 합니다. 이 시나리오에서 ViewControllerA와 ViewControllerB는 다른 보기 컨트롤러가 아닌 서로 직접 통신해야 합니다.
시나리오 2 : 둘 이상의 뷰 컨트롤러가 동일한 정보를 공유해야 합니다.
다이어그램 2를 참조하십시오.
애플리케이션에는 4개의 뷰 컨트롤러가 있습니다. 주택 재고를 관리하기 위한 탭 기반 애플리케이션입니다. 세 가지 보기 컨트롤러는 동일한 데이터에 대해 다르게 필터링된 보기를 제공합니다.
- ViewControllerA - 사치품
- ViewControllerB - 비보험 품목
- ViewControllerC - 전체 집 인벤토리
- ViewControllerD - 새 항목 추가 양식
개별 항목이 생성되거나 편집될 때마다 다른 뷰 컨트롤러와도 동기화되어야 합니다. 예를 들어 ViewControllerD에 보트를 추가했지만 아직 보험에 들지 않은 경우 사용자가 ViewControllerA(럭셔리 품목) 및 ViewControllerC(전체 홈 인벤토리)로 이동할 때 보트가 표시되어야 하지만 사용자가 다음으로 이동할 때는 보트가 표시되지 않습니다. ViewControllerB(비보험 품목). 우리는 새 항목을 추가할 뿐만 아니라 항목 삭제(4개의 보기 컨트롤러 중 하나에서 허용될 수 있음) 또는 기존 항목 편집("새 항목 추가 양식"에서 허용할 수 있는 동일한 용도로 사용할 수 있음)에 관심을 가질 필요가 있습니다. 편집용).
모든 뷰 컨트롤러는 동일한 데이터를 공유해야 하므로 4개의 뷰 컨트롤러 모두 동기화 상태를 유지해야 하므로 단일 뷰 컨트롤러가 기본 데이터를 변경할 때마다 다른 모든 뷰 컨트롤러와 일종의 통신이 필요합니다. 이 시나리오에서 각 뷰 컨트롤러가 서로 직접 통신하는 것을 원하지 않는다는 것은 상당히 분명해야 합니다. 명확하지 않은 경우 4개가 아닌 20개의 다른 뷰 컨트롤러가 있는지 고려하십시오. 하나의 뷰 컨트롤러가 변경될 때마다 다른 19개의 뷰 컨트롤러에 각각 알리는 것이 얼마나 어렵고 오류가 발생하기 쉬운 일입니까?
솔루션: 대리자 및 관찰자 패턴, 싱글톤
시나리오 1에는 다른 답변에서 알 수 있듯이 몇 가지 실행 가능한 솔루션이 있습니다.
- 세그
- 대의원
- 뷰 컨트롤러에서 직접 속성 설정
- NSUserDefaults(실제로는 잘못된 선택)
시나리오 2에는 다른 실행 가능한 솔루션이 있습니다.
싱글톤 은 클래스의 인스턴스이며 해당 인스턴스는 수명 동안 존재하는 유일한 인스턴스입니다. 싱글톤은 단일 인스턴스라는 사실에서 이름을 얻습니다. 일반적으로 싱글톤을 사용하는 개발자는 싱글톤에 액세스하기 위한 특수 클래스 메서드가 있습니다.
+ (HouseholdInventoryManager*) sharedManager; { static dispatch_once_t onceQueue; static HouseholdInventoryManager* _sharedInstance; // dispatch_once is guaranteed to only be executed // once in the lifetime of the application dispatch_once(&onceQueue, ^{ _sharedInstance = [[self alloc] init]; }); return _sharedInstance; }
이제 싱글톤이 무엇인지 이해했으므로 싱글톤이 관찰자 패턴에 어떻게 맞는지 논의해 보겠습니다. 관찰자 패턴은 한 개체가 다른 개체의 변경 사항에 응답하는 데 사용됩니다. 두 번째 시나리오에는 기본 데이터의 변경 사항에 대해 모두 알고 싶어하는 4개의 서로 다른 뷰 컨트롤러가 있습니다. "기본 데이터"는 단일 인스턴스인 싱글톤에 속해야 합니다. "변경 사항에 대한 정보"는 싱글톤에 대한 변경 사항을 관찰하여 수행됩니다.
가정 인벤토리 애플리케이션에는 인벤토리 항목 목록을 관리하도록 설계된 클래스의 단일 인스턴스가 있습니다. 관리자는 가정 용품 컬렉션을 관리합니다. 다음은 데이터 관리자에 대한 클래스 정의입니다.
#import <Foundation/Foundation.h> @class JGCHouseholdInventoryItem; @interface HouseholdInventoryManager : NSObject /*! The global singleton for accessing application data */ + (HouseholdInventoryManager*) sharedManager; - (NSArray *) entireHouseholdInventory; - (NSArray *) luxuryItems; - (NSArray *) nonInsuredItems; - (void) addHouseholdItemToHomeInventory:(JGCHouseholdInventoryItem*)item; - (void) editHouseholdItemInHomeInventory:(JGCHouseholdInventoryItem*)item; - (void) deleteHoueholdItemFromHomeInventory:(JGCHouseholdInventoryItem*)item; @end
홈 인벤토리 항목 컬렉션이 변경되면 뷰 컨트롤러가 이 변경 사항을 인식해야 합니다. 위의 클래스 정의는 이것이 어떻게 일어날지 명확하지 않습니다. 우리는 관찰자 패턴을 따라야 합니다. 뷰 컨트롤러는 공식적으로 sharedManager를 관찰해야 합니다. 다른 개체를 관찰하는 방법에는 두 가지가 있습니다.
- 키-값 관찰(KVO)
- NSNotificationCenter.
시나리오 2에서는 KVO를 사용하여 관찰할 수 있는 HouseholdInventoryManager의 단일 속성이 없습니다. 우리는 쉽게 관찰할 수 있는 단일 속성이 없기 때문에 이 경우 관찰자 패턴은 NSNotificationCenter를 사용하여 구현해야 합니다. 4개의 뷰 컨트롤러 각각은 알림을 구독하고 sharedManager는 적절한 경우 알림 센터에 알림을 보냅니다. 인벤토리 관리자는 인벤토리 항목 컬렉션이 변경되는 시점을 알고 싶어할 수 있는 뷰 컨트롤러 또는 다른 클래스의 인스턴스에 대해 알 필요가 없습니다. NSNotificationCenter는 이러한 구현 세부 사항을 처리합니다. View Controller는 단순히 알림을 구독하고 데이터 관리자는 단순히 알림을 게시합니다.
많은 초보자 프로그래머는 전역적으로 액세스할 수 있는 응용 프로그램 수명 동안 항상 정확히 하나의 응용 프로그램 대리자가 있다는 사실을 이용합니다. 초보 프로그래머는 이 사실을 사용하여 응용 프로그램의 다른 곳에서 액세스할 수 있는 편의를 위해 개체와 기능을 appDelegate에 넣습니다. AppDelegate가 싱글톤이라고 해서 다른 모든 싱글톤을 대체해야 하는 것은 아닙니다. 이것은 한 클래스에 너무 많은 부담을 주어 좋은 객체 지향 관행을 깨뜨리기 때문에 좋지 않은 관행입니다. 각 클래스는 종종 클래스 이름으로 쉽게 설명할 수 있는 명확한 역할을 가지고 있어야 합니다.
Application Delegate가 부풀려지기 시작할 때마다 기능을 싱글톤으로 제거하기 시작하십시오. 예를 들어 Core Data Stack은 AppDelegate에 남겨두어서는 안 되며 대신 자체 클래스인 coreDataManager 클래스에 넣어야 합니다.
참고문헌
Jason CrossViewController 2(대상)에서 viewController 1(소스)로 데이터를 다시 전달하는 것이 더 흥미로운 일입니다. StoryBoard를 사용한다고 가정하면 다음과 같은 방법이 있습니다.
- 대리자
- 공고
- 사용자 기본값
- 하나씩 일어나는 것
그것들은 이미 여기에서 논의되었습니다.
더 많은 방법이 있음을 발견했습니다.
차단 콜백 사용:
prepareForSegue
메소드에서 사용하십시오.
NextViewController *destinationVC = (NextViewController *) segue.destinationViewController; [destinationVC setDidFinishUsingBlockCallback:^(NextViewController *destinationVC) { self.blockLabel.text = destination.blockTextField.text; }];
스토리보드 사용하기(종료)
다음과 같이 VC 1에서 UIStoryboardSegue 인수를 사용하여 메서드를 구현합니다.
-(IBAction)UnWindDone:(UIStoryboardSegue *)segue { }
StoryBoard에서 "return" 버튼을 vc의 녹색 Exit 버튼(Unwind)에 연결합니다. 이제 "되돌아가는" segue가 있으므로 VC2의 prepareForSegue에서 destinationViewController 속성을 사용하고 돌아가기 전에 VC1의 속성을 변경할 수 있습니다.
스토리보드를 사용하는 또 다른 옵션 해제(종료) - VC1에서 작성한 방법을 사용할 수 있습니다.
-(IBAction)UnWindDone:(UIStoryboardSegue *)segue { NextViewController *nextViewController = segue.sourceViewController; self.unwindLabel.text = nextViewController.unwindPropertyPass; }
그리고 VC1의 prepareForSegue에서 공유하려는 속성을 변경할 수 있습니다.
두 가지 해제 옵션 모두에서 버튼의 태그 속성을 설정하고 prepareForSegue에서 확인할 수 있습니다.
YevgeniOP는 뷰 컨트롤러에 대해 언급하지 않았지만 많은 답변이 있으므로 한 뷰 컨트롤러에서 다른 뷰 컨트롤러로 데이터를 전달하고 싶을 때 LLVM의 새로운 기능 중 일부를 사용하여 이를 더 쉽게 만들 수 있다는 점을 알리고 싶었습니다. 일부 결과를 다시 얻고 있습니다.
Storyboard segues, ARC 및 LLVM 블록을 사용하면 이 작업이 그 어느 때보다 쉬워집니다. 위에서 언급한 스토리보드와 segues의 일부 답변은 이미 위임에 의존하고 있습니다. 대리자를 정의하는 것은 확실히 효과가 있지만 일부 사람들은 포인터나 코드 블록을 전달하는 것이 더 쉽다고 생각할 수 있습니다.
UINavigator와 segue를 사용하면 정보를 하위 컨트롤러에 전달하고 정보를 다시 가져오는 쉬운 방법이 있습니다. ARC는 NSObjects에서 파생된 것에 대한 포인터 전달을 간단하게 하므로 하위 컨트롤러가 일부 데이터를 추가/변경/수정하도록 하려면 변경 가능한 인스턴스에 대한 포인터를 전달합니다. 블록을 사용하면 작업을 쉽게 전달할 수 있으므로 하위 컨트롤러가 상위 수준 컨트롤러에서 작업을 호출하도록 하려면 블록을 전달합니다. 당신은 당신에게 의미가있는 인수를 허용하도록 블록을 정의합니다. 여러 블록을 사용하는 것이 더 적합하다면 API를 설계할 수도 있습니다.
다음은 세그 접착제의 두 가지 간단한 예입니다. 첫 번째는 입력을 위해 전달된 하나의 매개변수를 보여주고, 두 번째는 출력을 위해 전달되는 것을 간단하게 보여줍니다.
// Prepare the destination view controller by passing it the input we want it to work on // and the results we will look at when the user has navigated back to this controller's view. - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { [[segue destinationViewController] // This parameter gives the next controller the data it works on. segueHandoffWithInput:self.dataForNextController // This parameter allows the next controller to pass back results // by virtue of both controllers having a pointer to the same object. andResults:self.resultsFromNextController]; }
이 두 번째 예는 두 번째 인수에 대한 콜백 블록을 전달하는 방법을 보여줍니다. 저는 블록을 사용하는 것을 좋아합니다. 왜냐하면 상위 레벨 소스인 소스에서 관련 세부 정보를 서로 가깝게 유지하기 때문입니다.
// Prepare the destination view controller by passing it the input we want it to work on // and the callback when it has done its work. - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { [[segue destinationViewController] // This parameter gives the next controller the data it works on. segueHandoffWithInput:self.dataForNextController // This parameter allows the next controller to pass back results. resultsBlock:^(id results) { // This callback could be as involved as you like. // It can use Grand Central Dispatch to have work done on another thread for example. [self setResultsFromNextController:results]; }]; }
WeakPointer데이터를 공유하는 방법에는 여러 가지가 있습니다.
NSUserDefaults
사용하여 항상 데이터를 공유할 수 있습니다. 선택한 키와 관련하여 공유하려는 값을 설정하고 다음 뷰 컨트롤러에서 해당 키와 연결된 NSUserDefault
[[NSUserDefaults standardUserDefaults] setValue:value forKey:key] [[NSUserDefaults standardUserDefaults] objectForKey:key]
viewcontrollerA
에서 속성을 생성하면 됩니다. viewcontrollerA
에 viewcontrollerB
의 개체를 만들고 해당 속성에 원하는 값을 할당합니다.
이를 위해 사용자 지정 대리자를 만들 수도 있습니다.
Anubrata Santra한 컨트롤러에서 다른 컨트롤러로 데이터를 전달하려면 다음 코드를 시도하십시오.
파일 FirstViewController.h
@property (nonatomic, retain) NSString *str;
SecondViewController.h
@property (nonatomic, retain) NSString *str1;
파일 FirstViewController.m
- (void)viewDidLoad { // Message for the second SecondViewController self.str = @"text message"; [super viewDidLoad]; } -(IBAction)ButtonClicked { SecondViewController *secondViewController = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil]; secondViewController.str1 = str; [self.navigationController pushViewController:secondViewController animated:YES]; }
user2998756이것은 매우 오래된 답변이며 이것은 안티 패턴입니다. 대리인을 이용하시기 바랍니다. 이 방법을 사용하지 마십시오!!
1. 두 번째 뷰 컨트롤러에서 첫 번째 뷰 컨트롤러의 인스턴스를 만들고 속성을 @property (nonatomic,assign)
.
2. 이 뷰 컨트롤러 SecondviewController
인스턴스를 할당합니다.
2. 선택 작업이 끝나면 어레이를 첫 번째 View Controller에 복사합니다. 두 번째 보기를 언로드하면 첫 번째 보기에 배열 데이터가 저장됩니다.
kaar3k나는이 솔루션을 오랫동안 찾고 있었고 마침내 찾았습니다. 우선 SecondViewController.h 파일의 모든 객체를 다음과 같이 선언합니다.
@interface SecondViewController: UIviewController { NSMutableArray *myAray; CustomObject *object; }
이제 구현 파일에서 다음과 같이 해당 개체에 대한 메모리를 할당합니다.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization myAray=[[NSMutableArray alloc] init]; object=[[CustomObject alloc] init]; } return self; }
Array
및 개체에 대한 메모리를 할당했습니다. ViewController
를 푸시하기 전에 메모리를 채울 수 있습니다.
SecondViewController.h로 이동하여 두 가지 방법을 작성하십시오.
-(void)setMyArray:(NSArray *)_myArray; -(void)setMyObject:(CustomObject *)_myObject;
구현 파일에서 함수를 구현할 수 있습니다.
-(void)setMyArray:(NSArray *)_myArray { [myArra addObjectsFromArray:_myArray]; } -(void)setMyObject:(CustomObject *)_myObject { [object setCustomObject:_myObject]; }
CustomObject
에는 setter 기능이 있어야 합니다.
이제 기본 작업이 완료되었습니다. SecondViewController
를 푸시하려는 위치로 이동하여 다음 작업을 수행합니다.
SecondViewController *secondView= [[SecondViewController alloc] initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]] ; [secondView setMyArray:ArrayToPass]; [secondView setMyObject:objectToPass]; [self.navigationController pushViewController:secondView animated:YES ];
맞춤법 오류에 주의하세요.
AsifHabib스위프트 5
Matt Price의 답변 은 데이터를 전달하는 데 완벽하게 적합하지만 최신 Swift 버전에서 다시 작성할 것입니다. 왜냐하면 원래 게시물이 Objective-C에 있기 때문에 새로운 프로그래머가 새로운 구문과 메서드/프레임워크로 인해 어려움을 겪는다고 생각하기 때문입니다. .
뷰 컨트롤러 간에 데이터 를 전달하기 위한 여러 옵션이 있습니다.
- 탐색 컨트롤러 푸시 사용
- Segue 사용
- 대리인 사용
- 알림 관찰자 사용
- 블록 사용
최신 iOS 프레임워크를 사용하여 Swift에서 그의 논리를 다시 작성할 것입니다.
탐색 컨트롤러 푸시를 통해 데이터 전달 : ViewControllerA에서 ViewControllerB로
1단계. ViewControllerB에서 변수 선언
var isSomethingEnabled = false
2단계. ViewControllerB' ViewDidLoad 메서드에서 변수 인쇄
override func viewDidLoad() { super.viewDidLoad() // Print value received through segue, navigation push print("Value of 'isSomethingEnabled' from ViewControllerA: ", isSomethingEnabled) }
3단계 . ViewControllerA에서 탐색 컨트롤러를 통해 푸시하는 동안 데이터 전달
if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB { viewControllerB.isSomethingEnabled = true if let navigator = navigationController { navigator.pushViewController(viewControllerB, animated: true) } }
다음은 전체 코드입니다.
뷰컨트롤러A
import UIKit class ViewControllerA: UIViewController { override func viewDidLoad() { super.viewDidLoad() } // MARK: Passing data through navigation PushViewController @IBAction func goToViewControllerB(_ sender: Any) { if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB { viewControllerB.isSomethingEnabled = true if let navigator = navigationController { navigator.pushViewController(viewControllerB, animated: true) } } } }
뷰컨트롤러B
import UIKit class ViewControllerB: UIViewController { // MARK: - Variable for Passing Data through Navigation push var isSomethingEnabled = false override func viewDidLoad() { super.viewDidLoad() // Print value received through navigation push print("Value of 'isSomethingEnabled' from ViewControllerA: ", isSomethingEnabled) } }
Segue를 통한 데이터 전달 : ViewControllerA에서 ViewControllerB로
1단계. ViewControllerA에서 ViewControllerB로 Segue를 생성하고 아래와 같이 Storyboard에 Identifier = showDetailSegue를 제공합니다.
2단계. ViewControllerB에서 isSomethingEnabled 라는 실행 가능한 이름을 선언하고 해당 값을 인쇄합니다.
3단계 . ViewControllerA에서 Segue를 전달하는 동안 isSomethingEnabled의 값을 전달합니다.
다음 은 전체 코드입니다.
뷰컨트롤러A
import UIKit class ViewControllerA: UIViewController { override func viewDidLoad() { super.viewDidLoad() } // MARK: - - Passing Data through Segue - - @IBAction func goToViewControllerBUsingSegue(_ sender: Any) { performSegue(withIdentifier: "showDetailSegue", sender: nil) } // Segue Delegate Method override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if (segue.identifier == "showDetailSegue") { let controller = segue.destination as? ViewControllerB controller?.isSomethingEnabled = true//passing data } } }
뷰컨트롤러B
import UIKit class ViewControllerB: UIViewController { var isSomethingEnabled = false override func viewDidLoad() { super.viewDidLoad() // Print value received through segue print("Value of 'isSomethingEnabled' from ViewControllerA: ", isSomethingEnabled) } }
Delegate를 통한 데이터 전달 : ViewControllerB에서 ViewControllerA로
1단계. ViewControllerB 파일에서 프로토콜 ViewControllerBDelegate 를 선언하지만 클래스 외부에 있습니다.
protocol ViewControllerBDelegate: NSObjectProtocol { // Classes that adopt this protocol MUST define // this method -- and hopefully do something in // that definition. func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) }
2단계. ViewControllerB에서 Delegate 변수 인스턴스 선언
var delegate: ViewControllerBDelegate?
3단계. ViewControllerB의 viewDidLoad 메소드 내에서 델리게이트를 위한 데이터 보내기
delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA")
4단계. ViewControllerA에서 ViewControllerBDelegate 확인
class ViewControllerA: UIViewController, ViewControllerBDelegate { // to do }
5단계. ViewControllerA에서 대리자를 구현할 것인지 확인합니다.
if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB { viewControllerB.delegate = self//confirming delegate if let navigator = navigationController { navigator.pushViewController(viewControllerB, animated: true) } }
6단계. ViewControllerA에서 데이터 수신을 위한 대리자 메서드 구현
func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) { print("Value from ViewControllerB's Delegate", item!) }
다음 은 전체 코드입니다.
뷰컨트롤러A
import UIKit class ViewControllerA: UIViewController, ViewControllerBDelegate { override func viewDidLoad() { super.viewDidLoad() } // Delegate method func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) { print("Value from ViewControllerB's Delegate", item!) } @IBAction func goToViewControllerForDelegate(_ sender: Any) { if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB { viewControllerB.delegate = self if let navigator = navigationController { navigator.pushViewController(viewControllerB, animated: true) } } } }
뷰컨트롤러B
import UIKit //Protocol decleare protocol ViewControllerBDelegate: NSObjectProtocol { // Classes that adopt this protocol MUST define // this method -- and hopefully do something in // that definition. func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) } class ViewControllerB: UIViewController { var delegate: ViewControllerBDelegate? override func viewDidLoad() { super.viewDidLoad() // MARK: - - - - Set Data for Passing Data through Delegate - - - - - - delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA") } }
알림 관찰자를 통해 데이터 전달 : ViewControllerB에서 ViewControllerA로
1단계. ViewControllerB의 알림 관찰자에서 데이터 설정 및 게시
let objToBeSent = "Test Message from Notification" NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)
2단계. ViewControllerA에 알림 옵저버 추가
NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)
3단계. ViewControllerA에서 알림 데이터 값 수신
@objc func methodOfReceivedNotification(notification: Notification) { print("Value of notification: ", notification.object ?? "") }
다음은 전체 코드입니다.
뷰컨트롤러A
import UIKit class ViewControllerA: UIViewController{ override func viewDidLoad() { super.viewDidLoad() // Add observer in controller(s) where you want to receive data NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil) } // MARK: Method for receiving Data through Post Notification @objc func methodOfReceivedNotification(notification: Notification) { print("Value of notification: ", notification.object ?? "") } }
뷰컨트롤러B
import UIKit class ViewControllerB: UIViewController { override func viewDidLoad() { super.viewDidLoad() // MARK:Set data for Passing Data through Post Notification let objToBeSent = "Test Message from Notification" NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent) } }
블록을 통한 데이터 전달 : ViewControllerB에서 ViewControllerA로
1단계. ViewControllerB에서 블록 선언
var authorizationCompletionBlock:((Bool)->())? = {_ in}
2단계. ViewControllerB의 블록에 데이터 설정
if authorizationCompletionBlock != nil { authorizationCompletionBlock!(true) }
3단계. ViewControllerA에서 블록 데이터 수신
// Receiver Block controller!.authorizationCompletionBlock = { isGranted in print("Data received from Block is: ", isGranted) }
다음 은 전체 코드입니다.
뷰컨트롤러A
import UIKit class ViewControllerA: UIViewController { override func viewDidLoad() { super.viewDidLoad() } // MARK:Method for receiving Data through Block override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if (segue.identifier == "showDetailSegue") { let controller = segue.destination as? ViewControllerB controller?.isSomethingEnabled = true // Receiver Block controller!.authorizationCompletionBlock = { isGranted in print("Data received from Block is: ", isGranted) } } } }
뷰컨트롤러B
import UIKit class ViewControllerB: UIViewController { // MARK: Variable for Passing Data through Block var authorizationCompletionBlock:((Bool)->())? = {_ in} override func viewDidLoad() { super.viewDidLoad() // MARK: Set data for Passing Data through Block if authorizationCompletionBlock != nil { authorizationCompletionBlock!(true) } } }
내 GitHub에서 완전한 샘플 애플리케이션을 찾을 수 있습니다. 이에 대해 질문이 있으면 알려주세요.
swiftBoy이것은 그것을 하는 방법이 아닙니다. 대리자를 사용해야 합니다.
ViewController1과 ViewController2라는 두 개의 뷰 컨트롤러가 있다고 가정하고 이 검사는 첫 번째에 있고 상태가 변경되면 ViewController2에서 무언가를 하고 싶어합니다. 적절한 방법으로 이를 달성하려면 다음을 수행해야 합니다.
프로젝트에 새 파일 추가(Objective-C 프로토콜) 메뉴 파일 → 새로 만들기 . 이제 이름을 ViewController1Delegate 또는 원하는 대로 지정하고 @interface와 @end 지시문 사이에 다음을 작성합니다.
@optional - (void)checkStateDidChange:(BOOL)checked;
이제 ViewController2.h로 이동하여 다음을 추가합니다.
#import "ViewController1Delegate.h"
그런 다음 정의를 다음과 같이 변경합니다.
@interface ViewController2: UIViewController<ViewController1Delegate>
이제 ViewController2.m으로 이동하여 구현 내부에 다음을 추가합니다.
- (void)checkStateDidChange:(BOOL)checked { if (checked) { // Do whatever you want here NSLog(@"Checked"); } else { // Also do whatever you want here NSLog(@"Not checked"); } }
이제 ViewController1.h로 이동하여 다음 속성을 추가합니다.
@property (weak, nonatomic) id<ViewController1Delegate> delegate;
이제 일부 이벤트 후에 ViewController2 내부에 ViewController1을 생성하는 경우 NIB 파일을 사용하여 다음과 같이 수행해야 합니다.
ViewController1* controller = [[NSBundle mainBundle] loadNibNamed:@"ViewController1" owner:self options:nil][0]; controller.delegate = self; [self presentViewController:controller animated:YES completion:nil];
이제 모든 설정이 완료되었습니다. ViewController1에서 변경된 확인 이벤트를 감지할 때마다 다음 작업만 하면 됩니다.
[delegate checkStateDidChange:checked]; // You pass here YES or NO based on the check state of your control
Boda하나의 viewController에서 다른 viewController로 데이터를 보내려면 다음과 같은 방법이 있습니다.
viewController: viewControllerA 및 viewControllerB가 있다고 가정해 보겠습니다.
이제 파일 viewControllerB.h에서
@interface viewControllerB : UIViewController { NSString *string; NSArray *array; } - (id)initWithArray:(NSArray)a andString:(NSString)s;
파일 viewControllerB.m에서 :
#import "viewControllerB.h" @implementation viewControllerB - (id)initWithArray:(NSArray)a andString:(NSString)s { array = [[NSArray alloc] init]; array = a; string = [[NSString alloc] init]; string = s; }
viewControllerA.m 파일에서 :
#import "viewControllerA.h" #import "viewControllerB.h" @implementation viewControllerA - (void)someMethod { someArray = [NSArray arrayWithObjects:@"One", @"Two", @"Three", nil]; someString = [NSString stringWithFormat:@"Hahahahaha"]; viewControllerB *vc = [[viewControllerB alloc] initWithArray:someArray andString:someString]; [self.navigationController pushViewController:vc animated:YES]; [vc release]; }
따라서 이것이 대리자를 설정하지 않고 viewControllerA에서 viewControllerB로 데이터를 전달할 수 있는 방법입니다. ;)
AniruddhSwift 경사로 기본적인 예제를 원합니다. 여기에서 segue를 사용하여 이동하는 경우 데이터를 전달하는 방법이 있습니다.
위와 비슷하지만 버튼, 라벨 등이 없습니다. 한 보기에서 다음 보기로 데이터를 전달하기만 하면 됩니다.
스토리보드 설정
세 부분이 있습니다.
- 발신자
- 더 세구에
- 수신자
이것은 그들 사이에 segue가 있는 매우 간단한 보기 레이아웃입니다.
다음은 발신자 설정입니다.
다음은 수신기 설정입니다.
마지막으로 segue 설정입니다.
보기 컨트롤러
우리는 이것을 단순하게 유지하여 버튼과 동작이 없습니다. 애플리케이션이 로드될 때 단순히 데이터를 보낸 사람에서 받는 사람으로 이동한 다음 전송된 값을 콘솔에 출력하는 것입니다.
이 페이지는 처음에 로드된 값을 가져와서 전달합니다.
import UIKit class ViewControllerSender: UIViewController { // THE STUFF - put some information into a variable let favoriteMovie = "Ghost Busters" override func viewDidAppear(animated: Bool) { // PASS IDENTIFIER - go to the receiving view controller. self.performSegueWithIdentifier("goToReciever", sender: self) } override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { // GET REFERENCE - ...to the receiver view. var viewControllerReceiver = segue.destinationViewController as? ViewControllerReceiver // PASS STUFF - pass the variable along to the target. viewControllerReceiver!.yourFavMovie = self.favoriteMovie } }
이 페이지는 로드될 때 콘솔에 변수 값을 보냅니다. 이 시점에서 우리가 가장 좋아하는 영화가 그 변수에 있어야 합니다.
import UIKit class ViewControllerReceiver: UIViewController { // Basic empty variable waiting for you to pass in your fantastic favorite movie. var yourFavMovie = "" override func viewDidLoad() { super.viewDidLoad() // And now we can view it in the console. println("The Movie is \(self.yourFavMovie)") } }
이것이 segue를 사용하고 탐색 컨트롤러 아래에 페이지가 없는 경우 해결할 수 있는 방법입니다.
실행되면 자동으로 수신자 보기로 전환되고 발신자에서 수신자에게 값을 전달하여 콘솔에 값을 표시해야 합니다.
Christopher Wade Cantley제 경우에는 앱의 거의 모든 곳에서 데이터에 액세스할 수 있는 전역 개체로 작동할 수 있는 싱글톤 클래스를 사용했습니다.
첫 번째는 싱글톤 클래스를 빌드하는 것입니다. 내 Objective-C 싱글톤은 어떤 모습이어야 합니까? 페이지를 참조하십시오. .
그리고 객체를 전역적으로 접근 가능하게 만들기 위해 모든 클래스에 import 문을 적용하기 위한 appName_Prefix.pch
이 개체에 액세스하고 사용하기 위해 자체 변수를 포함하는 공유 인스턴스를 반환하는 클래스 메서드를 구현했습니다.
petershine아래와 같이 FirstViewController에서 SecondViewController로 데이터 전달
예를 들어:
FirstViewController 문자열 값
StrFirstValue = @"first";
따라서 아래 단계를 사용하여 두 번째 클래스에서 이 값을 전달할 수 있습니다.
SecondViewController.h 파일에 문자열 객체를 생성해야 합니다.
NSString *strValue;
.h 파일에서 아래 선언과 같이 속성을 선언해야 합니다.
@property (strong, nonatomic) NSString *strSecondValue;
헤더 선언 아래의 FirstViewController.m 파일에서 해당 값을 합성해야 합니다.
@synthesize strValue;
그리고 FirstViewController.h 파일에서 :
@property (strong, nonatomic) NSString *strValue;
두 번째 뷰로 이동하는 메서드인 FirstViewController에서 해당 메서드에 아래 코드를 작성하세요.
SecondViewController *secondView= [[SecondViewController alloc] initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]]; [secondView setStrSecondValue:StrFirstValue]; [self.navigationController pushViewController:secondView animated:YES ];
Chris Alan저는 현재 여기에서 찾을 수 있는 MCViewFactory라는 프로젝트를 통해 이 문제에 대한 오픈 소스 솔루션에 기여하고 있습니다.
맨티코어 iOS 뷰 팩토리
아이디어는 보고 있는 보기를 관리하기 위해 전역 팩토리를 사용하고 보기 간에 데이터를 전환하고 전달하기 위해 "의도"를 사용하여 Android의 의도 패러다임을 모방하는 것입니다. 모든 문서는 GitHub 페이지에 있지만 다음은 몇 가지 주요 사항입니다.
팩토리를 초기화하는 동안 모든 보기를 .XIB 파일로 설정하고 앱 대리자에 등록합니다.
// Register activities MCViewFactory *factory = [MCViewFactory sharedFactory]; // The following two lines are optional. [factory registerView:@"YourSectionViewController"];
이제 뷰 컨트롤러(VC)에서 새 VC로 이동하여 데이터를 전달하고 싶을 때마다 새 인텐트를 만들고 해당 사전(savedInstanceState)에 데이터를 추가합니다. 그런 다음 공장의 현재 의도를 설정하십시오.
MCIntent* intent = [MCIntent intentWithSectionName:@"YourSectionViewController"]; [intent setAnimationStyle:UIViewAnimationOptionTransitionFlipFromLeft]; [[intent savedInstanceState] setObject:@"someValue" forKey:@"yourKey"]; [[intent savedInstanceState] setObject:@"anotherValue" forKey:@"anotherKey"]; // ... [[MCViewModel sharedModel] setCurrentSection:intent];
이를 준수하는 모든 뷰는 MCViewController의 하위 클래스여야 하며, 이를 통해 전달한 데이터에 액세스할 수 있는 새로운 onResume: 메서드를 재정의할 수 있습니다.
-(void)onResume:(MCIntent *)intent { NSObject* someValue = [intent.savedInstanceState objectForKey:@"yourKey"]; NSObject* anotherValue = [intent.savedInstanceState objectForKey:@"anotherKey"]; // ... // Ensure the following line is called, especially for MCSectionViewController [super onResume:intent]; }
user2563044view controller .h
파일에 속성을 만들고 getter와 setter를 정의합니다.
nextVC의 NextVC.h에 property
을 추가합니다.
@property (strong, nonatomic) NSString *indexNumber;
추가하다
@synthesize indexNumber;
NextVC.m에서
그리고 마침내
NextVC *vc = [[NextVC alloc]init]; vc.indexNumber = @"123"; [self.navigationController vc animated:YES];
Vivek Yadav이를 수행하는 방법은 수없이 많으며 올바른 방법을 선택하는 것이 중요합니다. 아마도 가장 큰 아키텍처 결정 중 하나는 앱 전체에서 모델 코드를 공유하거나 액세스하는 방법에 있을 것입니다.
얼마 전에 이에 대한 블로그 게시물을 작성한 적이 있습니다. 모델 코드 공유 . 다음은 간략한 요약입니다.
공유 데이터
한 가지 접근 방식은 뷰 컨트롤러 간에 모델 개체에 대한 포인터를 공유하는 것입니다.
- 데이터를 설정하기 위해 뷰 컨트롤러(탐색 또는 탭 표시줄 컨트롤러에서)에 대한 무차별 대입 반복
- prepareForSegue(스토리보드인 경우) 또는 init(프로그래밍 방식인 경우)에 데이터 설정
segue 준비가 가장 일반적이므로 다음은 예입니다.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { var next = segue.destinationViewController as NextViewController next.dataSource = dataSource }
독립적인 접근
또 다른 접근 방식은 한 번에 데이터로 가득 찬 화면을 처리하고 뷰 컨트롤러를 서로 연결하는 대신 각 뷰 컨트롤러를 독립적으로 얻을 수 있는 단일 데이터 소스에 연결하는 것입니다.
내가 본 가장 일반적인 방법은 싱글톤 인스턴스입니다. 따라서 싱글톤 개체가 DataAccess
인 경우 UIViewController의 viewDidLoad 메서드에서 다음을 수행할 수 있습니다.
func viewDidLoad() { super.viewDidLoad() var data = dataAccess.requestData() }
데이터 전달에도 도움이 되는 추가 도구가 있습니다.
- 키-값 관찰
- NS알림
- 핵심 데이터
- NSFetchedResultsController
- 데이터 소스
핵심 데이터
Core Data의 좋은 점은 역관계가 있다는 것입니다. 따라서 NotesViewController에 메모 개체를 제공하려는 경우 메모장과 같은 다른 개체와 역 관계를 갖기 때문에 메모 개체를 제공할 수 있습니다. NotesViewController의 노트북에 데이터가 필요한 경우 다음을 수행하여 개체 그래프를 백업할 수 있습니다.
let notebookName = note.notebook.name
내 블로그 게시물: 모델 코드 공유 에서 이에 대한 자세한 내용을 읽어보십시오.
Korey HintonViewControlerOne에서 ViewControllerTwo로 데이터를 전달하려면 다음을 시도하십시오.
ViewControlerOne.h에서 다음을 수행하십시오.
@property (nonatomic, strong) NSString *str1;
ViewControllerTwo.h에서 다음을 수행하십시오.
@property (nonatomic, strong) NSString *str2;
ViewControllerTwo.m에서 str2 합성:
@interface ViewControllerTwo () @end @implementation ViewControllerTwo @synthesize str2;
ViewControlerOne.m에서 다음을 수행하십시오.
- (void)viewDidLoad { [super viewDidLoad]; // Data or string you wants to pass in ViewControllerTwo... self.str1 = @"hello world"; }
O 버튼 클릭 이벤트는 다음과 같이 하십시오.
-(IBAction)ButtonClicked { // Navigation on buttons click event from ViewControlerOne to ViewControlerTwo with transferring data or string.. ViewControllerTwo *objViewTwo = [self.storyboard instantiateViewControllerWithIdentifier:@"ViewControllerTwo"]; obj.str2 = str1; [self.navigationController pushViewController: objViewTwo animated:YES]; }
ViewControllerTwo.m에서 다음을 수행하십시오.
- (void)viewDidLoad { [super viewDidLoad]; NSLog(@"%@", str2); }
krushnsinh앱 대리자에 데이터를 저장하여 애플리케이션의 뷰 컨트롤러에서 데이터에 액세스할 수 있습니다. 앱 대리자의 공유 인스턴스를 생성하기만 하면 됩니다.
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
예를 들어
당신이 선언하면 NSArray object *arrayXYZ
, 당신은에 의한 뷰 컨트롤러에 액세스 할 수 있습니다 appDelegate.arrayXYZ
.
ak_tyagi뉴스보기 컨트롤러
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [tbl_View deselectRowAtIndexPath:indexPath animated:YES]; News *newsObj = [newstitleArr objectAtIndex:indexPath.row]; NewsDetailViewController *newsDetailView = [[NewsDetailViewController alloc] initWithNibName:@"NewsDetailViewController" bundle:nil]; newsDetailView.newsHeadlineStr = newsObj.newsHeadline; [self.navigationController pushViewController:newsDetailView animated:YES]; }
NewsDetailViewController.h
@interface NewsDetailViewController : UIViewController @property(nonatomic,retain) NSString *newsHeadlineStr; @end
NewsDetailViewController.m
@synthesize newsHeadlineStr;
Mohsin Sabasara위임은 .xib 파일을 사용할 때 이러한 작업을 수행하는 유일한 솔루션입니다. 그러나 이전의 모든 답변은 storyboard
대한 것입니다. 위임을 사용해야 합니다. 사용할 수 있는 유일한 솔루션입니다.
또 다른 솔루션은 싱글톤 클래스 패턴을 사용하는 것입니다. 한 번 초기화하고 전체 앱에서 사용하십시오.
user2786888나는 NSProxy를 기반으로 하는 모델 객체와 모의 객체가 사용자가 선택한 것을 취소할 수 있는 경우 데이터를 커밋하거나 버리는 아이디어를 좋아합니다.
단일 개체 또는 몇 개의 개체이기 때문에 데이터를 전달하기 쉽고 UINavigationController 컨트롤러가 있는 경우 모델에 대한 참조를 내부에 유지할 수 있고 푸시된 모든 뷰 컨트롤러는 탐색 컨트롤러에서 직접 액세스할 수 있습니다.
Ben AffleckdidSelectRowAtPath
메소드를 사용하여 이것을 복잡하게 만드는 많은 사람들을 보았습니다. 내 예제에서는 Core Data 를 사용하고 있습니다.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ // This solution is for using Core Data YourCDEntityName * value = (YourCDEntityName *)[[self fetchedResultsController] objectAtIndexPath: indexPath]; YourSecondViewController * details = [self.storyboard instantiateViewControllerWithIdentifier:@"nameOfYourSecondVC"]; // Make sure in storyboards you give your second VC an identifier // Make sure you declare your value in the second view controller details.selectedValue = value; // Now that you have said to pass value all you need to do is change views [self.navigationController pushViewController: details animated:YES]; }
메서드 내부에 4줄의 코드만 있으면 완료됩니다.
App Dev GuySwiftUI의 경우
@EnvironmentObject
를 많은 보기에서 @ObservedObject
를 사용하는 더 똑똑하고 간단한 방법이라고 생각하십시오. 뷰 A에서 일부 데이터를 생성한 다음 뷰 B, 뷰 C, 뷰 D에 전달하여 최종적으로 사용하는 대신 뷰에서 생성하고 뷰 B, C 및 D가 실행되도록 환경에 넣을 수 있습니다. 자동으로 액세스할 수 있습니다.
참고: 환경 개체는 상위 뷰에서 제공해야 합니다. SwiftUI가 올바른 유형의 환경 개체를 찾을 수 없으면 충돌이 발생합니다. 이것은 미리보기에도 적용되므로 주의하십시오.
예를 들어 다음은 사용자 설정을 저장하는 관찰 가능한 개체입니다.
class UserSettings: ObservableObject { @Published var score = 0 }
Hardik Bar실제로 작동하는 보기 컨트롤러 통신을 수행하는 다양한 방법을 제공하는 이 질문에 대한 많은 답변이 있지만 실제로 사용하는 것이 가장 좋고 피해야 하는 방법이 언급된 곳은 없습니다.
실제로 제 생각에는 몇 가지 솔루션만 권장됩니다.
- 데이터를 전달하려면:
- 스토리보드 및 segues를 사용할 때
UIViewController
prepare(for:sender:)
메소드를 재정의하십시오. - 코드를 통해 뷰 컨트롤러 전환을 수행할 때 초기화 또는 속성을 통해 데이터 전달
- 데이터를 거꾸로 전달하려면
- 앱 공유 상태 업데이트(위의 방법 중 하나로 뷰 컨트롤러 간에 전달할 수 있음)
- 위임 사용
- 긴장을 풀다
사용하지 않는 것이 좋습니다.
- 위임을 사용하는 대신 이전 컨트롤러를 직접 참조
- 싱글톤을 통한 데이터 공유
- 앱 대리자를 통해 데이터 전달
- 사용자 기본값을 통한 데이터 공유
- 알림을 통해 데이터 전달
이러한 솔루션은 단기적으로 작동하지만 앱 아키텍처를 왜곡하고 나중에 더 많은 문제를 일으킬 너무 많은 종속성을 도입합니다.
관심 있는 사람들을 위해 이러한 점을 더 깊이 다루고 다양한 단점을 강조하는 몇 가지 기사를 작성했습니다.
Matteo Manferdini출처 : http:www.stackoverflow.com/questions/5210535/passing-data-between-view-controllers