1. 이전 포스팅 확인하기
https://growingsaja.tistory.com/944
2. 작업 전 앱 실행 결과 화면
3. 이번 목표
a. 우측에 데이터 노출
고가 Futures가 있는 A 거래소 이미지 / A거래소 Future의 괴리율 / A거래소의 Spot 가격 및 24h 변동률
저가 Futures가 있는 B 거래소 이미지 / B거래소 Future의 괴리율 / B거래소의 Spot 가격 및 24h 변동률
b. 괴리율 계산 방식
(거래소 Futures 가격 - 거래소 Spot 가격) / 거래소 Spot 가격 x 100 (%)
4. View 구현 : PopularAssetListVC
일단 데이터가 들어갈 기본 틀만 만들어줍니다.
// vim Controller/PopluarAssetListVC.h
// ...
// Futures
@property (strong, nonatomic) NSMutableArray *cryptoPriceFuturesFirstLabelList; // Label
@property (strong, nonatomic) NSMutableArray *changePercent24FuturesFirstLabelList; // Label
@property (strong, nonatomic) NSMutableArray *futuresFirstExchangeImageList; // ImageView
@property (strong, nonatomic) NSMutableArray *cryptoPriceFuturesSecondLabelList; // Label
@property (strong, nonatomic) NSMutableArray *changePercent24FuturesSecondLabelList; // Label
@property (strong, nonatomic) NSMutableArray *futuresSecondExchangeImageList; // ImageView
@property (strong, nonatomic) NSMutableArray *disparityRatioFirstLabelList; // Label
@property (strong, nonatomic) NSMutableArray *disparityRatioFirstExchangeImageList; // ImageView
@property (strong, nonatomic) NSMutableArray *disparityRatioSecondLabelList; // Label
@property (strong, nonatomic) NSMutableArray *disparityRatioSecondExchangeImageList; // ImageView
/* #################### 데이터 세팅 #################### */
// default.json의 exchangeInfo 저장
@property (strong, nonatomic) NSDictionary *exchangeInfo;
// 카드 안의 정보들을 최신 데이터로 뷰 다시 그려주기
-(void) updateCardView;
// 해당 화면 전체 뷰 최신화하기
-(void) updateView;
-(void) loadPopularList;
@end
// vim PopularAssetListVC.m
// ...
/* #################### 거래소별로 Spot & Futures 괴리율 도출해 큰 거래소쪽 정보 노출 세팅 #################### */
// ****** High spot 현재 가격, 거래소 정보 노출 라벨 세팅 ****** //
// 암호화폐 가격 레이블을 생성하고 카드뷰에 추가합니다.
// 가격위한 기본 셋
UILabel *cryptoPriceFuturesFirstLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 5 * 4, basicMarginInCard, cardViewWidth / 5, 20)];
cryptoPriceFuturesFirstLabel.textAlignment = NSTextAlignmentRight;
cryptoPriceFuturesFirstLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
// 최근 24시간동안의 가격 변동률 정보 제공을 위한 기본 셋
UILabel *changePercent24FuturesFirstLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 20 * 17, basicMarginInCard, cardViewWidth/3, 20)];
changePercent24FuturesFirstLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
// 거래소 이름 및 로고 레이블을 생성하고 카드뷰에 추가합니다.
// 거래소 아이콘을 위한 기본 셋
UIImageView *futuresFirstExchangeImage = [[UIImageView alloc] initWithFrame:CGRectMake(cardViewWidth / 10 * 9, basicMarginInCard, miniimage, miniimage)];
futuresFirstExchangeImage.contentMode = UIViewContentModeScaleAspectFit; // 해당 옵션을 사용하여 가로세로 비율 유지 크기입니다.
// ****** Low spot 현재 가격, 거래소 정보 노출 라벨 세팅 ****** //
// 암호화폐 가격 레이블을 생성하고 카드뷰에 추가합니다.
// 가격위한 기본 셋
UILabel *cryptoPriceFuturesSecondLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 5 * 4, cardViewHeight/2, cardViewWidth / 5, 20)];
cryptoPriceFuturesSecondLabel.textAlignment = NSTextAlignmentRight;
cryptoPriceFuturesSecondLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
// 최근 24시간동안의 가격 변동률 정보 제공을 위한 기본 셋
UILabel *changePercent24FuturesSecondLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 20 * 17, cardViewHeight/2, cardViewWidth/3, 20)];
changePercent24FuturesSecondLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
// 거래소 이름 및 로고 레이블을 생성하고 카드뷰에 추가합니다.
// 거래소 아이콘을 위한 기본 셋
UIImageView *futuresSecondExchangeImage = [[UIImageView alloc] initWithFrame:CGRectMake(cardViewWidth / 10 * 9, cardViewHeight/2, miniimage, miniimage)];
futuresSecondExchangeImage.contentMode = UIViewContentModeScaleAspectFit; // 해당 옵션을 사용하여 가로세로 비율 유지 크기입니다.
// first rank disparity ratio
[cardView addSubview:cryptoPriceFuturesFirstLabel];
[_cryptoPriceFuturesFirstLabelList addObject:cryptoPriceFuturesFirstLabel];
[cardView addSubview:changePercent24FuturesFirstLabel];
[_changePercent24FuturesFirstLabelList addObject:changePercent24FuturesFirstLabel];
[cardView addSubview:futuresFirstExchangeImage];
[_futuresFirstExchangeImageList addObject:futuresFirstExchangeImage];
// second rank disparity ratio
[cardView addSubview:cryptoPriceFuturesSecondLabel];
[_cryptoPriceFuturesSecondLabelList addObject:cryptoPriceFuturesSecondLabel];
[cardView addSubview:changePercent24FuturesSecondLabel];
[_changePercent24FuturesSecondLabelList addObject:changePercent24FuturesSecondLabel];
[cardView addSubview:futuresSecondExchangeImage];
[_futuresSecondExchangeImageList addObject:futuresSecondExchangeImage];
// ...
5. View 구현 : PopularAssetListVC 소스코드 수정
// vim Controller/PopluarAssetListVC.h
#import <UIKit/UIKit.h>
// SecondViewController라는 이름의 뷰 컨트롤러 클래스를 선언합니다.
@interface PopluarAssetListVC : UIViewController
@property (readwrite, strong, nonatomic) UIScrollView *scrollView;
@property (readwrite, strong, nonatomic) UIStackView *stackView;
/* #################### 뷰 기본 세팅 #################### */
@property (strong, nonatomic) UILabel *UILabel;
// 탑 뷰
@property (strong, nonatomic) UIView *topInfoTab;
// 라벨 : 현재 시간
@property (strong, nonatomic) UILabel *earthLabel;
@property (strong, nonatomic) UILabel *liveUTCLabel;
@property (strong, nonatomic) UILabel *koreanFlagLabel;
@property (strong, nonatomic) UILabel *liveKSTLabel;
// 라벨 : 환율
@property (strong, nonatomic) UIImageView *ratesProviderImage;
@property (strong, nonatomic) UILabel *ratesLabel;
@property (strong, nonatomic) UILabel *ratesUpdateDateTimeLabel;
// 라벨 : 카드
@property (strong, nonatomic) NSMutableArray *cardViewList; // Label
@property (strong, nonatomic) NSMutableArray *cryptoNameLabelList; // Label
// Spot
@property (strong, nonatomic) NSMutableArray *cryptoPriceHighLabelList; // Label
@property (strong, nonatomic) NSMutableArray *changePricePercent24HighLabelList; // Label
@property (strong, nonatomic) NSMutableArray *exchangeLogoHighImageList; // ImageView
@property (strong, nonatomic) NSMutableArray *cryptoPriceLowLabelList; // Label
@property (strong, nonatomic) NSMutableArray *changePricePercent24LowLabelList; // Label
@property (strong, nonatomic) NSMutableArray *exchangeLogoLowImageList; // ImageView
@property (strong, nonatomic) NSMutableArray *maxPricePremiumPercentLabelList; // Label
@property (strong, nonatomic) NSMutableArray *maxPremiumTabHighExchangeImageList; // ImageView
@property (strong, nonatomic) NSMutableArray *maxPremiumTabLowExchangeImageList; // ImageView
// Futures
@property (strong, nonatomic) NSMutableArray *cryptoPriceFuturesFirstLabelList; // Label
@property (strong, nonatomic) NSMutableArray *changePercent24FuturesFirstLabelList; // Label
@property (strong, nonatomic) NSMutableArray *futuresFirstExchangeImageList; // ImageView
@property (strong, nonatomic) NSMutableArray *cryptoPriceFuturesLastLabelList; // Label
@property (strong, nonatomic) NSMutableArray *changePercent24FuturesLastLabelList; // Label
@property (strong, nonatomic) NSMutableArray *futuresLastExchangeImageList; // ImageView
@property (strong, nonatomic) NSMutableArray *disparityRatioFirstRankLabelList; // Label
@property (strong, nonatomic) NSMutableArray *disparityRatioLastRankLabelList; // Label
// 카드 안의 정보들을 최신 데이터로 뷰 다시 그려주기
-(void) updateCardView;
// 해당 화면 전체 뷰 최신화하기
-(void) updateView;
-(void) loadPopularList;
@end
// vim PopluarAssetListVC.m
#import <Foundation/Foundation.h>
#import "PopluarAssetListVC.h"
// default.json 데이터 읽기
#import "DefaultLoader.h"
// 수시로 변경할 수 있는 설정값
#import "CustomizeSetting.h"
// 현재 시간 가져오는 용도
#import "DateTime.h"
// price 정보 저장
#import "AllPriceLiveData.h"
// 환율 서비스
#import "ServiceRecentRates.h"
@implementation PopluarAssetListVC {
// json에서 가져온 popularList raw 데이터
NSArray *popularList;
NSArray *exchangeListSpot;
NSArray *exchangeListFutures;
}
- (void)viewDidLoad {
[super viewDidLoad];
// 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 = 0;
[self.scrollView addSubview:self.stackView];
// default.json의 필요 데이터 가져오기
[self loadExchangeInfo];
[self loadPopularList];
// 데이터를 표시할 레이블 생성
// **************************************** [Start] 뷰 그리기 **************************************** //
CGFloat defaultFontSize = 16.0;
//카드뷰 배치에 필요한 변수를 설정합니다.
// 카드 목록 나열될 공간 세팅
// ****************************** //
// 카드 자체에 대한 세팅
// 카드 높이 길이 (상하 길이) 설정
CGFloat cardViewHeight = defaultFontSize * 2;
// 카드 좌우 길이 phone size 참조하여 자동 조정
CGFloat cardViewWidth = [UIScreen mainScreen].bounds.size.width;
// ****************************** //
// **************************************** [Start] 최상단에 기타 정보 공간 **************************************** //
/* #################### 현재 시간 정보 #################### */
self.topInfoTab = [[UIView alloc] initWithFrame:CGRectMake(0,-cardViewHeight, cardViewWidth, cardViewHeight)];
// 국기 너비 길이 (좌우 길이) 설정
CGFloat miniimage = 14.0;
// ****** 글로벌 지구 이모티콘 및 시간 설정 ****** //
self.earthLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, miniimage * 3 / 2, miniimage)];
self.earthLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:miniimage];
self.earthLabel.text = @"🌍";
// 표준 시간 = 그리니치 표준시 : 더블린, 에든버러, 리스본, 런던, 카사블랑카, 몬로비아
self.liveUTCLabel = [[UILabel alloc] initWithFrame:CGRectMake(miniimage * 3 / 2, 0, cardViewWidth / 2, miniimage)];
self.liveUTCLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
// ****** 한국 이모티콘 및 시간 설정 ****** //
// 태극기
self.koreanFlagLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, defaultFontSize, miniimage * 3 / 2, miniimage)];
self.koreanFlagLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:miniimage];
self.koreanFlagLabel.text = @"🇰🇷";
// 한국 시간
self.liveKSTLabel = [[UILabel alloc] initWithFrame:CGRectMake(miniimage * 3 / 2, defaultFontSize, cardViewWidth / 2, miniimage)];
self.liveKSTLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
/* #################### 환율 정보 #################### */
// usdkrw 정보 출처 아이콘 이미지를 위한 기본 셋
self.ratesProviderImage = [[UIImageView alloc] initWithFrame:CGRectMake(cardViewWidth/12*7, 0, miniimage, miniimage)];
self.ratesProviderImage.contentMode = UIViewContentModeScaleAspectFit; // 해당 옵션을 사용하여 가로세로 비율 유지 크기입니다.
// usdkrw 환율
self.ratesLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth/12*7, 0, cardViewWidth/8*3, defaultFontSize)];
self.ratesLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
self.ratesLabel.textAlignment = NSTextAlignmentRight;
// 기준 시간
self.ratesUpdateDateTimeLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth/12*7, defaultFontSize, cardViewWidth/8*3, defaultFontSize)];
self.ratesUpdateDateTimeLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
self.ratesUpdateDateTimeLabel.textAlignment = NSTextAlignmentRight;
// ****** 상단에 배치할 Label들을 topInfoTab View에 추가 ****** //
// global 적용
[self.topInfoTab addSubview:_earthLabel];
[self.topInfoTab addSubview:_liveUTCLabel];
// korea 적용
[self.topInfoTab addSubview:_koreanFlagLabel];
[self.topInfoTab addSubview:_liveKSTLabel];
// 환율 적용
[self.topInfoTab addSubview:_ratesProviderImage];
[self.topInfoTab addSubview:_ratesLabel];
[self.topInfoTab addSubview:_ratesUpdateDateTimeLabel];
// ****** 상단 UIView를 self.scrollView에 추가 ****** //
[self.scrollView addSubview:_topInfoTab];
// **************************************** [Start] 카드뷰 목록 쭉 만들기 **************************************** //
// 카드뷰 큰 틀
self.cardViewList = [@[] mutableCopy];
// 암호화폐 symbol
self.cryptoNameLabelList = [@[] mutableCopy];
// 암호화폐 Spot 주요 정보
self.cryptoPriceHighLabelList = [@[] mutableCopy];
self.changePricePercent24HighLabelList = [@[] mutableCopy];
self.exchangeLogoHighImageList = [@[] mutableCopy];
self.cryptoPriceLowLabelList = [@[] mutableCopy];
self.changePricePercent24LowLabelList = [@[] mutableCopy];
self.exchangeLogoLowImageList = [@[] mutableCopy];
// 암호화폐 Spot 프리미엄
self.maxPricePremiumPercentLabelList = [@[] mutableCopy];
self.maxPremiumTabHighExchangeImageList = [@[] mutableCopy];
self.maxPremiumTabLowExchangeImageList = [@[] mutableCopy];
// 암호화폐 Spot & Futures Disparity 관련 주요 정보
self.cryptoPriceFuturesFirstLabelList = [@[] mutableCopy];
self.changePercent24FuturesFirstLabelList = [@[] mutableCopy];
self.futuresFirstExchangeImageList = [@[] mutableCopy];
self.cryptoPriceFuturesLastLabelList = [@[] mutableCopy];
self.changePercent24FuturesLastLabelList = [@[] mutableCopy];
self.futuresLastExchangeImageList = [@[] mutableCopy];
// 암호화폐 Spot & Futures Disparity 관련 프리미엄 정보
self.disparityRatioFirstRankLabelList = [@[] mutableCopy];
self.disparityRatioLastRankLabelList = [@[] mutableCopy];
for (int i=0; i<popularList.count; i++) {
/* #################### 카드뷰 기본 세팅 #################### */
UIView *cardView = [[UIView alloc] initWithFrame:CGRectMake(0, i * cardViewHeight, cardViewWidth, cardViewHeight)];
// UILabel의 텍스트 색상 및 배경색 설정
// 카드뷰 배경색을 설정합니다.
if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
// 다크모드인 경우
cardView.backgroundColor = [UIColor blackColor];
[self.view addSubview:cardView];
// 카드 테두리 다크그레이색
cardView.layer.borderColor = [UIColor darkGrayColor].CGColor;
} else {
// 라이트모드인 경우
cardView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:cardView];
// 카드 테두리 다크그레이색
cardView.layer.borderColor = [UIColor lightGrayColor].CGColor;
}
// 카드 테두리 두께
cardView.layer.borderWidth = 0.5;
// 카드뷰 모서리를 둥글게 설정합니다. 조건 1
cardView.layer.cornerRadius = 0.0;
// cardView의 경계를 기준으로 내용물이 보이는 영역을 제한합니다. masksToBounds를 YES로 설정하면, cardView의 경계 밖에 있는 모든 내용물은 자르고 숨깁니다(클립 됩니다). 즉 뷰의 경계 값을 초과한 부분을 자르기 위해 masksToBounds를 YES로 설정합니다. 반면 masksToBounds가 NO인 경우(기본값)에는 뷰의 경계 밖에 있는 내용물이 그대로 보이게 됩니다.
cardView.layer.masksToBounds = YES;
// UILabel 객체를 생성합니다. 이 레이블은 암호화폐의 이름을 표시할 것입니다.
// 따라서 CGRect를 사용하여 레이블의 위치와 크기를 설정하며, 왼쪽 위 모서리에서 시작합니다.
UILabel *cryptoNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, cardViewWidth / 4, cardViewHeight/2)];
cryptoNameLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
// systemFontOfSize:defaultFontSize
// 생성한 cryptoNameLabel을 cardView의 서브뷰로 추가합니다. 이렇게 함으로써 레이블이 카드 뷰에 표시됩니다.
[_cryptoNameLabelList addObject:cryptoNameLabel];
[cardView addSubview:cryptoNameLabel];
/* #################### High spot 현재 가격, 거래소 정보 노출 라벨 세팅 #################### */
// 암호화폐 가격 레이블을 생성하고 카드뷰에 추가합니다.
// 가격위한 기본 셋
UILabel *cryptoPriceHighLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 20 * 3, 0, cardViewWidth/4, cardViewHeight/2)];
cryptoPriceHighLabel.textAlignment = NSTextAlignmentRight;
cryptoPriceHighLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
// 최근 24시간동안의 가격 변동률 정보 제공을 위한 기본 셋
UILabel *changePricePercent24HighLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 16 * 7, 0, cardViewWidth/6, cardViewHeight/2)];
changePricePercent24HighLabel.textAlignment = NSTextAlignmentRight;
changePricePercent24HighLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
// 거래소 이름 및 로고 레이블을 생성하고 카드뷰에 추가합니다.
// 거래소 아이콘을 위한 기본 셋
UIImageView *exchangeLogoHighImage = [[UIImageView alloc] initWithFrame:CGRectMake(cardViewWidth / 5 * 2, 0, miniimage, miniimage)];
exchangeLogoHighImage.contentMode = UIViewContentModeScaleAspectFit; // 해당 옵션을 사용하여 가로세로 비율 유지 크기입니다.
/* #################### Low spot 현재 가격, 거래소 정보 노출 라벨 세팅 #################### */
// 암호화폐 가격 레이블을 생성하고 카드뷰에 추가합니다.
// 가격위한 기본 셋
UILabel *cryptoPriceLowLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 20 * 3, cardViewHeight/2, cardViewWidth/4, cardViewHeight/2)];
cryptoPriceLowLabel.textAlignment = NSTextAlignmentRight;
cryptoPriceLowLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
// 최근 24시간동안의 가격 변동률 정보 제공을 위한 기본 셋
UILabel *changePricePercent24LowLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 16 * 7, cardViewHeight/2, cardViewWidth/6, cardViewHeight/2)];
changePricePercent24LowLabel.textAlignment = NSTextAlignmentRight;
changePricePercent24LowLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
// 거래소 이름 및 로고 레이블을 생성하고 카드뷰에 추가합니다.
// 거래소 아이콘을 위한 기본 셋
UIImageView *exchangeLogoLowImage = [[UIImageView alloc] initWithFrame:CGRectMake(cardViewWidth / 5 * 2, cardViewHeight/2, miniimage, miniimage)];
exchangeLogoLowImage.contentMode = UIViewContentModeScaleAspectFit; // 해당 옵션을 사용하여 가로세로 비율 유지 크기입니다.
/* #################### spot 프리미엄 노출 세팅 #################### */
// 기본 셋
UILabel *maxPricePremiumPercentLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, cardViewHeight/2, cardViewWidth / 8 + miniimage, cardViewHeight/2)];
maxPricePremiumPercentLabel.textAlignment = NSTextAlignmentRight;
maxPricePremiumPercentLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
// top rank 거래소 아이콘을 위한 기본 셋
UIImageView *maxPremiumTabHighExchangeImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, cardViewHeight/2, miniimage, miniimage)];
maxPremiumTabHighExchangeImage.contentMode = UIViewContentModeScaleAspectFit; // 해당 옵션을 사용하여 가로세로 비율 유지 크기입니다.
// bottom rank 거래소 아이콘을 위한 기본 셋
UIImageView *maxPremiumTabLowExchangeImage = [[UIImageView alloc] initWithFrame:CGRectMake(miniimage + cardViewWidth / 8, cardViewHeight/2, miniimage, miniimage)];
maxPremiumTabLowExchangeImage.contentMode = UIViewContentModeScaleAspectFit; // 해당 옵션을 사용하여 가로세로 비율 유지 크기입니다.
// top rank spot
[cardView addSubview:cryptoPriceHighLabel];
[_cryptoPriceHighLabelList addObject:cryptoPriceHighLabel];
[cardView addSubview:changePricePercent24HighLabel];
[_changePricePercent24HighLabelList addObject:changePricePercent24HighLabel];
[cardView addSubview:exchangeLogoHighImage];
[_exchangeLogoHighImageList addObject:exchangeLogoHighImage];
// bottom rank spot
[cardView addSubview:cryptoPriceLowLabel];
[_cryptoPriceLowLabelList addObject:cryptoPriceLowLabel];
[cardView addSubview:changePricePercent24LowLabel];
[_changePricePercent24LowLabelList addObject:changePricePercent24LowLabel];
[cardView addSubview:exchangeLogoLowImage];
[_exchangeLogoLowImageList addObject:exchangeLogoLowImage];
// spot max premium % in spot
[cardView addSubview:maxPricePremiumPercentLabel];
[_maxPricePremiumPercentLabelList addObject:maxPricePremiumPercentLabel];
[cardView addSubview:maxPremiumTabHighExchangeImage];
[_maxPremiumTabHighExchangeImageList addObject:maxPremiumTabHighExchangeImage];
[cardView addSubview:maxPremiumTabLowExchangeImage];
[_maxPremiumTabLowExchangeImageList addObject:maxPremiumTabLowExchangeImage];
/* #################### 거래소별로 Spot & Futures 괴리율 도출해 큰 거래소쪽 정보 노출 세팅 #################### */
// ****** First Rank 현재 가격, 거래소 정보 노출 라벨 세팅 ****** //
// 암호화폐 가격 레이블을 생성하고 카드뷰에 추가합니다.
// 가격위한 기본 셋
UILabel *cryptoPriceFuturesFirstLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 5 * 4, 0, cardViewWidth / 5, defaultFontSize/5*3)];
cryptoPriceFuturesFirstLabel.textAlignment = NSTextAlignmentRight;
cryptoPriceFuturesFirstLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize/5*3];
// 최근 24시간동안의 가격 변동률 정보 제공을 위한 기본 셋
UILabel *changePercent24FuturesFirstLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 40 * 37, cardViewHeight/4, cardViewWidth/8, defaultFontSize/7*4)];
changePercent24FuturesFirstLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize/7*4];
// 거래소 이름 및 로고 레이블을 생성하고 카드뷰에 추가합니다.
// 거래소 아이콘을 위한 기본 셋
UIImageView *futuresFirstExchangeImage = [[UIImageView alloc] initWithFrame:CGRectMake(cardViewWidth / 40 * 29, 0, miniimage, miniimage)];
futuresFirstExchangeImage.contentMode = UIViewContentModeScaleAspectFit; // 해당 옵션을 사용하여 가로세로 비율 유지 크기입니다.
// first rank disparity ratio
[cardView addSubview:cryptoPriceFuturesFirstLabel];
[_cryptoPriceFuturesFirstLabelList addObject:cryptoPriceFuturesFirstLabel];
[cardView addSubview:changePercent24FuturesFirstLabel];
[_changePercent24FuturesFirstLabelList addObject:changePercent24FuturesFirstLabel];
[cardView addSubview:futuresFirstExchangeImage];
[_futuresFirstExchangeImageList addObject:futuresFirstExchangeImage];
// ****** last rank 현재 가격, 거래소 정보 노출 라벨 세팅 ****** //
// 암호화폐 가격 레이블을 생성하고 카드뷰에 추가합니다.
// 가격위한 기본 셋
UILabel *cryptoPriceFuturesLastLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 5 * 4, cardViewHeight/2, cardViewWidth / 5, defaultFontSize/5*3)];
cryptoPriceFuturesLastLabel.textAlignment = NSTextAlignmentRight;
cryptoPriceFuturesLastLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize/5*3];
// 최근 24시간동안의 가격 변동률 정보 제공을 위한 기본 셋
UILabel *changePercent24FuturesLastLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 40 * 37, cardViewHeight/4*3, cardViewWidth/8, defaultFontSize/7*4)];
changePercent24FuturesLastLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize/7*4];
// 거래소 이름 및 로고 레이블을 생성하고 카드뷰에 추가합니다.
// 거래소 아이콘을 위한 기본 셋
UIImageView *futuresLastExchangeImage = [[UIImageView alloc] initWithFrame:CGRectMake(cardViewWidth / 40 * 29, cardViewHeight/2, miniimage, miniimage)];
futuresLastExchangeImage.contentMode = UIViewContentModeScaleAspectFit; // 해당 옵션을 사용하여 가로세로 비율 유지 크기입니다.
// last rank disparity ratio
[cardView addSubview:cryptoPriceFuturesLastLabel];
[_cryptoPriceFuturesLastLabelList addObject:cryptoPriceFuturesLastLabel];
[cardView addSubview:changePercent24FuturesLastLabel];
[_changePercent24FuturesLastLabelList addObject:changePercent24FuturesLastLabel];
[cardView addSubview:futuresLastExchangeImage];
[_futuresLastExchangeImageList addObject:futuresLastExchangeImage];
// ****** Disparity Ratio Premium 정보, 해당하는 거래소 정보 노출 라벨 세팅 ****** //
// 프리미엄 수치 기본 셋 - first rank
UILabel *disparityRatioFirstRankLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 4 * 3, 0, cardViewWidth/13*2, cardViewHeight/2)];
disparityRatioFirstRankLabel.textAlignment = NSTextAlignmentRight;
disparityRatioFirstRankLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
// 프리미엄 수치 기본 셋 - last rank
UILabel *disparityRatioLastRankLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 4 * 3, cardViewHeight/2, cardViewWidth/13*2, cardViewHeight/2)];
disparityRatioLastRankLabel.textAlignment = NSTextAlignmentRight;
disparityRatioLastRankLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
// disparity ratio Premium
[cardView addSubview:disparityRatioFirstRankLabel];
[_disparityRatioFirstRankLabelList addObject:disparityRatioFirstRankLabel];
[cardView addSubview:disparityRatioLastRankLabel];
[_disparityRatioLastRankLabelList addObject:disparityRatioLastRankLabel];
/* #################### 카드뷰 세로로 축적하기 #################### */
// cardView를 self.scrollView에 추가합니다.
[self.scrollView addSubview:cardView];
// 레이블 세팅 완료된 cardView를 CardViewList에 넣기
[self.cardViewList addObject: cardView];
}
// 상하 스크롤 최대치 자동 설정
CGFloat contentHeight = popularList.count * cardViewHeight;
self.scrollView.contentSize = CGSizeMake(self.view.bounds.size.width, contentHeight);
// NSTimer 생성 및 메서드 호출 설정 - 매 특정시간마다 호출
[NSTimer scheduledTimerWithTimeInterval:VIEW_UPDATE_TERM
target:self
selector:@selector(updateCardView)
userInfo:nil
repeats:YES];
}
// **************************************** 카드뷰 다시 그리기 **************************************** //
-(void) updateCardView {
// 메인 스레드에서만 UI 업데이트 수행
dispatch_async(dispatch_get_main_queue(), ^{
[self updateView];
});
}
// **************************************** 기본 데이터 세팅하기 **************************************** //
/* #################### default.json 파일의 exchangeInfo 데이터 읽기 #################### */
-(void) loadExchangeInfo {
// ****** default.json의 exchangeInfo 불러오기 ****** //
exchangeListSpot = [DefaultLoader sharedInstance].exchangeListSpot;
exchangeListFutures = [DefaultLoader sharedInstance].exchangeListFutures;
}
/* #################### default.json 파일의 popularList 데이터 읽기 #################### */
-(void) loadPopularList {
// ****** default.json의 popularList 불러오고 정상인지 1차 확인하기 ****** //
// popularList에 있는 각 element들의 개수가 같은지 확인
// popularList에 있는 데이터들 중, isAvailable이 true인 애들만 추출하기
NSArray *popularList_raw = [[DefaultLoader sharedInstance].popularList mutableCopy];
NSMutableArray *popularList_tmp = [@[] mutableCopy];
int checkSizeOfEachPopularList = 0;
for (int i=0; i<popularList_raw.count-1; i++) {
// 각 popularAsset의 array 길이가 같은지 각각 확인해서 counting
if ([popularList_raw[i] count] == [popularList_raw[i+1] count]) {
checkSizeOfEachPopularList += 1;
}
}
if (checkSizeOfEachPopularList == [popularList_raw count]-1) {
// default.json파일의 popularList 안에 있는 Array들 중 길이가 모두 같으면 정상으로 판단하고 isActive 활성화 데이터만 사용
for (NSArray *each in popularList_raw) {
if ([each[0] boolValue]) {
// index 0은 isAvailable 데이터로, 활성화 여부가 true인 애들만 넣어주기
[popularList_tmp addObject:each];
}
}
popularList = popularList_tmp;
NSLog(@"%@", @"[INFO] Default Setting Load Complete");
} else {
// default.json파일의 popularList 안에 있는 Array들 중 길이가 다른 것이 1개라도 있으면 WARN 출력 및 available된 popularList 데이터 활용 미진행
NSLog(@"****************************************************");
NSLog(@"[WARN] Check File : default.json - popularList");
NSLog(@"****************************************************");
}
}
// **************************************** 전체 화면 뷰 **************************************** //
/* #################### 화면 업데이트 실시 #################### */
-(void) updateView {
// 현재 시간 확인을 위한 singlton instance 생성
DateTime *now = [DateTime sharedInstance];
// 레이블의 텍스트를 설정합니다. 여기에서는 UTC 시간을 업데이트합니다.
[now NowUTC: @"yyyy-MM-dd (E) HH:mm:ss"];
self.liveUTCLabel.text = now.dateTime;
// 레이블의 텍스트를 설정합니다. 여기에서는 KST 시간을 업데이트합니다.
[now NowKST: @"yyyy-MM-dd (E) HH:mm:ss"];
self.liveKSTLabel.text = now.dateTime;
// ****** USDKRW 환율 정보 업데이트 ****** //
ServiceRecentRates *ratesInfo = [ServiceRecentRates sharedInstance];
// 환율 노출
if (ratesInfo.usdkrw) {
// 환율 변동률 가공 및 값에 따라 환율 정보 색 지정
NSString *ratesChangeInfo = [ratesInfo.changePercentUsdkrw stringByAppendingString:@"%)"];
if ( [ratesInfo.changePercentUsdkrw hasPrefix:@"-"]) {
// 음수인 경우 이미 - 붙어있으니까 그냥 합치기
ratesChangeInfo = [@" (" stringByAppendingString:ratesChangeInfo];
// 음수인 경우 하락색
self.ratesLabel.textColor = [UIColor redColor];
} else {
// 양수이거나 0인 경우 + 붙여서 합치기
ratesChangeInfo = [@" (+" stringByAppendingString:ratesChangeInfo];
if ([ratesInfo.changePercentUsdkrw isEqual:@"0.00"]) {
// 0인 경우
if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
// 다크모드인 경우 글자색 흰색으로 원복
self.ratesLabel.textColor = [UIColor whiteColor];
} else {
// 라이트모드인 경우 글자색 흑색으로 원복
self.ratesLabel.textColor = [UIColor blackColor];
}
} else {
// 양수인 경우 상승색
self.ratesLabel.textColor = [UIColor greenColor];
}
}
// 환율 호가
NSString *ratesMainInfo = ratesInfo.usdkrw;
self.ratesLabel.text = [ratesMainInfo stringByAppendingString:ratesChangeInfo];
// ****** rates 정보 출처 provider 이미지 설정 ****** //
if ([ratesInfo.provider isEqual:@"하나은행"]) {
self.ratesProviderImage.image = [UIImage imageNamed:[DefaultLoader sharedInstance].ratesApi[@"DunamuQuotation"][@"image"]];
} else {
self.ratesProviderImage.image = [UIImage imageNamed:[DefaultLoader sharedInstance].ratesApi[ratesInfo.provider][@"image"]];
if ( ! [ratesInfo.provider isEqual:@"OpenExchangeRates"]) {
NSLog(@"[INFO] Rates Image Loading Now...");
}
}
} else {
// 데이터 불러오기 실패 또는 불러오기 전인 경우
if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
// 다크모드인 경우 글자색 흰색으로 원복
self.ratesLabel.textColor = [UIColor whiteColor];
} else {
// 라이트모드인 경우 글자색 흑색으로 원복
self.ratesLabel.textColor = [UIColor blackColor];
}
self.ratesLabel.text = @"-";
}
// 환율 최근 업데이트 일시 노출
self.ratesUpdateDateTimeLabel.text = ratesInfo.recentDateTime;
// ****** Spot 가격 정보 불러오기 ****** //
AllPriceLiveData *allTicker = [AllPriceLiveData sharedInstance];
/* #################### [Start] 카드뷰 데이터 업데이트 #################### */
for (int i=0; i<popularList.count; i++) {
// ****** 기준 key인 symbol 변수를 popularAsset 정보 토대로 만들기 ****** //
NSString *asset = popularList[i][2];
NSString *paymentCurrency = popularList[i][3];
NSString *symbol = [asset stringByAppendingString:paymentCurrency];
// ****** 카드뷰 기본 세팅 ****** //
UILabel *cryptoNameLabel = _cryptoNameLabelList[i];
cryptoNameLabel.text = symbol;
// ****** 누가 High로 배치될지, Low로 배치될지 로직 ****** //
// 거래소 가격 비교해보고 탑랭크, 바텀랭크 지정
NSString *topRankPriceExchange = @"";
NSString *bottomRankPriceExchange = @"";
NSUInteger minIndex = 0;
NSUInteger startIndex = 1;
NSUInteger maxIndex = 0;
// 해당 symbol이 상장되어있는 거래소 개수 counting하기
NSUInteger aliveSymbolCount = 0;
for (int i = 0; i<exchangeListSpot.count; i++) {
if ([allTicker.recentSpotTicker[exchangeListSpot[i]] objectForKey:asset]) {
// 특정 자산 있는지 확인
if ([allTicker.recentSpotTicker[exchangeListSpot[i]][asset] objectForKey:paymentCurrency]) {
// 특정 지불 화폐 있는지 확인
aliveSymbolCount++;
}
}
}
if (aliveSymbolCount >= 1) {
// 가격 정보가 1개 이상인 경우
for (NSUInteger i=0; i<exchangeListSpot.count; i++) {
// minIndex 배정 전에, 정상적인 유효 거래소 price의 index를 일단 찾아서 minIndex로 넣고 그거랑 비교 진행
if (minIndex != startIndex && allTicker.recentSpotTicker[exchangeListSpot[i]][asset][paymentCurrency][@"price"]) {
// minIndex랑 startIndex가 다른, 초기 상태이면서 price가 null이 아닌 유효한 값을 가질 때에만 진행, 만약 if문 안에 들어온다면 minIndex와 startIndex, maxIndex가 같아지면서 해당 로직 미진행
// 목표는, 처음으로 값이 존재하는 exchange를 찾아 해당 거래소부터 대소비교를 진행하기 위함임!
startIndex = i;
minIndex = i;
maxIndex = i;
}
}
for (NSUInteger i=0; i<exchangeListSpot.count; i++) {
// min, max index 찾기 실행
// 그 전에, Thread 1: EXC_BAD_ACCESS (code=1, address=...) 오류를 예방하기위해 자주 쓰는 변수 넣는 value 선언하기
NSDictionary *priceOfAssetData = allTicker.recentSpotTicker[exchangeListSpot[i]];
NSDictionary *priceOfAssetData_min = allTicker.recentSpotTicker[exchangeListSpot[minIndex]];
NSDictionary *priceOfAssetData_max = allTicker.recentSpotTicker[exchangeListSpot[maxIndex]];
if (minIndex == i) {
// min, max index 찾을때, startIndex는 어차피 minIndex에서 가져가면서 체크하기때문에 체크 미진행
// pass
} else {
// min, max index 찾는 거 실행
if (priceOfAssetData[asset][paymentCurrency]) {
// index가 깊어서, 해당 깊이의 index가 존재하는지 확인한 후 대소 비교 진행하도록 if 조건문 실행 -> 만약 없으면 그냥 pass!
if (priceOfAssetData_min[asset][paymentCurrency]) {
if (priceOfAssetData_max[asset][paymentCurrency]) {
// 값이 없는 거래소의 경우 대소비교 미진행을 위한 예외처리
// !!! 한번에 여러 index에 접근하여 데이터를 참조하는 경우 잘못된 메모리 주소 참조를 방지할 수 있습니다. !!!
// 아예 수집을 하지 못한 거래소가 최소값인 거래소로 나오지 않도록, 없는 값은 아닌지 확인! null을 floatValue 하면 0.000000 이 나오기 때문에 무조건 작은 index로 잡힙니다. null인 애는 제외하고 대소비교를 해야합니다.
if ([priceOfAssetData[asset][paymentCurrency][@"price"] floatValue] < [priceOfAssetData_min[asset][paymentCurrency][@"price"] floatValue]) {
// 가장 저렴한 Exchange index 찾기
minIndex = i;
}
if ([priceOfAssetData[asset][paymentCurrency][@"price"] floatValue] > [priceOfAssetData_max[asset][paymentCurrency][@"price"] floatValue]) {
// 가장 비싼 Exchange index 찾기
maxIndex = i;
}
}
}
}
}
}
// 찾은 최소 index와 최대 index를 통해 top, bottom 거래소명 값 저장
topRankPriceExchange = exchangeListSpot[maxIndex];
bottomRankPriceExchange = exchangeListSpot[minIndex];
} else {
// 가격 정보가 모두 없을 경우 (로딩중이거나 못불러오거나) 전부 0으로 노출
topRankPriceExchange = exchangeListSpot[0];
bottomRankPriceExchange = exchangeListSpot[0];
allTicker.recentSpotTicker[topRankPriceExchange][asset][paymentCurrency][@"price"] = 0;
allTicker.recentSpotTicker[bottomRankPriceExchange][asset][paymentCurrency][@"price"] = 0;
}
// ****** top price spot 거래소명 및 이미지 설정 ****** //
UIImageView *exchangeLogoHighImage = _exchangeLogoHighImageList[i];
exchangeLogoHighImage.image = [UIImage imageNamed:[DefaultLoader sharedInstance].exchangeInfo[topRankPriceExchange][@"information"][@"image"]];
// ****** top price spot 가격 설정 ****** //
NSDictionary *topRankPriceData = allTicker.recentSpotTicker[topRankPriceExchange][asset][paymentCurrency];
// Array에서 사용할 UILabel 가져오기
UILabel *cryptoPriceHighLabel = _cryptoPriceHighLabelList[i];
UILabel *changePricePercent24HighLabel = _changePricePercent24HighLabelList[i];
// 데이터 출력하기
if (topRankPriceData) {
// 데이터가 정상적으로 있는 경우
// 최신 가격
NSString *price = topRankPriceData[@"price"];
cryptoPriceHighLabel.text = price;
// ****** top price spot 최근 24시간 가격 변동률 ****** //
// 24시간 가격 변동률
NSString *changePricePercent24 = [topRankPriceData[@"changePricePercent24"] stringByAppendingString:@"%"];
changePricePercent24HighLabel.text = changePricePercent24;
// 24시간 가격 변동률에 색 입히거나, 없는 경우에 대한 노출 경우의 수 처리
if ([changePricePercent24 isEqual:@"-%"]) {
// pass = defaul Color로 text 색칠하고, 기존 % 없는 -로 출력하기, 대표적으로 Kraken 거래소는 최근 24시간 가격 변동률을 구할 수 없어서 해당 기능 넣었음, 하지만 Kraken 아예 수집 안하는거로 수정해서 필요한 내용은 아님
changePricePercent24HighLabel.text = @" - ";
} else if ([changePricePercent24 hasPrefix:@"+"]) {
// 음수가 아니라 0 또는 양수인 경우
if ([topRankPriceData[@"changePricePercent24"] floatValue] == 0) {
// 0.00이면 보합
if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
// 다크모드인 경우 글자색 흰색으로 원복
changePricePercent24HighLabel.textColor = [UIColor whiteColor];
cryptoPriceHighLabel.textColor = [UIColor whiteColor];
} else {
// 라이트모드인 경우 글자색 흑색으로 원복
changePricePercent24HighLabel.textColor = [UIColor blackColor];
cryptoPriceHighLabel.textColor = [UIColor blackColor];
}
} else {
// 상승은 상승색
changePricePercent24HighLabel.textColor = [UIColor greenColor];
cryptoPriceHighLabel.textColor = [UIColor greenColor];
}
} else {
// 하락은 하락색
changePricePercent24HighLabel.textColor = [UIColor redColor];
cryptoPriceHighLabel.textColor = [UIColor redColor];
}
} else {
// ****** top price spot 데이터가 없는 경우 ****** //
// 비정상 상태는 아니고, 0개의 거래소에 상장되어있는 경우 데이터가 아예 없어서 이런 현상 발생 + 이 경우에는 변동률 정보도 당연히 없음
cryptoPriceHighLabel.text = @" - ";
changePricePercent24HighLabel.text = @" - ";
}
// ****** bottom price spot 거래소 이미지 설정 ****** //
UIImageView *exchangeLogoLowImage = _exchangeLogoLowImageList[i];
exchangeLogoLowImage.image = [UIImage imageNamed:[DefaultLoader sharedInstance].exchangeInfo[bottomRankPriceExchange][@"information"][@"image"]];
// ****** bottom price spot 가격 설정 ****** //
NSDictionary *bottomRankPriceData = allTicker.recentSpotTicker[bottomRankPriceExchange][asset][paymentCurrency];
// Array에서 사용할 UILabel 가져오기
UILabel *cryptoPriceLowLabel = _cryptoPriceLowLabelList[i];
UILabel *changePricePercent24LowLabel = _changePricePercent24LowLabelList[i];
if (bottomRankPriceData[@"price"]) {
// 데이터가 정상적으로 있는 경우
cryptoPriceLowLabel.text = bottomRankPriceData[@"price"];
NSString *changePricePercent24;
if (bottomRankPriceData[@"changePricePercent24"]) {
changePricePercent24 = [bottomRankPriceData[@"changePricePercent24"] stringByAppendingString:@"%"];
changePricePercent24LowLabel.text = changePricePercent24;
if ([changePricePercent24 hasPrefix:@"+"]) {
// 음수가 아닌 0이거나 양수인 경우
if ([bottomRankPriceData[@"changePricePercent24"] floatValue] == 0) {
// 0인 경우 기본색 = 0.00이면 보합
if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
// 다크모드인 경우 글자색 흰색으로 원복
changePricePercent24LowLabel.textColor = [UIColor whiteColor];
cryptoPriceLowLabel.textColor = [UIColor whiteColor];
} else {
// 라이트모드인 경우 글자색 흑색으로 원복
changePricePercent24LowLabel.textColor = [UIColor blackColor];
cryptoPriceLowLabel.textColor = [UIColor blackColor];
}
} else {
// 양수인 경우
changePricePercent24LowLabel.textColor = [UIColor greenColor];
cryptoPriceLowLabel.textColor = [UIColor greenColor];
}
} else {
// 음수인 경우
changePricePercent24LowLabel.textColor = [UIColor redColor];
cryptoPriceLowLabel.textColor = [UIColor redColor];
}
} else {
changePricePercent24LowLabel.text = @" - %";
NSLog(@"[WARN] changePricePercent24LowLabel error : %@", allTicker.recentSpotTicker[bottomRankPriceExchange][asset]);
}
} else {
// 데이터가 없는 경우 : 비정상 상태는 아니고, 1개의 거래소에만 상장되어있는 경우 데이터가 없어서 이런 현상 발생
cryptoPriceLowLabel.text = @" - ";
changePricePercent24LowLabel.text = @" - ";
}
/* #################### 프리미엄 비교 #################### */
// data를 float로 빼서 사용하기
float topPrice;
float bottomPrice;
if (topRankPriceData[@"price"]) {
// topRankPriceExchange 관련 정보 있는 경우에만 처리
topPrice = [topRankPriceData[@"price"] floatValue];
} else {
// 없으면 1로!
topPrice = 1;
}
if (bottomRankPriceData[@"price"]) {
// bottomRankPriceExchange 관련 정보 있는 경우에만 처리
bottomPrice = [bottomRankPriceData[@"price"] floatValue];
} else {
// 없으면 1로!
bottomPrice = 1;
}
// 소수점 둘째자리 수까지
NSString *maxPremiumPercent = [NSString stringWithFormat:@"%.2f", (topPrice-bottomPrice)/bottomPrice*100];
// ****** max premium spot 이미지 및 프리미엄 수치 설정 ****** //
if ([maxPremiumPercent isEqual:@"inf"]) {
// 가격 비교할 거래소가 없어 1개의 거래소 데이터만 있는 경우
maxPremiumPercent = @" - % ";
} else if ([maxPremiumPercent isEqual:@"nan"]) {
// 아무런 거래소에도 상장되어있지 않은 경우
maxPremiumPercent = @" - % ";
} else {
// 가격 비교할 거래소가 있어 2개 이상의 거래소 데이터가 있는 일반적인 경우
maxPremiumPercent = [maxPremiumPercent stringByAppendingString:@"%"];
}
// 특성 적용
UILabel *maxPricePremiumPercentLabel = _maxPricePremiumPercentLabelList[i];
// 특정 값 이상 프리미엄 발생시, 텍스트에 배경색 입히기
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:maxPremiumPercent];
if ([maxPremiumPercent floatValue] >= 0.1) {
// 0.1 이상인 경우 주황색
[attributedString addAttribute:NSBackgroundColorAttributeName
value:[UIColor orangeColor]
range:NSMakeRange(0, maxPremiumPercent.length)];
} else if ([maxPremiumPercent floatValue] >= 0.05) {
// 0.05 이상인 경우 회색
if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
// ****** 시스템 테마 설정에 따라 색 다르게 적용 ****** //
// 다크모드인 경우
[attributedString addAttribute:NSBackgroundColorAttributeName
value:[UIColor darkGrayColor]
range:NSMakeRange(0, maxPremiumPercent.length)];
} else {
// 라이트모드인 경우
[attributedString addAttribute:NSBackgroundColorAttributeName
value:[UIColor lightGrayColor]
range:NSMakeRange(0, maxPremiumPercent.length)];
}
} else {
// 일반 상태의 경우 clearColor로 지정해, 기존 색을 삭제
[attributedString addAttribute:NSBackgroundColorAttributeName
value:[UIColor clearColor]
range:NSMakeRange(0, maxPremiumPercent.length)];
}
maxPricePremiumPercentLabel.attributedText = attributedString;
UIImageView *maxPremiumTabHighExchangeImage = _maxPremiumTabHighExchangeImageList[i];
UIImageView *maxPremiumTabLowExchangeImage = _maxPremiumTabLowExchangeImageList[i];
maxPremiumTabHighExchangeImage.image = [UIImage imageNamed: [DefaultLoader sharedInstance].exchangeInfo[topRankPriceExchange][@"information"][@"image"]];
maxPremiumTabLowExchangeImage.image = [UIImage imageNamed:[DefaultLoader sharedInstance].exchangeInfo[bottomRankPriceExchange][@"information"][@"image"]];
// **************************************** Futures & Spot 괴리율 disparity ratio 1,2등 **************************************** //
/* #################### 데이터 들어갈 위치랑 Label, View 선언 및 세팅 #################### */
// ****** 괴리울 1위 UILabel, UIImageView 가져오기 ****** //
UILabel *cryptoPriceFuturesFirstLabel = _cryptoPriceFuturesFirstLabelList[i];
UILabel *changePercent24FuturesFirstLabel = _changePercent24FuturesFirstLabelList[i];
UIImageView *futuresFirstExchangeImage = _futuresFirstExchangeImageList[i];
// ****** Futures & Spot 괴리울 2위 UILabel, UIImageView 가져오기 ****** //
UILabel *cryptoPriceFuturesLastLabel = _cryptoPriceFuturesLastLabelList[i];
UILabel *changePercent24FuturesLastLabel = _changePercent24FuturesLastLabelList[i];
UIImageView *futuresLastExchangeImage = _futuresLastExchangeImageList[i];
/* #################### First, Last 정보들 찾아내기 #################### */
// ****** 사용할 변수들 선언해두기! Futures & Spot 괴리율 1,2등 거래소 정보 저장용 데이터 + 각 거래소의 index 찾을때 필요한 변수 선언 ****** //
NSString *disparityRatioFirstRankExchange;
NSString *disparityRatioLastRankExchange;
startIndex = 1;
NSUInteger firstRankIndex = 0;
NSUInteger lastRankIndex = 0;
// 괴리가격, 괴리율 정보 계산해서 데이터셋 만들기
NSMutableDictionary *disparityData = [@{} mutableCopy];
// 해당 symbol이 상장되어있는 거래소 개수 counting하기
aliveSymbolCount = 0;
for (int i=0; i<exchangeListFutures.count; i++) {
if ([allTicker.recentFuturesTicker[exchangeListFutures[i]] objectForKey:asset]) {
// 특정 자산 있는지 확인
if ([allTicker.recentFuturesTicker[exchangeListFutures[i]][asset] objectForKey:paymentCurrency]) {
// 특정 지불 화폐 있는지 확인
aliveSymbolCount++;
}
}
}
if (aliveSymbolCount == 0) {
// 해당 symbol이 상장된 거래소 정보를 찾는 로딩 중이거나 거래소 정보가 없는 경우 미노출
// pass
} else {
// 1개 이상의 상장된 거래소가 발견될 경우
// ****** rank를 매기기 위해 disparity ratio 계산하기 ****** //
for (NSUInteger i=0; i<exchangeListFutures.count; i++) {
if ([exchangeListSpot containsObject:exchangeListFutures[i]]) {
// 계산하려는 Futures 거래소를 Spot에서 취급하고 있는지 확인
if (allTicker.recentSpotTicker[exchangeListFutures[i]]) {
// 취급하려는 Futures exchange가 Spot Ticker에도 있는지 확인
if (allTicker.recentSpotTicker[exchangeListFutures[i]]) {
// exchange에 대해서
NSDictionary *priceOfFuturesData = allTicker.recentFuturesTicker[exchangeListFutures[i]];
NSDictionary *priceOfSpotData = allTicker.recentSpotTicker[exchangeListFutures[i]];
NSString *futuresPrice = priceOfFuturesData[asset][paymentCurrency][@"price"];
NSString *spotPrice = priceOfSpotData[asset][paymentCurrency][@"price"];
float disparityPrice = [futuresPrice floatValue] - [spotPrice floatValue];
float disparityRatio = disparityPrice / [spotPrice floatValue] * 100;
disparityData[exchangeListFutures[i]] = @{
@"disparityPrice": [NSString stringWithFormat:@"%f", disparityPrice],
@"disparityRatio": [NSString stringWithFormat:@"%f", disparityRatio],
};
}
}
}
}
// ****** first, last rank index 찾기 실행 ****** //
// first index 먼저 찾기 = 가장 큰 값 찾기
for (NSUInteger i=0; i<exchangeListFutures.count; i++) {
// firstRankIndex 배정 전에, 정상적인 유효 거래소 price의 index를 일단 찾아서 firstRankIndex로 넣고 그거랑 비교 진행
if (firstRankIndex != startIndex && allTicker.recentFuturesTicker[exchangeListFutures[i]][asset][paymentCurrency][@"price"]) {
// firstRankIndex랑 startIndex가 다른, 초기 상태이면서 price가 null이 아닌 유효한 값을 가질 때에만 진행, 만약 if문 안에 들어온다면 firstRankIndex와 startIndex, maxIndex가 같아지면서 해당 로직 미진행
// 목표는, 처음으로 값이 존재하는 exchange를 찾아 해당 거래소부터 대소비교를 진행하기 위함임!
startIndex = i;
firstRankIndex = i;
lastRankIndex = i;
}
}
for (NSUInteger i=0; i<exchangeListFutures.count; i++) {
if (startIndex == i) {
// first, last index 찾을때, startIndex는 어차피 firstRankIndex에서 가져가면서 체크하기때문에 체크 미진행
// pass
} else {
// first index 찾는 거 진행
if (disparityData[exchangeListFutures[i]]) {
// index가 깊어서, 해당 깊이의 index가 존재하는지 확인한 후 대소 비교 진행하도록 if 조건문 실행 -> 만약 없으면 그냥 pass!
if (disparityData[exchangeListFutures[i]][@"disparityRatio"]) {
if (disparityData[exchangeListFutures[i]][@"disparityPrice"]) {
// 값이 없는 거래소의 경우 대소비교 미진행을 위한 예외처리
// 아예 수집을 하지 못한 거래소가 최소값인 거래소로 나오지 않도록, 없는 값은 아닌지 확인! null을 floatValue 하면 0.000000 이 나오기 때문에 무조건 작은 index로 잡힙니다. null인 애는 제외하고 대소비교를 해야합니다.
if ([disparityData[exchangeListFutures[i]][@"disparityRatio"] floatValue] > [disparityData[exchangeListFutures[firstRankIndex]][@"disparityRatio"] floatValue]) {
// 가장 비싼 Exchange index 찾기
firstRankIndex = i;
} else if ([disparityData[exchangeListFutures[i]][@"disparityRatio"] floatValue] < [disparityData[exchangeListFutures[lastRankIndex]][@"disparityRatio"] floatValue]) {
// 가장 싼 Exchange index 찾기
lastRankIndex = i;
} else {
// 같은 경우에는 뒤쪽 array에 배치된 거래소를 lastRank로 배정
lastRankIndex = i;
}
}
}
}
}
}
// 거래소가 1개 뿐인 경우에는, first rank와 last rank가 같은 정보를 가지고 있습니다.
disparityRatioFirstRankExchange = exchangeListFutures[firstRankIndex];
disparityRatioLastRankExchange = exchangeListFutures[lastRankIndex];
}
/* #################### 데이터 출력해주기 #################### */
// ****** first rank 거래소 정보 설정 ****** //
futuresFirstExchangeImage.image = [UIImage imageNamed: [DefaultLoader sharedInstance].exchangeInfo[disparityRatioFirstRankExchange][@"information"][@"image"]];
// ****** first Rank 가격 설정 ****** //
NSDictionary *firstRankDisparityRatioData = allTicker.recentFuturesTicker[disparityRatioFirstRankExchange][asset][paymentCurrency];
// 데이터 출력하기
if (firstRankDisparityRatioData) {
// 데이터가 정상적으로 있는 경우
// 최신 가격
NSString *price = firstRankDisparityRatioData[@"price"];
cryptoPriceFuturesFirstLabel.text = price;
// ****** first Rank에서 해당 Futures의 최근 24시간 가격 변동률 ****** //
// 24시간 가격 변동률
NSString *changePricePercent24 = [firstRankDisparityRatioData[@"changePricePercent24"] stringByAppendingString:@"%"];
changePercent24FuturesFirstLabel.text = changePricePercent24;
// 24시간 가격 변동률에 색 입히거나, 없는 경우에 대한 노출 경우의 수 처리
if ([changePricePercent24 isEqual:@"-%"]) {
// pass = defaul Color로 text 색칠하고, 기존 % 없는 -로 출력하기, 대표적으로 Kraken 거래소는 최근 24시간 가격 변동률을 구할 수 없어서 해당 기능 넣었음, 하지만 Kraken 아예 수집 안하는거로 수정해서 필요한 내용은 아님
} else if ([changePricePercent24 hasPrefix:@"+"]) {
// 음수가 아니라 0 또는 양수인 경우
if ([firstRankDisparityRatioData[@"changePricePercent24"] floatValue] == 0) {
// 0.00이면 보합
if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
// 다크모드인 경우 글자색 흰색으로 원복
changePercent24FuturesFirstLabel.textColor = [UIColor whiteColor];
} else {
// 라이트모드인 경우 글자색 흑색으로 원복
changePercent24FuturesFirstLabel.textColor = [UIColor blackColor];
}
} else {
// 상승은 상승색
changePercent24FuturesFirstLabel.textColor = [UIColor greenColor];
}
} else {
// 하락은 하락색
changePercent24FuturesFirstLabel.textColor = [UIColor redColor];
}
} else {
// ****** first Rank에서 해당 Futures의 데이터가 없는 경우 ****** //
// 비정상 상태는 아니고, 0개의 거래소에 상장되어있는 경우 데이터가 아예 없어서 이런 현상 발생 + 이 경우에는 변동률 정보도 당연히 없음
// pass
}
// ****** disparity ratio - last rank 거래소 정보 설정 ****** //
NSDictionary *lastRankDisparityRatioData;
// 거래소가 1개만 있는 경우에는 first rank 정보와 last rank 정보가 같으므로 노출X
if (! disparityRatioLastRankExchange) {
// 해당하는 거래소가 없는 경우
// pass
} else {
// 거래소 정보가 있다면
futuresLastExchangeImage.image = [UIImage imageNamed:[DefaultLoader sharedInstance].exchangeInfo[disparityRatioLastRankExchange][@"information"][@"image"]];
// ****** disparity ratio - last rank 가격 설정 ****** //
lastRankDisparityRatioData = allTicker.recentFuturesTicker[disparityRatioLastRankExchange][asset][paymentCurrency];
}
if (lastRankDisparityRatioData[@"price"]) {
// 데이터가 정상적으로 있는 경우
NSString *price = lastRankDisparityRatioData[@"price"];
cryptoPriceFuturesLastLabel.text = price;
// ****** disparity ratio - first Rank에서 해당 Futures의 최근 24시간 가격 변동률 ****** //
// 24시간 가격 변동률
NSString *changePricePercent24;
if (lastRankDisparityRatioData[@"changePricePercent24"]) {
changePricePercent24 = [lastRankDisparityRatioData[@"changePricePercent24"] stringByAppendingString:@"%"];
changePercent24FuturesLastLabel.text = changePricePercent24;
if ([changePricePercent24 hasPrefix:@"+"]) {
// 음수가 아닌 0이거나 양수인 경우
if ([lastRankDisparityRatioData[@"changePricePercent24"] floatValue] == 0) {
// 0인 경우 기본색 = 0.00이면 보합
if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
// 다크모드인 경우 글자색 흰색으로 원복
changePercent24FuturesLastLabel.textColor = [UIColor whiteColor];
} else {
// 라이트모드인 경우 글자색 흑색으로 원복
changePercent24FuturesLastLabel.textColor = [UIColor blackColor];
}
} else {
// 양수인 경우
changePercent24FuturesLastLabel.textColor = [UIColor greenColor];
}
} else {
// 음수인 경우
changePercent24FuturesLastLabel.textColor = [UIColor redColor];
}
} else {
changePercent24FuturesLastLabel.text = @" - %";
NSLog(@"[WARN] recentFuturesTicker[disparityRatioLastRankExchange][asset] error : %@", allTicker.recentFuturesTicker[disparityRatioLastRankExchange][asset]);
}
} else {
// 데이터가 없는 경우 : 비정상 상태는 아니고, 1개의 거래소에만 상장되어있는 경우 데이터가 없어서 이런 현상 발생
// pass
}
if ( ! [disparityData isEqual:@{}]) {
// ****** Futures & Spot 비교 프리미엄 ****** //
// first Rank
NSString *firstRankPremiumPercent;
if ([disparityData objectForKey:disparityRatioFirstRankExchange]) {
UILabel *disparityRatioFirstRankLabel = _disparityRatioFirstRankLabelList[i];
// 괴리율 노출
NSString *disparityRatio = [NSString stringWithFormat:@"%.2f", [disparityData[disparityRatioFirstRankExchange][@"disparityRatio"] floatValue]];
if ([disparityRatio hasPrefix:@"-"]) {
// 음수인 경우
firstRankPremiumPercent = disparityRatio;
disparityRatioFirstRankLabel.textColor = [UIColor redColor];
} else {
firstRankPremiumPercent = [@"+" stringByAppendingString:disparityRatio];
if ([firstRankPremiumPercent floatValue] == 0) {
// 0인 경우에는 색 원복
if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
// 다크모드인 경우 글자색 흰색으로 원복
disparityRatioFirstRankLabel.textColor = [UIColor whiteColor];
} else {
// 라이트모드인 경우 글자색 흑색으로 원복
disparityRatioFirstRankLabel.textColor = [UIColor blackColor];
}
} else {
// 양수인 경우
disparityRatioFirstRankLabel.textColor = [UIColor greenColor];
}
}
disparityRatioFirstRankLabel.text = [firstRankPremiumPercent stringByAppendingString:@"%"];
}
// last rank
NSString *lastRankPremiumPercent;
// 거래소가 2개 이상 있는 경우이므로 둘 다 노출
UILabel *disparityRatioLastRankLabel = _disparityRatioLastRankLabelList[i];
// 괴리율 노출
NSString *disparityRatio = [NSString stringWithFormat:@"%.2f", [disparityData[disparityRatioLastRankExchange][@"disparityRatio"] floatValue]];
if ([disparityRatio hasPrefix:@"-"]) {
// 음수인 경우
lastRankPremiumPercent = disparityRatio;
disparityRatioLastRankLabel.textColor = [UIColor redColor];
} else {
// 양수이거나 0인 경우
lastRankPremiumPercent = [@"+" stringByAppendingString:disparityRatio];
if ([lastRankPremiumPercent floatValue] == 0) {
// 0인 경우에는 색 원복
if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
// 다크모드인 경우 글자색 흰색으로 원복
disparityRatioLastRankLabel.textColor = [UIColor whiteColor];
} else {
// 라이트모드인 경우 글자색 흑색으로 원복
disparityRatioLastRankLabel.textColor = [UIColor blackColor];
}
} else {
// 양수인 경우
disparityRatioLastRankLabel.textColor = [UIColor greenColor];
}
}
disparityRatioLastRankLabel.text = [lastRankPremiumPercent stringByAppendingString:@"%"];
}
}
// **************************************** [End] 카드뷰 목록 쭉 만들기 **************************************** //
}
@end
6. 결과 예시