1. 이전 포스팅 확인하기

 

https://growingsaja.tistory.com/892

 

[Objective-C] 앱 만들기 입문 - 5 : api call 후 return받은 데이터 사용하기

1. 이전 포스팅 확인하기 https://growingsaja.tistory.com/891 [Objective-C] 앱 만들기 입문 - 4 : json file 읽어서 저장된 데이터 사용하기 1. 이전 포스팅 확인하기 https://growingsaja.tistory.com/890 [Objective-C] 앱 만들

growingsaja.tistory.com

 

 

 

 

 

2. 프로젝트 구조

 

 - json file의 데이터를 읽고, 해당 정보를 기준으로 view 목록을 그립니다.

 - 1번째 탭은 매 1초마다 api call을 하고 return받은 data로 view를 다시 그려줍니다.

 

 

 

 

 

 

3. default.json 파일

 

{
    "favoriteList_description": [
        ["exchangeName", "Fullname", "codeName", "unit", "searchName", "sort", "isAvailable"],
        ["거래소이름", "전체이름", "코드 또는 축약이름", "단위", "검색명", "종류", "활성화 여부"]
    ],
    "favoriteList": [
        ["Bybit", "Bitcoin", "BTC", "USDT", "BTCUSDT", "spot", "Y"],
        ["Bybit", "Ethereum", "ETH", "USDT", "ETHUSDT", "spot", "Y"],
        ["Bybit", "Cardano", "ADA", "USDT", "ADAUSDT", "spot", "Y"],
        ["Bybit", "Binance Coin", "BNB", "USDT", "BNBUSDT", "spot", "Y"],
        ["Bybit", "Solana", "SOL", "USDT", "SOLUSDT", "spot", "Y"],
        ["Bybit", "Ripple", "XRP", "USDT", "XRPUSDT", "spot", "Y"],
        ["Bybit", "Polkadot", "DOT", "USDT", "DOTUSDT", "spot", "Y"],
        ["Bybit", "Dogecoin", "DOGE", "USDT", "DOGEUSDT", "spot", "Y"],
        ["Bybit", "Chainlink", "LINK", "USDT", "LINKUSDT", "spot", "Y"],
        ["Bybit", "Cosmos", "ATOM", "USDT", "ATOMUSDT", "spot", "Y"],
        ["Bybit", "Polygon", "MATIC", "USDT", "MATICUSDT", "spot", "Y"],
        ["Bybit", "Stellar", "XLM", "USDT", "XLMUSDT", "spot", "Y"],
        ["Bybit", "Litecoin", "LTC", "USDT", "LTCUSDT", "spot", "Y"],
        ["Bybit", "Algorand", "ALGO", "USDT", "ALGOUSDT", "spot", "Y"],
        ["Bybit", "Aave", "AAVE", "USDT", "AAVEUSDT", "spot", "Y"],
        ["Bybit", "Filecoin", "FIL", "USDT", "FILUSDT", "spot", "Y"],
        ["Bybit", "Klaytn", "KLAY", "USDT", "KLAYUSDT", "spot", "Y"],
        ["Bybit", "Terra", "LUNA", "USDT", "LUNAUSDT", "spot", "Y"],
        ["Bybit", "Uniswap", "UNI", "USDT", "UNIUSDT", "spot", "Y"],
        ["Bybit", "Wrapped Bitcoin", "WBTC", "USDT", "WBTCUSDT", "spot", "Y"],
        ["Bybit", "Internet Computer", "ICP", "USDT", "ICPUSDT", "spot", "Y"],
        ["Bybit", "Zilliqa", "ZIL", "USDT", "ZILUSDT", "spot", "Y"],
        ["Bybit", "OMG Network", "OMG", "USDT", "OMGUSDT", "spot", "Y"],
        ["Bybit", "Tezos", "XTZ", "USDT", "XTZUSDT", "spot", "Y"],
        ["Bybit", "Shiba Inu", "SHIB", "USDT", "SHIBUSDT", "spot", "Y"],
        ["Bybit", "Elrond", "EGLD", "USDT", "EGLDUSDT", "spot", "Y"],
        ["Bybit", "Axie Infinity", "AXS", "USDT", "AXSUSDT", "spot", "Y"],
        ["Bybit", "Decentraland", "MANA", "USDT", "MANAUSDT", "spot", "Y"],
        ["Bybit", "Waves", "WAVES", "USDT", "WAVESUSDT", "spot", "Y"]

    ]
}

 

 

 

 

 

