1. 이전 포스팅 확인하기
https://growingsaja.tistory.com/933
2. 목표
a. default.json에 있는 소스 코드
3. default.json 수정
// vim default.json
{
"popularList_description": [
["isAvailable", "Fullname", "codeName", "paymentCurrency", "searchKeyword"],
["활성화 여부", "전체이름", "코드 또는 축약이름", "지급 화폐 단위", "검색 키워드"]
],
"popularList": [
[true, "Bitcoin", "BTC", "USDT", "BTCUSDT"],
[true, "Ethereum", "ETH", "USDT", "ETHUSDT"],
[true, "Ripple", "XRP", "USDT", "XRPUSDT"],
[true, "Dogecoin", "DOGE", "USDT", "DOGEUSDT"],
[true, "Cardano", "ADA", "USDT", "ADAUSDT"],
[true, "Solana", "SOL", "USDT", "SOLUSDT"],
[true, "Polkadot", "DOT", "USDT", "DOTUSDT"],
[true, "Chainlink", "LINK", "USDT", "LINKUSDT"],
[true, "Litecoin", "LTC", "USDT", "LTCUSDT"],
[true, "Tezos", "XTZ", "USDT", "XTZUSDT"],
[true, "Binance Coin", "BNB", "USDT", "BNBUSDT"],
[true, "Axie Infinity", "AXS", "USDT", "AXSUSDT"],
[true, "Decentraland", "MANA", "USDT", "MANAUSDT"],
[true, "Zilliqa", "ZIL", "USDT", "ZILUSDT"],
[true, "Klaytn", "KLAY", "USDT", "KLAYUSDT"],
[true, "Blur", "BLUR", "USDT", "BLURUSDT"],
[true, "Waves", "WAVES", "USDT", "WAVESUSDT"],
[true, "Shiba Inu", "SHIB", "USDT", "SHIBUSDT"],
[true, "Polygon", "MATIC", "USDT", "MATICUSDT"],
[true, "Stellar", "XLM", "USDT", "XLMUSDT"],
[true, "Filecoin", "FIL", "USDT", "FILUSDT"],
[true, "Cosmos", "ATOM", "USDT", "ATOMUSDT"],
[true, "Terra", "LUNA", "USDT", "LUNAUSDT"],
[true, "Algorand", "ALGO", "USDT", "ALGOUSDT"],
[true, "Aave", "AAVE", "USDT", "AAVEUSDT"],
[true, "Uniswap", "UNI", "USDT", "UNIUSDT"],
[true, "Wrapped Bitcoin", "WBTC", "USDT", "WBTCUSDT"],
[false, "Internet Computer", "ICP", "USDT", "ICPUSDT"],
[true, "OMG Network", "OMG", "USDT", "OMGUSDT"],
[true, "Elrond", "EGLD", "USDT", "EGLDUSDT"]
],
"externalApi": {
"OpenExchangeRates": {
// ...
4. PopularAssetListVC 파일 수정 - 주요 변경 소스 코드
// vim Controller/PopluarAssetListVC.m
// ...
/* #################### 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(@"****************************************************");
}
}
// ...
5. PopularAssetListVC 전체 소스코드
// vim Controller/PopluarAssetListVC.m
#import <Foundation/Foundation.h>
#import "PopluarAssetListVC.h"
// default.json 데이터 읽기
#import "DefaultLoader.h"
// 수시로 변경할 수 있는 설정값
#import "CustomizeSetting.h"
// 현재 시간 가져오는 용도
#import "DateTime.h"
// price 정보 저장
#import "AllSpotLiveData.h"
// 환율 서비스
#import "ServiceRecentRates.h"
@implementation PopluarAssetListVC {
// json에서 가져온 popularList raw 데이터
NSArray *popularList;
}
- (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] 뷰 그리기 **************************************** //
//카드뷰 배치에 필요한 변수를 설정합니다.
// 카드 목록 나열될 공간 세팅
// ****************************** //
// 목록 상단 공백 margin 설정
CGFloat cardViewTopStartMargin = 10.0;
// 카드 목록의 좌우 공백 각각의 margin
CGFloat horizontalMargin = 2.0;
// 카드와 카드 사이의 공백
CGFloat cardViewSpacing = 0.0;
// 카드뷰 목록 노출 시작 x축 위치 자동 설정
CGFloat cardViewXPosition = horizontalMargin;
// ****************************** //
// 카드 자체에 대한 세팅
// 카드 높이 길이 (상하 길이) 설정
CGFloat cardViewHeight = 44.0;
// 카드 좌우 길이 phone size 참조하여 자동 조정
CGFloat cardViewWidth = [UIScreen mainScreen].bounds.size.width - horizontalMargin * 2;
// 카드뷰 안에 내용 들어가는 공간까지의 margin
CGFloat basicMarginInCard = 4.0;
CGFloat defaultFontSize = 16.0;
// ****************************** //
// **************************************** [Start] 최상단에 기타 정보 공간 **************************************** //
/* #################### 현재 시간 정보 #################### */
self.topInfoTab = [[UIView alloc] initWithFrame:CGRectMake(cardViewXPosition, cardViewTopStartMargin - (cardViewSpacing + cardViewHeight), cardViewWidth, cardViewHeight)];
// 국기 너비 길이 (좌우 길이) 설정
CGFloat miniimage = 20.0;
// ****** 글로벌 지구 이모티콘 및 시간 설정 ****** //
self.earthLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard, basicMarginInCard, miniimage, 20)];
self.earthLabel.text = @"🌍";
// 표준 시간 = 그리니치 표준시 : 더블린, 에든버러, 리스본, 런던, 카사블랑카, 몬로비아
self.liveUTCLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard + miniimage, basicMarginInCard, cardViewWidth / 2, 20)];
self.liveUTCLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
// ****** 한국 이모티콘 및 시간 설정 ****** //
// 태극기
self.koreanFlagLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard, basicMarginInCard + cardViewHeight / 2, miniimage, 20)];
self.koreanFlagLabel.text = @"🇰🇷";
// 한국 시간
self.liveKSTLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard + miniimage, basicMarginInCard + cardViewHeight / 2, cardViewWidth / 2, 20)];
self.liveKSTLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
/* #################### 환율 정보 #################### */
// usdkrw 정보 출처 아이콘 이미지를 위한 기본 셋
self.ratesProviderImage = [[UIImageView alloc] initWithFrame:CGRectMake(basicMarginInCard + cardViewWidth/12*7, basicMarginInCard, miniimage, 20)];
self.ratesProviderImage.contentMode = UIViewContentModeScaleAspectFit; // 해당 옵션을 사용하여 가로세로 비율 유지 크기입니다.
// usdkrw 환율
self.ratesLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard + cardViewWidth/12*7, basicMarginInCard, cardViewWidth/8*3, 20)];
self.ratesLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
self.ratesLabel.textAlignment = NSTextAlignmentRight;
// 기준 시간
self.ratesUpdateDateTimeLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard + cardViewWidth/12*7, basicMarginInCard + cardViewHeight / 2, cardViewWidth/8*3, 20)];
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];
self.cryptoNameLabelList = [@[] mutableCopy];
self.cryptoPriceHighLabelList = [@[] mutableCopy];
self.changePricePercent24HighLabelList = [@[] mutableCopy];
self.exchangeLogoHighImageList = [@[] mutableCopy];
self.cryptoPriceLowLabelList = [@[] mutableCopy];
self.changePricePercent24LowLabelList = [@[] mutableCopy];
self.exchangeLogoLowImageList = [@[] mutableCopy];
self.maxPricePremiumPercentLabelList = [@[] mutableCopy];
self.maxPremiumTabHighExchangeImageList = [@[] mutableCopy];
self.maxPremiumTabLowExchangeImageList = [@[] mutableCopy];
for (int i=0; i<popularList.count; i++) {
/* #################### 카드뷰 기본 세팅 #################### */
UIView *cardView = [[UIView alloc] initWithFrame:CGRectMake(cardViewXPosition, cardViewTopStartMargin + i * (cardViewSpacing + 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(basicMarginInCard, basicMarginInCard, cardViewWidth / 4, 20)];
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 / 5, basicMarginInCard, cardViewWidth / 5, 20)];
cryptoPriceHighLabel.textAlignment = NSTextAlignmentRight;
cryptoPriceHighLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
// 최근 24시간동안의 가격 변동률 정보 제공을 위한 기본 셋
UILabel *changePricePercent24HighLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 20 * 9, basicMarginInCard, cardViewWidth/3, 20)];
changePricePercent24HighLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
// 거래소 이름 및 로고 레이블을 생성하고 카드뷰에 추가합니다.
// 거래소 아이콘을 위한 기본 셋
UIImageView *exchangeLogoHighImage = [[UIImageView alloc] initWithFrame:CGRectMake(cardViewWidth / 5 * 2, basicMarginInCard, miniimage, miniimage)];
exchangeLogoHighImage.contentMode = UIViewContentModeScaleAspectFit; // 해당 옵션을 사용하여 가로세로 비율 유지 크기입니다.
/* #################### Low spot 현재 가격, 거래소 정보 노출 라벨 세팅 #################### */
// 암호화폐 가격 레이블을 생성하고 카드뷰에 추가합니다.
// 가격위한 기본 셋
UILabel *cryptoPriceLowLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 5, cardViewHeight/2, cardViewWidth / 5, 20)];
cryptoPriceLowLabel.textAlignment = NSTextAlignmentRight;
cryptoPriceLowLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
// 최근 24시간동안의 가격 변동률 정보 제공을 위한 기본 셋
UILabel *changePricePercent24LowLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 20 * 9, cardViewHeight/2, cardViewWidth/3, 20)];
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(basicMarginInCard, cardViewHeight/2, cardViewWidth / 8 + miniimage, 20)];
maxPricePremiumPercentLabel.textAlignment = NSTextAlignmentRight;
maxPricePremiumPercentLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
// top rank 거래소 아이콘을 위한 기본 셋
UIImageView *maxPremiumTabHighExchangeImage = [[UIImageView alloc] initWithFrame:CGRectMake(basicMarginInCard, cardViewHeight/2, miniimage, miniimage)];
maxPremiumTabHighExchangeImage.contentMode = UIViewContentModeScaleAspectFit; // 해당 옵션을 사용하여 가로세로 비율 유지 크기입니다.
// bottom rank 거래소 아이콘을 위한 기본 셋
UIImageView *maxPremiumTabLowExchangeImage = [[UIImageView alloc] initWithFrame:CGRectMake(basicMarginInCard + 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];
// cardView를 self.scrollView에 추가합니다.
[self.scrollView addSubview:cardView];
// 레이블 세팅 완료된 cardView를 CardViewList에 넣기
[self.cardViewList addObject: cardView];
}
// 상하 스크롤 최대치 자동 설정
CGFloat contentHeight = popularList.count * (cardViewHeight + cardViewSpacing);
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 불러오기 ****** //
_exchangeInfo = [DefaultLoader sharedInstance].exchangeInfo;
}
/* #################### 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 blueColor];
} else {
// 양수인 경우 + 붙여서 합치기
ratesChangeInfo = [@" (+" stringByAppendingString:ratesChangeInfo];
self.ratesLabel.textColor = [UIColor redColor];
}
// 환율 호가
NSString *ratesMainInfo = ratesInfo.usdkrw;
self.ratesLabel.text = [ratesMainInfo stringByAppendingString:ratesChangeInfo];
// ****** rates 정보 출처 provider 이미지 설정 ****** //
if ([ratesInfo.provider isEqual:@"하나은행"]) {
self.ratesProviderImage.image = [UIImage imageNamed:[DefaultLoader sharedInstance].externalApi[@"DunamuQuotation"][@"image"]];
} else {
self.ratesProviderImage.image = [UIImage imageNamed:[DefaultLoader sharedInstance].externalApi[ratesInfo.provider][@"image"]];
if ( ! [ratesInfo.provider isEqual:@"OpenExchangeRates"]) {
// Error
NSLog(@"[WARN] Rates Image Error!");
}
}
} else {
self.ratesLabel.text = @"-";
}
// 환율 최근 업데이트 일시 노출
self.ratesUpdateDateTimeLabel.text = ratesInfo.recentDateTime;
// ****** Spot 가격 정보 불러오기 ****** //
AllSpotLiveData *allSpot = [AllSpotLiveData sharedInstance];
// ****** 취급 상품 정리 및 데이터 가공 for 동일 상품에 대한 거래소간 가격 비교 기존 Array 제작 ****** //
NSArray *exchangeNameList = [_exchangeInfo allKeys];
/* #################### [Start] 카드뷰 데이터 업데이트 #################### */
for (int i=0; i<popularList.count; i++) {
// 기준 key인 symbol 변수 만들기
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<exchangeNameList.count; i++) {
if ([allSpot.recentPriceData[exchangeNameList[i]] objectForKey:asset]) {
// 특정 자산 있는지 확인
if ([allSpot.recentPriceData[exchangeNameList[i]][asset] objectForKey:paymentCurrency]) {
// 특정 지불 화폐 있는지 확인
aliveSymbolCount++;
}
}
}
if (aliveSymbolCount >= 1) {
// 가격 정보가 1개 이상인 경우
for (NSUInteger i=0; i<exchangeNameList.count; i++) {
// minIndex 배정 전에, 정상적인 유효 거래소 price의 index를 일단 찾아서 minIndex로 넣고 그거랑 비교 진행
if (minIndex != startIndex && allSpot.recentPriceData[exchangeNameList[i]][asset][paymentCurrency][@"price"]) {
// minIndex랑 startIndex가 다른, 초기 상태이면서 price가 null이 아닌 유효한 값을 가질 때에만 진행, 만약 if문 안에 들어온다면 minIndex와 startIndex가 같아지면서 해당 로직 미진행
startIndex = i;
minIndex = i;
}
}
for (NSUInteger i=0; i<exchangeNameList.count; i++) {
// min, max index 찾기 실행
if (startIndex == i) {
// min, max index 찾을때, startIndex는 어차피 minIndex에서 가져가면서 체크하기때문에 체크 미진행
// pass
} else {
// min, max index 찾는 거 실행
if (allSpot.recentPriceData[exchangeNameList[i]]) {
// index가 깊어서, 해당 깊이의 index가 존재하는지 확인한 후 대소 비교 진행하도록 if 조건문 실행 -> 만약 없으면 그냥 pass!
if (allSpot.recentPriceData[exchangeNameList[i]][asset]) {
if (allSpot.recentPriceData[exchangeNameList[i]][asset][paymentCurrency]) {
if (allSpot.recentPriceData[exchangeNameList[i]][asset][paymentCurrency][@"price"]) {
// 값이 없는 거래소의 경우 대소비교 미진행을 위한 예외처리
// !!! 한번에 여러 index에 접근하여 데이터를 참조하는 경우 잘못된 메모리 주소 참조를 방지할 수 있습니다. !!!
// 아예 수집을 하지 못한 거래소가 최소값인 거래소로 나오지 않도록, 없는 값은 아닌지 확인! null을 floatValue 하면 0.000000 이 나오기 때문에 무조건 작은 index로 잡힙니다. null인 애는 제외하고 대소비교를 해야합니다.
if ([allSpot.recentPriceData[exchangeNameList[i]][asset][paymentCurrency][@"price"] floatValue] <= [allSpot.recentPriceData[exchangeNameList[minIndex]][asset][paymentCurrency][@"price"] floatValue]) {
// 가장 저렴한 Exchange index 찾기
minIndex = i;
}
if ([allSpot.recentPriceData[exchangeNameList[i]][asset][paymentCurrency][@"price"] floatValue] > [allSpot.recentPriceData[exchangeNameList[maxIndex]][asset][paymentCurrency][@"price"] floatValue]) {
// 가장 비싼 Exchange index 찾기
maxIndex = i;
}
}
}
}
}
}
}
// 찾은 최소 index와 최대 index를 통해 top, bottom 거래소명 값 저장
topRankPriceExchange = exchangeNameList[maxIndex];
bottomRankPriceExchange = exchangeNameList[minIndex];
} else {
// 가격 정보가 모두 없을 경우 (로딩중이거나 못불러오거나) 전부 0으로 노출
topRankPriceExchange = exchangeNameList[0];
bottomRankPriceExchange = exchangeNameList[0];
allSpot.recentPriceData[topRankPriceExchange][asset][paymentCurrency][@"price"] = 0;
allSpot.recentPriceData[bottomRankPriceExchange][asset][paymentCurrency][@"price"] = 0;
}
// ****** top price spot 거래소명 및 이미지 설정 ****** //
((UIImageView *)_exchangeLogoHighImageList[i]).image = [UIImage imageNamed:[DefaultLoader sharedInstance].exchangeInfo[topRankPriceExchange][@"information"][@"image"]];
// ****** top price spot 가격 설정 ****** //
if (allSpot.recentPriceData[topRankPriceExchange][asset][paymentCurrency][@"price"]) {
// 데이터가 정상적으로 있는 경우
// 최신 가격
NSString *price = allSpot.recentPriceData[topRankPriceExchange][asset][paymentCurrency][@"price"];
((UILabel *)_cryptoPriceHighLabelList[i]).text = price;
// ****** top price spot 최근 24시간 가격 변동률 ****** //
// 24시간 가격 변동률
NSString *changePricePercent24 = [allSpot.recentPriceData[topRankPriceExchange][asset][paymentCurrency][@"changePricePercent24"] stringByAppendingString:@"%"];
((UILabel *)_changePricePercent24HighLabelList[i]).text = changePricePercent24;
// 24시간 가격 변동률에 색 입히거나, 없는 경우에 대한 노출 경우의 수 처리
if ([changePricePercent24 isEqual:@"-%"]) {
// pass = defaul Color로 text 색칠하고, 기존 % 없는 -로 출력하기, 대표적으로 Kraken 거래소는 최근 24시간 가격 변동률을 구할 수 없어서 해당 기능 넣었음, 하지만 Kraken 아예 수집 안하는거로 수정해서 필요한 내용은 아님
((UILabel *)_changePricePercent24HighLabelList[i]).text = @" - ";
} else if ([changePricePercent24 hasPrefix:@"+"]) {
// 상승은 빨간색
((UILabel *)_changePricePercent24HighLabelList[i]).textColor = [UIColor redColor];
} else {
// 하락은 파란색
((UILabel *)_changePricePercent24HighLabelList[i]).textColor = [UIColor blueColor];
}
} else {
// ****** top price spot 데이터가 없는 경우 ****** //
// 비정상 상태는 아니고, 0개의 거래소에 상장되어있는 경우 데이터가 아예 없어서 이런 현상 발생 + 이 경우에는 변동률 정보도 당연히 없음
((UILabel *)_cryptoPriceHighLabelList[i]).text = @" - ";
((UILabel *)_changePricePercent24HighLabelList[i]).text = @" - ";
}
// ****** bottom price spot 거래소 이미지 설정 ****** //
((UIImageView *)_exchangeLogoLowImageList[i]).image = [UIImage imageNamed:[DefaultLoader sharedInstance].exchangeInfo[bottomRankPriceExchange][@"information"][@"image"]];
// bottom price 가격 확보 실패시
if (allSpot.recentPriceData[bottomRankPriceExchange][asset][paymentCurrency][@"price"]) {
// 데이터가 정상적으로 있는 경우
((UILabel *)_cryptoPriceLowLabelList[i]).text = allSpot.recentPriceData[bottomRankPriceExchange][asset][paymentCurrency][@"price"];
NSString *changePricePercent24 = [allSpot.recentPriceData[bottomRankPriceExchange][asset][paymentCurrency][@"changePricePercent24"] stringByAppendingString:@"%"];
((UILabel *)_changePricePercent24LowLabelList[i]).text = changePricePercent24;
if ([changePricePercent24 hasPrefix:@"+"]) {
((UILabel *)_changePricePercent24LowLabelList[i]).textColor = [UIColor redColor];
} else {
((UILabel *)_changePricePercent24LowLabelList[i]).textColor = [UIColor blueColor];
}
} else {
// 데이터가 없는 경우 : 비정상 상태는 아니고, 1개의 거래소에만 상장되어있는 경우 데이터가 없어서 이런 현상 발생
((UILabel *)_cryptoPriceLowLabelList[i]).text = @" - ";
((UILabel *)_changePricePercent24LowLabelList[i]).text = @" - ";
}
/* #################### 프리미엄 비교 #################### */
float topPrice = [allSpot.recentPriceData[topRankPriceExchange][asset][paymentCurrency][@"price"] floatValue];
float bottomPrice = [allSpot.recentPriceData[bottomRankPriceExchange][asset][paymentCurrency][@"price"] floatValue];
// 소수점 둘째자리 수까지
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:@"%"];
}
// 특정 값 이상 프리미엄 발생시, 텍스트에 배경색 입히기
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)];
}
// 특성 적용
((UILabel *)_maxPricePremiumPercentLabelList[i]).attributedText = attributedString;
((UIImageView *)_maxPremiumTabHighExchangeImageList[i]).image = [UIImage imageNamed: [DefaultLoader sharedInstance].exchangeInfo[topRankPriceExchange][@"information"][@"image"]];
((UIImageView *)_maxPremiumTabLowExchangeImageList[i]).image = [UIImage imageNamed:[DefaultLoader sharedInstance].exchangeInfo[bottomRankPriceExchange][@"information"][@"image"]];
}
// **************************************** [End] 카드뷰 목록 쭉 만들기 **************************************** //
}
@end