4. CRTConnect 파일 작성

 

// vim CRTConnect.h

// Foundation 프레임워크를 임포트합니다. Foundation은 많은 기본적인 Objective-C 클래스와 인터페이스를 포함하고 있습니다.
#import <Foundation/Foundation.h>

// CRTConnect라는 이름의 클래스를 선언하고, NSObject 클래스를 상속받습니다. NSObject는 모든 Objective-C 클래스에서 사용되는 기본 클래스입니다.
@interface CRTConnect : NSObject

// 선언된 메서드를 사용해 API 호출을 하고, 결과를 completionHandler로 반환합니다.
// fetchDataFromAPI:withCompletionHandler: 라는 메서드를 선언합니다. 이 메서드는 다음과 같이 정의되어 있습니다:
// 매서드 자체 반환값: void, 반환값이 없습니다.
// 매개변수1: (NSString *)apiURL: 호출할 API의 URL을 넘겨줍니다. 이는 문자열 (NSString)로 전달됩니다.
// 매개변수2: withCompletionHandler:(void (^)(NSDictionary *jsonResponse, NSError *error))completionHandler: 결과를 반환할 때 호출할 completionHandler 블록입니다. 이 블록은 두 가지 인자를 받습니다:
// 매개변수2-1: NSDictionary *jsonResponse: API에서 반환된 JSON 데이터를 NSDictionary 형식으로 변환한 객체입니다.
// 매개변수2-2: NSError *error: API 호출 중 발생한 오류를 포함하는 NSError 객체입니다. 오류가 없는 경우, 이 값은 nil입니다.
- (void)fetchDataFromAPI:(NSString *)apiURL withCompletionHandler:(void (^)(NSDictionary *jsonResponse, NSError *error))completionHandler;

// CRTConnect 클래스의 인터페이스 선언을 종료합니다.
@end

// 이 클래스는 CRTConnect 객체를 사용하여 API를 호출하고, 호출 결과를 처리할 수 있는 기능을 제공하도록 설계되어 있습니다. NSData를 사용하여 API를 호출한 후, 결과를 딕셔너리 형태로 파싱하고 completionHandler에 전달하는 구현이 필요합니다.

 

// vim CRTConnect.m

#import "CRTConnect.h"

@implementation CRTConnect

- (void)fetchDataFromAPI:(NSString *)apiURL withCompletionHandler:(void (^)(NSDictionary *jsonResponse, NSError *error))completionHandler {
    // NSURLSession을 사용해 API 호출을 준비합니다.
    NSURL *url = [NSURL URLWithString:apiURL];
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (error) {
            // 네트워크 오류 발생시 completionHandler에 에러 정보를 전달합니다.
            NSLog(@"Error: %@", error.localizedDescription);
            completionHandler(nil, error);
            return;
        } else {
            NSError *jsonError;
            // JSON 데이터를 NSDictionary 객체로 변환합니다.
            NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonError];
            
            if (jsonError) {
                // JSON 파싱 오류 발생시 completionHandler에 에러 정보를 전달합니다.
                completionHandler(nil, jsonError);
                NSLog(@"JSON parsing error: %@", jsonError.localizedDescription);
                return;
            } else {
                // 정상적으로 파싱된 경우 completionHandler에 결과를 전달합니다.
                completionHandler(jsonResponse, nil);
            }
        }
    }];
    
    // URL 세션 태스크를 시작합니다.
    [dataTask resume];
}

@end

 

 

 

 

 

5. CRTJSONReader 파일 작성

 

// vim CRTJSONReader.h

#import <Foundation/Foundation.h>

@interface CRTJSONReader : NSDictionary

// JSON 파일을 읽어오기 위한 메소드 선언
- (NSDictionary*)loadJSONFromFile:(NSString *)fileName;

@end

 

// vim CRTJSONReader.m

#import "CRTJSONReader.h"

@implementation CRTJSONReader

- (NSDictionary *) loadJSONFromFile:(NSString *)jsonFileName {
    NSBundle *mainBundle = [NSBundle mainBundle];
    NSString *jsonFilePath = [mainBundle pathForResource:jsonFileName ofType:@"json"];
    NSError *errorJson = nil;
    // 불러올 객체 없을때 reutrn하는 데이터 생성
    NSDictionary *emptyDictionary = [NSDictionary dictionary];

    // 파일 경로를 확인합니다.
    if (jsonFilePath != nil) {
        NSLog(@"JSON 파일 경로: %@", jsonFilePath);

        // JSON 데이터를 읽어옵니다.
        NSData *jsonData = [NSData dataWithContentsOfFile:jsonFilePath];

        // JSON을 NSDictionary로 파싱합니다.
        NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&errorJson];
        if (errorJson) {
            NSLog(@"JSON 파싱 오류: %@", [errorJson localizedDescription]);
            return emptyDictionary;
        } else {
            // JSON 데이터를 사용합니다.
//            NSLog(@"JSON 객체: %@", jsonObject);
            return jsonObject;
        }
    } else {
        NSLog(@"JSON 파일을 찾을 수 없습니다.");
        return emptyDictionary;
    }
}

@end

 

 

 

 

 

6. FavoriteAssetTeackerListVC 파일 작성

 

 - 1번째 탭 화면

 

// vim Controller/FavoriteAssetTeackerListVC.h

#import <UIKit/UIKit.h>

// SecondViewController라는 이름의 뷰 컨트롤러 클래스를 선언합니다.
@interface FavoriteAssetTeackerListVC : UIViewController
@property (readwrite, strong, nonatomic) UIScrollView *scrollView;
@property (readwrite, strong, nonatomic) UIStackView *stackView;
@end

 

// vim Controller/FavoriteAssetTeackerListVC.m

#import <Foundation/Foundation.h>
#import "FavoriteAssetTeackerListVC.h"
#import "CRTConnect.h"
#import "CRTJSONReader.h"

@implementation FavoriteAssetTeackerListVC {
    // json에서 가져온 favoriteList 데이터 각각의 마지막에 append해서 price 기재
    int priceIndexOfFavoriteGoods;
    // json에서 가져온 favoriteList raw 데이터
    NSMutableArray *favoriteListFullInfo;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 스크롤 뷰 생성 및 초기화
    self.scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
    self.scrollView.backgroundColor = [UIColor clearColor];
    [self.view addSubview:self.scrollView];
    
    // 스택 뷰 생성 및 초기화
    self.stackView = [[UIStackView alloc] initWithFrame:self.scrollView.bounds];
    self.stackView.axis = UILayoutConstraintAxisVertical;
    self.stackView.distribution = UIStackViewDistributionEqualSpacing;
    self.stackView.alignment = UIStackViewAlignmentFill;
    self.stackView.spacing = 12;
    [self.scrollView addSubview:self.stackView];
    
    // default.json의 favoriteList 데이터 가져오기
    [self loadFavoriteList];
    
    // NSTimer 생성 및 메서드 호출 설정 - 매 특정시간마다 호출
    [NSTimer scheduledTimerWithTimeInterval:1
                                     target:self
                                   selector:@selector(updateDataAndView)
                                   userInfo:nil
                                    repeats:YES];
}
- (void)updateDataAndView {
    // 데이터 가져오기 및 뷰 업데이트 코드
    // ****************************** [Start] Bybit 데이터 가져오기 ****************************** //
    CRTConnect* tryApiCall = [[CRTConnect alloc] init];
    NSString *apiURL = @"https://api.bybit.com/spot/quote/v1/ticker/price";
    [tryApiCall fetchDataFromAPI:apiURL withCompletionHandler:^(NSDictionary *jsonResponse, NSError *error) {
        if (error) {
            NSLog(@"Error: %@", error.localizedDescription);
        } else {
            // 여기에서 jsonResponse를 가공 한 후 앱에서 사용하실 수 있습니다.
            // result 안의 value만 추출
            NSArray* resultOfApi = jsonResponse[@"result"];
            
            // Bybit Api 결과를 Array에서 깔끔한 Dict로 가공하기 위한 데이터 선언
            NSMutableDictionary *allBybitDictTmp = [@{} mutableCopy];
            // api로 받은 데이터 깔끔한 dictionary로 가공하기
            for (int i=0; i<resultOfApi.count; i++) {
                NSString *keyTmp = resultOfApi[i][@"symbol"];
                NSString *valueTmp = resultOfApi[i][@"price"];
                allBybitDictTmp[keyTmp] = valueTmp;
            }
            
            // favorite List에 가격 append하기
            NSString *indexKey = [[NSString alloc] init];
            for (int i=0; i<self->favoriteListFullInfo.count; i++) {
                indexKey = self->favoriteListFullInfo[i][4];
                // 추후 json에는 있는데, bybit api에는 없는 경우에 대한 오류 해결 if문 추가 필요
                self->favoriteListFullInfo[i][7] = allBybitDictTmp[indexKey];
            }
            // 메인 스레드에서만 UI 업데이트 수행
            dispatch_async(dispatch_get_main_queue(), ^{
                [self updateView];
            });
        }
    }];
    // ****************************** [End] Bybit 데이터 가져오기 ****************************** //
    
}

// default.json 파일의 favoriteList 데이터 읽기
-(void) loadFavoriteList {
    CRTJSONReader *tryReadingDefaultJsonFile = [[CRTJSONReader alloc] init];
    NSDictionary *defaultData = [tryReadingDefaultJsonFile loadJSONFromFile:@"default"];
    favoriteListFullInfo = defaultData[@"favoriteList"];
    int checkDefailtJsonFile = 0;
    for (int i=0; i<favoriteListFullInfo.count-1; i++) {
        if (favoriteListFullInfo[i] == favoriteListFullInfo[i+1]) {
            checkDefailtJsonFile += 1;
        }
    }
    if (checkDefailtJsonFile == [favoriteListFullInfo count]-1) {
        // default.json파일의 favoriteList 안에 있는 Array들 중 길이가 모두 같으면
        priceIndexOfFavoriteGoods = (int)favoriteListFullInfo.count;
    } else {
        // default.json파일의 favoriteList 안에 있는 Array들 중 길이가 다른 것이 1개라도 있으면 WARN 출력 및 앱 dead
        priceIndexOfFavoriteGoods = 9999; // for error
        NSLog(@"****************************************************");
        NSLog(@"[WARN] Check File : default.json - favoriteList");
        NSLog(@"****************************************************");
    }
    
}

-(void) updateView {
    // ****************************** [Start] 뷰 그리기 ****************************** //
    // 기존 뷰 삭제
    for (UIView *subview in self.scrollView.subviews) {
        [subview removeFromSuperview];
    }
    // viewDidLoad 메서드에서 스크롤 뷰를 초기화하고 설정합니다.
    self.scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
    self.scrollView.backgroundColor = [UIColor clearColor];
    [self.view addSubview:self.scrollView];
    
    //카드뷰 배치에 필요한 변수를 설정합니다.
    // 카드 목록 나열될 공간 세팅
    // ****************************** //
    // 목록 상단 공백 margin 설정
    CGFloat cardViewTopStartMargin = 10.0;
    // 카드 목록의 좌우 공백 각각의 margin
    CGFloat horizontalMargin = 2;
    // 카드와 카드 사이의 공백
    CGFloat cardViewSpacing = 4.0;
    // 카드뷰 목록 노출 시작 x축 위치 자동 설정
    CGFloat cardViewXPosition = horizontalMargin;
    // ****************************** //
    // 카드 자체에 대한 세팅
    // 카드 높이 길이 (상하 길이) 설정
    CGFloat cardViewHeight = 60.0;
    // 카드 좌우 길이 phone size 참조하여 자동 조정
    CGFloat cardViewWidth = [UIScreen mainScreen].bounds.size.width - horizontalMargin * 2;
    // 카드뷰 안에 내용 들어가는 공간까지의 margin
    CGFloat basicMarginInCard = 10.0;
    CGFloat defaultFontSize = 16.0;
    // ****************************** //
    
    for (int i=0; i<favoriteListFullInfo.count; i++) {
        UIView *cardView = [[UIView alloc] initWithFrame:CGRectMake(cardViewXPosition, cardViewTopStartMargin + i * (cardViewSpacing + cardViewHeight), cardViewWidth, cardViewHeight)];
        
        // 카드뷰 모서리를 둥글게 설정합니다. 조건 1
        cardView.layer.cornerRadius = 8.0;
        // cardView의 경계를 기준으로 내용물이 보이는 영역을 제한합니다. masksToBounds를 YES로 설정하면, cardView의 경계 밖에 있는 모든 내용물은 자르고 숨깁니다(클립 됩니다). 즉 뷰의 경계 값을 초과한 부분을 자르기 위해 masksToBounds를 YES로 설정합니다. 반면 masksToBounds가 NO인 경우(기본값)에는 뷰의 경계 밖에 있는 내용물이 그대로 보이게 됩니다.
        cardView.layer.masksToBounds = YES;
        
        // UILabel의 텍스트 색상 및 배경색 설정
        // 카드뷰 배경색을 설정합니다.
        if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
            // 다크모드인 경우
            cardView.backgroundColor = [UIColor darkGrayColor];
            [self.view addSubview:cardView];
        } else {
            // 라이트모드인 경우
            cardView.backgroundColor = [UIColor lightGrayColor];
            [self.view addSubview:cardView];
        }
        
        // UILabel 객체를 생성합니다. 이 레이블은 암호화폐의 이름을 표시할 것입니다.
        // 따라서 CGRect를 사용하여 레이블의 위치와 크기를 설정하며, 왼쪽 위 모서리에서 시작합니다.
        UILabel *cryptoNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard, basicMarginInCard, cardViewWidth / 2 - 20, 20)];
        // 레이블의 텍스트를 설정합니다. 여기에서는 sortedArray 배열의 i 번째 요소를 사용합니다.
        cryptoNameLabel.text = favoriteListFullInfo[i][4];
        cryptoNameLabel.font = [UIFont systemFontOfSize:defaultFontSize];
        // 생성한 cryptoNameLabel을 cardView의 서브뷰로 추가합니다. 이렇게 함으로써 레이블이 카드 뷰에 표시됩니다.
        [cardView addSubview:cryptoNameLabel];
        
        
        // 암호화폐 가격 레이블을 생성하고 카드뷰에 추가합니다. 조건 3
        UILabel *cryptoPriceLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 3, basicMarginInCard, cardViewWidth / 2 - basicMarginInCard, 20)];
        cryptoPriceLabel.text = favoriteListFullInfo[i][7];
        cryptoPriceLabel.textAlignment = NSTextAlignmentRight;
        cryptoPriceLabel.font = [UIFont systemFontOfSize:defaultFontSize];
        [cardView addSubview:cryptoPriceLabel];
        
        // 거래소 이름 레이블을 생성하고 카드뷰에 추가합니다. 조건 5
        UILabel *exchangeNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, cardViewHeight - 25, cardViewWidth - 20, 20)];
        exchangeNameLabel.text = @"Bybit";
        exchangeNameLabel.font = [UIFont systemFontOfSize:defaultFontSize];
        [cardView addSubview:exchangeNameLabel];
        [self.scrollView addSubview:cardView];
    }
    // 상하 스크롤 최대치 자동 설정
    CGFloat contentHeight = favoriteListFullInfo.count * (cardViewHeight + cardViewSpacing);
    self.scrollView.contentSize = CGSizeMake(self.view.bounds.size.width, contentHeight);
    // ****************************** [End] 뷰 그리기 ****************************** //
}
@end

 

 

 

 

 

7. MyTabBarController 파일 작성

 

// vim MyTabBarController.h

#import <UIKit/UIKit.h>

// MyTabBarController라는 이름의 탭바 컨트롤러 클래스를 선언합니다.
@interface MyTabBarController : UITabBarController

@end

 

// vim MyTabBarController.m

#import <Foundation/Foundation.h>

#import "MyTabBarController.h"
#import "CurrencyTrackerListVC.h"
#import "FavoriteAssetTeackerListVC.h"
#import "MyProfileVC.h"

@implementation MyTabBarController

- (void)viewDidLoad {
    [super viewDidLoad];

    // FirstViewController를 생성하고 속성을 설정합니다.
    FavoriteAssetTeackerListVC *firstTabMainVC = [[FavoriteAssetTeackerListVC alloc] init];
    UINavigationController *naviVC1 = [[UINavigationController alloc] initWithRootViewController:firstTabMainVC];
    naviVC1.tabBarItem.title = @"첫 번째";
    naviVC1.tabBarItem.image = [UIImage systemImageNamed:@"camera"];
    
    // SecondViewController를 생성하고 속성을 설정합니다.
    CurrencyTrackerListVC *secondTabMainVC = [[CurrencyTrackerListVC alloc] init];
    UINavigationController *naviVC2 = [[UINavigationController alloc] initWithRootViewController:secondTabMainVC];
    naviVC2.tabBarItem.title = @"두 번째";
    naviVC2.tabBarItem.image = [UIImage systemImageNamed:@"house"];
    
    // ThirdViewController를 생성하고 속성을 설정합니다.
    MyProfileVC *thirdTabMainVC = [[MyProfileVC alloc] init];
    UINavigationController *naviVC3 = [[UINavigationController alloc] initWithRootViewController:thirdTabMainVC];
    naviVC3.tabBarItem.title = @"세 번째";
    naviVC3.tabBarItem.image = [UIImage systemImageNamed:@"trash.fill"];

    // 생성된 뷰 컨트롤러를 탭바 컨트롤러에 배치합니다.
    NSArray *viewControllers = @[naviVC1, naviVC2, naviVC3];
    self.viewControllers = viewControllers;}

@end

 

 

 

 

 

8. ViewController 파일 작성

 

// vim ViewController.h

// UIKit 프레임워크의 헤더 파일을 import합니다. UIKit은 iOS 애플리케이션 개발에 필요한 다양한 클래스와 기능을 제공합니다.
#import <UIKit/UIKit.h>
// MARK: - [클래스 설명]
/*
// -----------------------------------------
1. ViewController (선언부)
2. 전역변수 , 메소드 , 인스턴스변수 (클래스 생성자) 선언
// -----------------------------------------
*/
@class MyTabBarController;
// -----------------------------------------
@interface ViewController : UIViewController

@property (strong, nonatomic) MyTabBarController *mainBottomMenuTabBarController;

// -----------------------------------------
@end
// -----------------------------------------

 

 

 

 

 

9. CurrencyTrackerListVC 파일 작성

 

 - 2번째 화면

 

// vim Controller/CurrencyTrackerListVC.h

#import <UIKit/UIKit.h>

// CurrencyTrackerListVC라는 이름의 뷰 컨트롤러 클래스를 선언합니다.
@interface CurrencyTrackerListVC : UIViewController {
    UIScrollView *scrollView;
}

@end

 

// vim Controller/CurrencyTrackerListVC.m

#import <Foundation/Foundation.h>
#import "CurrencyTrackerListVC.h"
#import "CRTJSONReader.h"

@implementation CurrencyTrackerListVC {
    NSArray* favoriteList;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // ****************************** [Start] 데이터 가져오기 ****************************** //
    // JSONReader 객체를 생성합니다. = 카드뷰에 표시할 데이터를 초기화합니다.
    CRTJSONReader *defaultData = [[CRTJSONReader alloc] init];
    // JSON 파일명을 전달하여 JSON 데이터를 읽어옵니다.
    NSDictionary* resultOfDefaultJsonFile = [defaultData loadJSONFromFile:@"default"];  // example.json 파일을 대상으로 합니다.
    // JSON 데이터를 사용합니다.
    favoriteList = resultOfDefaultJsonFile[@"favoriteList"];
    // ****************************** [End] 데이터 가져오기 ****************************** //
    
    // ****************************** [Start] 뷰 ****************************** //
    // viewDidLoad 메서드에서 스크롤 뷰를 초기화하고 설정합니다.
    scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
    scrollView.backgroundColor = [UIColor clearColor];
    [self.view addSubview:scrollView];
    
    //카드뷰 배치에 필요한 변수를 설정합니다.
    // 카드 목록 나열될 공간 세팅
    // ****************************** //
    // 목록 상단 공백 margin 설정
    CGFloat cardViewTopStartMargin = 10.0;
    // 카드 목록의 좌우 공백 각각의 margin
    CGFloat horizontalMargin = 2;
    // 카드와 카드 사이의 공백
    CGFloat cardViewSpacing = 12.0;
    // 카드뷰 목록 노출 시작 x축 위치 자동 설정
    CGFloat cardViewXPosition = horizontalMargin;
    // ****************************** //
    // 카드 자체에 대한 세팅
    // 카드 높이 길이 (상하 길이) 설정
    CGFloat cardViewHeight = 60.0;
    // 카드 좌우 길이 phone size 참조하여 자동 조정
    CGFloat cardViewWidth = [UIScreen mainScreen].bounds.size.width - horizontalMargin * 2;
    // 카드뷰 안에 내용 들어가는 공간까지의 margin
    CGFloat basicMarginInCard = 10.0;
    CGFloat defaultFontSize = 16.0;
    // ****************************** //
    // ****************************** [End] 뷰 ****************************** //
    
    // 카드뷰들을 생성하고 뷰에 추가합니다.
    for (int i = 0; i < favoriteList.count; i++) {
        UIView *cardView = [[UIView alloc] initWithFrame:CGRectMake(cardViewXPosition, cardViewTopStartMargin + i * (cardViewSpacing + cardViewHeight), cardViewWidth, cardViewHeight)];
        
        // 카드뷰 모서리를 둥글게 설정합니다. 조건 1
        cardView.layer.cornerRadius = 8.0;
        // cardView의 경계를 기준으로 내용물이 보이는 영역을 제한합니다. masksToBounds를 YES로 설정하면, cardView의 경계 밖에 있는 모든 내용물은 자르고 숨깁니다(클립 됩니다). 즉 뷰의 경계 값을 초과한 부분을 자르기 위해 masksToBounds를 YES로 설정합니다. 반면 masksToBounds가 NO인 경우(기본값)에는 뷰의 경계 밖에 있는 내용물이 그대로 보이게 됩니다.
        cardView.layer.masksToBounds = YES;
        
        // UILabel의 텍스트 색상 및 배경색 설정
        // 카드뷰 배경색을 설정합니다.
        if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
            // 다크모드인 경우
            cardView.backgroundColor = [UIColor darkGrayColor];
            [self.view addSubview:cardView];
        } else {
            // 라이트모드인 경우
            cardView.backgroundColor = [UIColor lightGrayColor];
            [self.view addSubview:cardView];
        }

        // UILabel 객체를 생성합니다. 이 레이블은 암호화폐의 이름을 표시할 것입니다.
        // 따라서 CGRect를 사용하여 레이블의 위치와 크기를 설정하며, 왼쪽 위 모서리에서 시작합니다.
        UILabel *cryptoNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard, basicMarginInCard, cardViewWidth / 2 - 20, 20)];
        // 레이블의 텍스트를 설정합니다. 여기에서는 favoriteList 배열의 i 번째 요소를 사용합니다.
        cryptoNameLabel.text = [favoriteList[i][2] stringByAppendingString:favoriteList[i][3]];
        cryptoNameLabel.font = [UIFont systemFontOfSize:defaultFontSize];
        // 생성한 cryptoNameLabel을 cardView의 서브뷰로 추가합니다. 이렇게 함으로써 레이블이 카드 뷰에 표시됩니다.
        [cardView addSubview:cryptoNameLabel];


        // 암호화폐 가격 레이블을 생성하고 카드뷰에 추가합니다. 조건 3
        UILabel *cryptoPriceLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 3, basicMarginInCard, cardViewWidth / 2 - basicMarginInCard, 20)];
        cryptoPriceLabel.text = favoriteList[i][1];
        cryptoPriceLabel.textAlignment = NSTextAlignmentRight;
        cryptoPriceLabel.font = [UIFont systemFontOfSize:defaultFontSize];
        [cardView addSubview:cryptoPriceLabel];

        // 활성화 스위치를 생성하고 카드뷰에 추가합니다. 조건 4
        UISwitch *activationSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(cardViewWidth / 7 * 6, cardViewHeight - 40, 200, 40)];
        [activationSwitch addTarget:self action:@selector(onSwitchValueChanged:) forControlEvents:UIControlEventValueChanged];
        [cardView addSubview:activationSwitch];

        // 거래소 이름 레이블을 생성하고 카드뷰에 추가합니다. 조건 5
        UILabel *exchangeNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, cardViewHeight - 25, cardViewWidth - 20, 20)];
        exchangeNameLabel.text = favoriteList[i][0];
        exchangeNameLabel.font = [UIFont systemFontOfSize:defaultFontSize];
        [cardView addSubview:exchangeNameLabel];
        [scrollView addSubview:cardView];
    }
    // 상하 스크롤 최대치 자동 설정
    CGFloat contentHeight = favoriteList.count * (cardViewHeight + cardViewSpacing);
    scrollView.contentSize = CGSizeMake(self.view.bounds.size.width, contentHeight);
}

- (void)onSwitchValueChanged:(UISwitch *)sender {
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"Switch value changed: %@", sender.isOn ? @"ON" : @"OFF");
    });
    
}


@end

 

 

 

 

 

10. My파일 작성

 

// vim Controller/MyProfileVC.h

#import <UIKit/UIKit.h>

// MyProfileVC라는 이름의 뷰 컨트롤러 클래스를 선언합니다.
@interface MyProfileVC : UIViewController

@end

 

// vim Controller/MyProfileVC.m

#import <Foundation/Foundation.h>
#import "MyProfileVC.h"

@implementation MyProfileVC

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 레이블을 생성하고 뷰에 추가합니다.
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(80, 150, 300, 800)];
    label.text = @"세 번째 뷰입니다.";
    [self.view addSubview:label];
}

@end

 

 

 

 

 

11. 결과

 

 

 

 

 

 

 

+ Recent posts