1. 이전 포스팅 확인하기
https://growingsaja.tistory.com/922
2. 이번 목표
symbol 기준으로 최근 24시간동안의 가격 변동률 정보 노출 기능 추가
3. Binance 파일 내용 확인
// vim SpotBinance.h
@interface SpotBinance : NSObject
// symbol 기준으로 현재 실시간 정보 얻기
@property (readwrite, strong, nonatomic) NSMutableDictionary *dataFromSymbol;
// asset, payment currency 기준으로 현재 실시간 정보 얻기
@property (readwrite, strong, nonatomic) NSMutableDictionary *dataFromAsset;
// payment currency 정보
@property (readwrite, strong, nonatomic) NSMutableDictionary *paymentCurrencyInfo;
+(instancetype)sharedInstance;
-(void) fetchDataWithCompletion;
@end
// vim SpotBinance.m
#import <Foundation/Foundation.h>
#import "SpotBinance.h"
// 주요 데이터 get
#import "DefaultLoader.h"
// api 연결
#import "APIManager.h"
@implementation SpotBinance
+(instancetype)sharedInstance {
static SpotBinance *sharedInstance = nil;
static dispatch_once_t oneToken;
dispatch_once(&oneToken, ^{
sharedInstance = [[self alloc] init];
sharedInstance.dataFromSymbol = [@{} mutableCopy];
sharedInstance.dataFromAsset = [@{} mutableCopy];
sharedInstance.paymentCurrencyInfo = [@{
@"spot": [@[] mutableCopy],
@"futures": [@[] mutableCopy]
} mutableCopy];
// payment currency 데이터 참조해서 symbol 찢기 용도 데이터셋 선언
[sharedInstance getOnlyPaymentCurrencyData];
});
return sharedInstance;
}
-(void) fetchDataWithCompletion {
// **************************************** [Start] api 콜 준비 **************************************** //
NSDictionary *binance = [DefaultLoader sharedInstance].exchangeInfo[@"Binance"];
APIManager* tryApiCall = [APIManager sharedInstance];
// **************************************** [Start] Bybit 현재 호가, 변동률 등 주요 데이터 가져오기 **************************************** //
NSString *apiURL = [binance[@"baseUrlList"][0] stringByAppendingString:binance[@"publicApi"][@"ticker"]];
[tryApiCall fetchDataFromAPI:apiURL withCompletionHandler:^(NSDictionary *jsonResponse, NSError *error) {
if (error) {
NSLog(@"[Error] %@", error.localizedDescription);
} else {
// 여기에서 jsonResponse를 가공 한 후 앱에서 사용하실 수 있습니다.
if ([jsonResponse isKindOfClass:[NSDictionary class]]) {
// ****** 비정상으로 실패시 ****** //
// 에러가 발생한 경우, code, msg라는 key를 가지는 json으로 return한다.
if ([jsonResponse[@"code"] isEqual:@-1003]) {
// -1003 코드로 문제 발생, api call 이 1분 안에 너무 많이 진행되어 발생한 이슈
NSLog(@"[WARN] Binance Api Call Not Work with code -1003 Because Binance Api Call Limit is over in a minute 1,200 weight : %@", jsonResponse[@"msg"]);
} else {
// -1003 코드는 이전에 받아봤는데... 이건 무슨 에러인지 확인 필요
NSLog(@"[ERROR] Binance API => not -1003 code : %@", jsonResponse[@"msg"]);
}
} else {
// ****** 정상 성공시 ****** //
// 정상인 경우 array로 return한다.
// result 안의 value만 추출
NSArray* resultOfApi = (NSArray *)jsonResponse;
// api로 받은 데이터 깔끔한 dictionary로 가공하기
for (int i=0; i<resultOfApi.count; i++) {
if ([resultOfApi[i][@"firstId"] isEqual:@-1] && [resultOfApi[i][@"lastId"] isEqual:@-1]) {
// 상장되지 않은 symbol 거르기
// pass
} else {
// 상장되어 있는 symbol만 포함하기
NSString *symbol = resultOfApi[i][@"symbol"]; // 심볼
// 가격 정보는 데이터를 다른 거래소 정보와 함께 노출시 통일성있게 하기 위해 뒤에 불필요한 소수점 맨 뒤쪽의 0들을 제거합니다.
NSString *price = resultOfApi[i][@"lastPrice"]; // 가격
if ([price floatValue] >= 1000) {
price = [price substringWithRange:NSMakeRange(0, price.length-6)];
} else if ([price floatValue] >= 100) {
price = [price substringWithRange:NSMakeRange(0, price.length-5)];
} else if ([price floatValue] >= 1) {
price = [price substringWithRange:NSMakeRange(0, price.length-4)];
} else if ([price floatValue] >= 0.1) {
price = [price substringWithRange:NSMakeRange(0, price.length-3)];
} else if ([price floatValue] >= 0.01) {
price = [price substringWithRange:NSMakeRange(0, price.length-2)];
} else if ([price floatValue] >= 0.001) {
price = [price substringWithRange:NSMakeRange(0, price.length-1)];
} else {
// price값 그대로 가져다씁니다.
}
// 변동률은 데이터를 다른 거래소 정보와 함께 노출시 통일성있게 하기 위해 부분 가공이 진행됩니다.
NSString *changePricePercent24 = resultOfApi[i][@"priceChangePercent"]; // 근24시간 가격 변동률
changePricePercent24 = [NSString stringWithFormat:@"%.2f", [changePricePercent24 floatValue]];
if (![changePricePercent24 hasPrefix:@"-"]) {
// 음수가 아닌 경우, +로 기호 맨앞에 붙여주기
changePricePercent24 = [@"+" stringByAppendingString:changePricePercent24];
}
NSString *turnover24 = resultOfApi[i][@"quoteVolume"]; // 근24시간 거래대금
NSString *volume24 = resultOfApi[i][@"volume"]; // 근24시간 거래량
self.dataFromSymbol[symbol] = [@{} mutableCopy];
self.dataFromSymbol[symbol][@"price"] = price; // 가격
self.dataFromSymbol[symbol][@"changePricePercent24"] = changePricePercent24; // 근24시간 가격 변동률
self.dataFromSymbol[symbol][@"turnover24"] = turnover24; // 근24시간 거래대금
self.dataFromSymbol[symbol][@"volume24"] = volume24; // 근24시간 거래량
// dataFromAsset
NSString *asset;
NSString *paymentCurrenct;
// 사용된 payment currency 찾고 symbol을 찢기
for (NSString *eachPaymentCurrenct in self.paymentCurrencyInfo[@"spot"]) {
// 뒤에 payment currency 매치되는거 찾은 경우 분리 진행
if ([symbol hasSuffix:eachPaymentCurrenct]) {
asset = [[symbol componentsSeparatedByString:eachPaymentCurrenct] objectAtIndex:0];
paymentCurrenct = eachPaymentCurrenct;
}
}
// dataFromAsset 데이터셋 만들어주고
if (asset && paymentCurrenct) {
// 취급하는 payment currency인 경우
self.dataFromAsset[asset] = [@{} mutableCopy];
self.dataFromAsset[asset][paymentCurrenct] = [@{} mutableCopy];
// dataFromAsset 데이터셋 만들어주고
self.dataFromAsset[asset][paymentCurrenct][@"price"] = price; // 가격
self.dataFromAsset[asset][paymentCurrenct][@"changePricePercent24"] = changePricePercent24; // 근24시간 가격 변동률
self.dataFromAsset[asset][paymentCurrenct][@"turnover24"] = turnover24; // 근24시간 거래대금
self.dataFromAsset[asset][paymentCurrenct][@"volume24"] = volume24; // 근24시간 거래량
} else {
// 취급하지 않는 payment currency인 경우
// pass합니다.
NSLog(@"[WARN] not include : %@", symbol);
}
}
}
// api로 받은 데이터 깔끔한 dictionary로 가공하기
}
}
}];
}
// _paymentCurrencyInfo : spot, futures 각각에서 취급하는 paymentCurrencyList를 보유하게 됩니다. 해당 정보를 토대로, symbol 데이터를 활용해 asset과 paymentCurrency를 분리할 수 있습니다.
-(void) getOnlyPaymentCurrencyData {
// bybit 관련 default 데이터 불러오기
NSDictionary *binance = [DefaultLoader sharedInstance].exchangeInfo[@"Binance"];
NSArray *categoryList = @[@"spot", @"futures"];
// 데이터 정리하기
for (NSString *category in categoryList) {
for (NSString *group in [binance[category] allKeys]) {
if ([binance[category][group][@"category"] isEqual:category]) {
// spot
[_paymentCurrencyInfo[category] addObjectsFromArray:binance[category][group][@"paymentCurrencyList"]];
} else if ([binance[category][group][@"category"] isEqual:@"coin-m"]) {
// usd
[_paymentCurrencyInfo[category] addObjectsFromArray:binance[category][@"coinMargin"][@"paymentCurrencyList"]];
} else if ([binance[category][group][@"category"] isEqual:@"usd(s)-m"]) {
// usdt
[_paymentCurrencyInfo[category] addObjectsFromArray:binance[category][@"stableMargin"][@"paymentCurrencyList"]];
} else {
NSLog(@"[WARN] Bybit spot payment currency not match : Skip...");
}
}
}
}
@end
4. Bybit 파일 내용 확인
// vim SpotBybit.h
@interface SpotBybit : NSObject
// symbol 기준으로 현재 실시간 정보 얻기
@property (readwrite, strong, nonatomic) NSMutableDictionary *dataFromSymbol;
// asset, payment currency 기준으로 현재 실시간 정보 얻기
@property (readwrite, strong, nonatomic) NSMutableDictionary *dataFromAsset;
// payment currency 정보
@property (readwrite, strong, nonatomic) NSMutableDictionary *paymentCurrencyInfo;
+(instancetype)sharedInstance;
-(void) fetchDataWithCompletion;
@end
// vim SpotBybit.m
#import <Foundation/Foundation.h>
#import "SpotBybit.h"
// 주요 데이터 get
#import "DefaultLoader.h"
// api 연결
#import "APIManager.h"
// 시간 unix to string
#import "CRTunixToString.h"
@implementation SpotBybit
+(instancetype)sharedInstance {
static SpotBybit *sharedInstance = nil;
static dispatch_once_t oneToken;
dispatch_once(&oneToken, ^{
sharedInstance = [[self alloc] init];
sharedInstance.dataFromSymbol = [@{} mutableCopy];
sharedInstance.dataFromAsset = [@{} mutableCopy];
sharedInstance.paymentCurrencyInfo = [@{
@"spot": [@[] mutableCopy],
@"futures": [@[] mutableCopy]
} mutableCopy];
// payment currency 데이터 참조해서 symbol 찢기 용도 데이터셋 선언
[sharedInstance getOnlyPaymentCurrencyData];
});
return sharedInstance;
}
-(void) fetchDataWithCompletion {
// **************************************** [Start] default 데이터 가공 **************************************** //
// bybit 관련 default 데이터 불러오기
NSDictionary *bybit = [DefaultLoader sharedInstance].exchangeInfo[@"Bybit"];
// **************************************** [Start] api 콜 준비 **************************************** //
APIManager* tryApiCall = [APIManager sharedInstance];
// **************************************** [Start] Bybit 현재 호가, 변동률 등 주요 데이터 가져오기 **************************************** //
NSString *apiURL = [bybit[@"baseUrlList"][0] stringByAppendingString:bybit[@"publicApi"][@"ticker"]];
apiURL = [apiURL stringByAppendingString:@"?category=spot"];
[tryApiCall fetchDataFromAPI:apiURL withCompletionHandler:^(NSDictionary *jsonResponse, NSError *error) {
if (error) {
NSLog(@"[Error] %@", error.localizedDescription);
} else {
// 여기에서 jsonResponse를 가공 한 후 앱에서 사용하실 수 있습니다.
NSNumber *unixTimestamp = jsonResponse[@"time"];
CRTunixToString *now = [CRTunixToString sharedInstance];
[now stringFromUnix:unixTimestamp andFormat:@"yyyy-MM-dd (E) HH:mm:ss"];
if ([jsonResponse[@"retCode"] isEqual:@0]) {
// ****** 정상 성공시 ****** //
// 에러가 발생하지 않은 경우 result라는 키값을 가지며 그 안에 category 및 list로 상품별 정보를 가집니다. 추가로 time, retExtInfo라는 키값을 포함해 총 4개의 데이터를 return합니다. (time은 unix timestamp입니다.)
// result 안의 value만 추출
NSArray* resultOfApi = jsonResponse[@"result"][@"list"];
// api로 받은 데이터 깔끔한 dictionary로 가공하기
for (int i=0; i<resultOfApi.count; i++) {
// ****** api의 데이터들 수집한거 필요시 가공해서 저장하기 ****** //
NSString *symbol = resultOfApi[i][@"symbol"]; // 심볼
NSString *price = resultOfApi[i][@"lastPrice"]; // 가격
// 변동률은 데이터를 다른 거래소 정보와 함께 노출시 통일성있게 하기 위해 부분 가공이 진행됩니다.
NSString *changePricePercent24 = [NSString stringWithFormat:@"%.2f", [resultOfApi[i][@"price24hPcnt"] floatValue] * 100]; // 근24시간 가격 변동률
if (![changePricePercent24 hasPrefix:@"-"]) {
// 음수가 아닌 경우, +로 기호 맨앞에 붙여주기
changePricePercent24 = [@"+" stringByAppendingString:changePricePercent24];
}
NSString *volume24 = resultOfApi[i][@"turnover24h"]; // 근24시간 거래대금
NSString *turnover24 = resultOfApi[i][@"volume24h"]; // 근24시간 거래량
// dataFromSymbol
self.dataFromSymbol[symbol] = [@{} mutableCopy];
self.dataFromSymbol[symbol][@"price"] = price; // 가격
self.dataFromSymbol[symbol][@"changePricePercent24"] = changePricePercent24; // 근24시간 가격 변동률
self.dataFromSymbol[symbol][@"turnover24"] = turnover24; // 근24시간 거래대금
self.dataFromSymbol[symbol][@"volume24"] = volume24; // 근24시간 거래량
// dataFromAsset
NSString *asset;
NSString *paymentCurrenct;
// 사용된 payment currency 찾고 symbol을 찢기
for (NSString *eachPaymentCurrenct in self.paymentCurrencyInfo[@"spot"]) {
// 뒤에 payment currency 매치되는거 찾은 경우 분리 진행
if ([symbol hasSuffix:eachPaymentCurrenct]) {
asset = [[symbol componentsSeparatedByString:eachPaymentCurrenct] objectAtIndex:0];
paymentCurrenct = eachPaymentCurrenct;
}
}
// dataFromAsset 데이터셋 만들어주고
if (asset && paymentCurrenct) {
// 취급하는 payment currency인 경우
self.dataFromAsset[asset] = [@{} mutableCopy];
self.dataFromAsset[asset][paymentCurrenct] = [@{} mutableCopy];
// dataFromAsset 데이터셋 만들어주고
self.dataFromAsset[asset][paymentCurrenct][@"price"] = price; // 가격
self.dataFromAsset[asset][paymentCurrenct][@"changePricePercent24"] = changePricePercent24; // 근24시간 가격 변동률
self.dataFromAsset[asset][paymentCurrenct][@"turnover24"] = turnover24; // 근24시간 거래대금
self.dataFromAsset[asset][paymentCurrenct][@"volume24"] = volume24; // 근24시간 거래량
} else {
// 취급하지 않는 payment currency인 경우
// pass합니다. 그래도 뭐가 skip되는지는 알아야하니까 1번만 출력합니다.
static dispatch_once_t oneTokenTmp;
dispatch_once(&oneTokenTmp, ^{
NSLog(@"[WARN] not include : %@", symbol);
});
}
}
} else {
// ****** 비정상으로 실패시 ****** //
// 에러가 발생한 경우 retCode는 0이 아니며, retMsg에 관련 상세 내용이 있습니다.
NSLog(@"[WARN] Return Code : %@", jsonResponse[@"retCode"]);
NSLog(@"[WARN] Return Message : %@", jsonResponse[@"retMsg"]);
// api로 받은 데이터 깔끔한 dictionary로 가공하기
}
}
}];
}
// _paymentCurrencyInfo : spot, futures 각각에서 취급하는 paymentCurrencyList를 보유하게 됩니다. 해당 정보를 토대로, symbol 데이터를 활용해 asset과 paymentCurrency를 분리할 수 있습니다.
-(void) getOnlyPaymentCurrencyData {
// bybit 관련 default 데이터 불러오기
NSDictionary *bybit = [DefaultLoader sharedInstance].exchangeInfo[@"Bybit"];
NSArray *categoryList = @[@"spot", @"futures"];
// 데이터 정리하기
for (NSString *category in categoryList) {
for (NSString *group in [bybit[category] allKeys]) {
if ([bybit[category][group][@"category"] isEqual:category]) {
// spot
[_paymentCurrencyInfo[category] addObjectsFromArray:bybit[category][group][@"paymentCurrencyList"]];
} else if ([bybit[category][group][@"category"] isEqual:@"inverse"]) {
// usd
[_paymentCurrencyInfo[category] addObjectsFromArray:bybit[category][@"coinMargin"][@"paymentCurrencyList"]];
} else if ([bybit[category][group][@"category"] isEqual:@"linear"]) {
// usdt
[_paymentCurrencyInfo[category] addObjectsFromArray:bybit[category][@"stableMargin"][@"paymentCurrencyList"]];
} else {
NSLog(@"[WARN] Bybit spot payment currency not match : Skip...");
}
}
}
}
@end
5. 활용하는 부분 주요 코드
// vim PopularAssetListVC.m
// ...
/* #################### 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, miniImange, miniImange)];
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, miniImange, miniImange)];
exchangeLogoLowImage.contentMode = UIViewContentModeScaleAspectFit; // 해당 옵션을 사용하여 가로세로 비율 유지 크기입니다.
/* #################### spot 프리미엄 노출 세팅 #################### */
// 기본 셋
// ...
// ****** top price spot 거래소명 및 이미지 설정 ****** //
((UIImageView *)_exchangeLogoHighImageList[i]).image = [UIImage imageNamed:[DefaultLoader sharedInstance].exchangeInfo[topRankPriceExchange][@"image"]];;
// top price 가격 확보 실패시
if (_popularSpotPriceList[topRankPriceExchange][symbol][@"price"]) {
// 데이터가 정상적으로 있는 경우
((UILabel *)_cryptoPriceHighLabelList[i]).text = _popularSpotPriceList[topRankPriceExchange][symbol][@"price"];
NSString *changePricePercent24 = [_popularSpotPriceList[topRankPriceExchange][symbol][@"changePricePercent24"] stringByAppendingString:@"%"];
((UILabel *)_changePricePercent24HighLabelList[i]).text = changePricePercent24;
if ([changePricePercent24 hasPrefix:@"+"]) {
((UILabel *)_changePricePercent24HighLabelList[i]).textColor = [UIColor redColor];
} else {
((UILabel *)_changePricePercent24HighLabelList[i]).textColor = [UIColor blueColor];
}
} else {
// 데이터가 없는 경우 : 비정상 상태는 아니고, 0개의 거래소에 상장되어있는 경우 데이터가 아예 없어서 이런 현상 발생
((UILabel *)_cryptoPriceHighLabelList[i]).text = @" - ";
((UILabel *)_changePricePercent24HighLabelList[i]).text = @" - ";
}
// ****** bottom price spot 거래소 이미지 설정 ****** //
((UIImageView *)_exchangeLogoLowImageList[i]).image = [UIImage imageNamed:[DefaultLoader sharedInstance].exchangeInfo[bottomRankPriceExchange][@"image"]];
// bottom price 가격 확보 실패시
if (_popularSpotPriceList[bottomRankPriceExchange][symbol][@"price"]) {
// 데이터가 정상적으로 있는 경우
((UILabel *)_cryptoPriceLowLabelList[i]).text = _popularSpotPriceList[bottomRankPriceExchange][symbol][@"price"];
NSString *changePricePercent24 = [_popularSpotPriceList[bottomRankPriceExchange][symbol][@"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 = @" - ";
}
/* #################### 프리미엄 비교 #################### */
// ...
6. 결과 예시
7. 전체 소스코드
// vim Controller/PopluarAssetListVC.m
#import <Foundation/Foundation.h>
#import "PopluarAssetListVC.h"
// api 통신 용도
#import "APIManager.h"
// default.json 데이터 읽기
#import "DefaultLoader.h"
// 현재 시간 가져오는 용도
#import "DateTime.h"
// unix 변환용
#import "CRTunixToString.h"
// 환율 서비스
#import "ServiceRecentRates.h"
// 거래소 api
#import "SpotBybit.h"
#import "SpotBinance.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];
[self setPriceList];
// 데이터를 표시할 레이블 생성
// **************************************** [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 miniImange = 18.0;
// ****** 글로벌 지구 이모티콘 및 시간 설정 ****** //
self.earthLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard, basicMarginInCard, miniImange, 20)];
self.earthLabel.text = @"🌍";
// 표준 시간 = 그리니치 표준시 : 더블린, 에든버러, 리스본, 런던, 카사블랑카, 몬로비아
self.liveUTCLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard + miniImange, basicMarginInCard, cardViewWidth / 2, 20)];
self.liveUTCLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
// ****** 한국 이모티콘 및 시간 설정 ****** //
// 태극기
self.koreanFlagLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard, basicMarginInCard + cardViewHeight / 2, miniImange, 20)];
self.koreanFlagLabel.text = @"🇰🇷";
// 한국 시간
self.liveKSTLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard + miniImange, basicMarginInCard + cardViewHeight / 2, cardViewWidth / 2, 20)];
self.liveKSTLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
/* #################### 환율 정보 #################### */
self.ratesLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard + cardViewWidth/3*2, basicMarginInCard, cardViewWidth/4, 20)];
self.ratesLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
self.ratesLabel.textAlignment = NSTextAlignmentRight;
self.beforeRatesLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard + cardViewWidth/3*2, basicMarginInCard + cardViewHeight / 2, cardViewWidth/4, 20)];
self.beforeRatesLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
self.beforeRatesLabel.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:_ratesLabel];
[self.topInfoTab addSubview:_beforeRatesLabel];
// ****** 상단 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, miniImange, miniImange)];
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, miniImange, miniImange)];
exchangeLogoLowImage.contentMode = UIViewContentModeScaleAspectFit; // 해당 옵션을 사용하여 가로세로 비율 유지 크기입니다.
/* #################### spot 프리미엄 노출 세팅 #################### */
// 기본 셋
UILabel *maxPricePremiumPercentLabel = [[UILabel alloc] initWithFrame:CGRectMake(basicMarginInCard, cardViewHeight/2, cardViewWidth / 8 + miniImange, 20)];
maxPricePremiumPercentLabel.textAlignment = NSTextAlignmentRight;
maxPricePremiumPercentLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
// top rank 거래소 아이콘을 위한 기본 셋
UIImageView *maxPremiumTabHighExchangeImage = [[UIImageView alloc] initWithFrame:CGRectMake(basicMarginInCard, cardViewHeight/2, miniImange, miniImange)];
maxPremiumTabHighExchangeImage.contentMode = UIViewContentModeScaleAspectFit; // 해당 옵션을 사용하여 가로세로 비율 유지 크기입니다.
// bottom rank 거래소 아이콘을 위한 기본 셋
UIImageView *maxPremiumTabLowExchangeImage = [[UIImageView alloc] initWithFrame:CGRectMake(basicMarginInCard + miniImange + cardViewWidth / 8, cardViewHeight/2, miniImange, miniImange)];
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 생성 및 메서드 호출 설정 - 매 특정시간마다 호출 - 바이낸스 2초에 1번씩
[NSTimer scheduledTimerWithTimeInterval:2
target:self
selector:@selector(updateCardDataBinance)
userInfo:nil
repeats:YES];
// NSTimer 생성 및 메서드 호출 설정 - 매 특정시간마다 호출 - 바이비트 0.5초에 1번씩
[NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:@selector(updateCardDataBybit)
userInfo:nil
repeats:YES];
// NSTimer 생성 및 메서드 호출 설정 - 매 특정시간마다 호출 - 뷰 그리기 0.5초에 1번씩
[NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:@selector(updateCardView)
userInfo:nil
repeats:YES];
}
// 데이터 가져오기 코드
- (void)updateCardDataBinance {
// **************************************** [Start] Binance 데이터 가져오기 **************************************** //
// api로 받은 데이터 깔끔한 dictionary로 가공하기
SpotBinance *mainData = [SpotBinance sharedInstance];
[mainData fetchDataWithCompletion];
self.popularSpotPriceList[@"Binance"] = mainData.dataFromSymbol;
// **************************************** [End] Binance 데이터 가져오기 **************************************** //
}
// 데이터 가져오기 코드
- (void)updateCardDataBybit {
// **************************************** [Start] Bybit 데이터 가져오기 **************************************** //
// api로 받은 데이터 깔끔한 dictionary로 가공하기
SpotBybit *mainData = [SpotBybit sharedInstance];
[mainData fetchDataWithCompletion];
self.popularSpotPriceList[@"Bybit"] = mainData.dataFromSymbol;
// **************************************** [End] Bybit 데이터 가져오기 **************************************** //
}
-(void) updateCardView {
// 메인 스레드에서만 UI 업데이트 수행
dispatch_async(dispatch_get_main_queue(), ^{
[self updateView];
});
}
// default.json 파일의 exchangeInfo 데이터 읽기
-(void) loadExchangeInfo {
// default.json의 popularList 불러오고 정상인지 1차 확인하기 = popularList에 있는 각 element들의 개수가 같은지 확인
_exchangeInfo = [DefaultLoader sharedInstance].exchangeInfo;
}
// default.json 파일의 popularList 데이터 읽기
-(void) loadPopularList {
// default.json의 popularList 불러오고 정상인지 1차 확인하기 = popularList에 있는 각 element들의 개수가 같은지 확인
popularList = [DefaultLoader sharedInstance].popularList;
int checkDefaultJsonFile = 0;
for (int i=0; i<popularList.count-1; i++) {
if ([popularList[i] count] == [popularList[i+1] count]) {
checkDefaultJsonFile += 1;
}
}
if (checkDefaultJsonFile == [popularList count]-1) {
// default.json파일의 popularList 안에 있는 Array들 중 길이가 모두 같으면
NSLog(@"%@", @"[INFO] default.json Check : Normal");
} else {
// default.json파일의 popularList 안에 있는 Array들 중 길이가 다른 것이 1개라도 있으면 WARN 출력 및 앱 dead
NSLog(@"****************************************************");
NSLog(@"[WARN] Check File : default.json - popularList");
NSLog(@"****************************************************");
}
}
// popularList에서 수집을 1건 이상이라도 할 예정인 거래소 목록을 실시간 호가 정보 데이터에 셋업하기
-(void) setPriceList {
// 초기값 세팅 (안해주면 for문 돌면서 해당 dictionary에 데이터가 들어가지 않음)
_popularSpotPriceList = [@{} mutableCopy];
for (int i=0; i<popularList.count; i++) {
for (NSString *eachPopular in popularList[i][0]) {
NSString *category = [[eachPopular componentsSeparatedByString:@"-"] objectAtIndex:0];
NSString *exchange = [[eachPopular componentsSeparatedByString:@"-"] objectAtIndex:1];
if ([category isEqual:@"spot"]) {
_popularSpotPriceList[exchange] = [@{} mutableCopy];
}
}
}
}
-(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];
[ratesInfo getData];
self.ratesLabel.text = [ratesInfo.usdkrw stringValue];
self.beforeRatesLabel.text = [ratesInfo.beforeUsdkrw stringValue];
// **************************************** [Start] 카드뷰 데이터 업데이트 **************************************** //
for (int i=0; i<popularList.count; i++) {
// 기준 key인 symbol 변수 설정
NSString *symbol = popularList[i][4];
/* #################### 카드뷰 기본 세팅 #################### */
UILabel *cryptoNameLabel = _cryptoNameLabelList[i];
cryptoNameLabel.text = symbol;
/* #################### 취급 상품 정리 및 데이터 가공 for 동일 상품에 대한 거래소간 가격 비교 기존 Array 제작 #################### */
NSMutableArray *exchangeNameList = [@[] mutableCopy];
// 특정 거래소에 있는지 확인
for (NSString *item in popularList[i][0]) {
NSString *category = [[item componentsSeparatedByString:@"-"] objectAtIndex:0];
// spot인 상품의 거래소 목록 만들기
if ([category isEqual: @"spot"]) {
NSString *exchangeName = [[item componentsSeparatedByString:@"-"] objectAtIndex:1];
[exchangeNameList addObject:exchangeName];
}
}
/* #################### 누가 High로 배치될지, Low로 배치될지 로직 #################### */
// 거래소 가격 비교해보고 탑랭크, 바텀랭크 지정
NSString *topRankPriceExchange = @"";
NSString *bottomRankPriceExchange = @"";
// 가격 정보가 1개라도 있을 경우
if ([_popularSpotPriceList[exchangeNameList[0]] objectForKey:symbol] || [_popularSpotPriceList[exchangeNameList[1]] objectForKey:symbol]) {
if ([_popularSpotPriceList[exchangeNameList[0]][symbol][@"price"] floatValue] >= [_popularSpotPriceList[exchangeNameList[1]][symbol][@"price"] floatValue]) {
// index 0번 거래소가 1번 거래소보다 더 크거나 같으면
topRankPriceExchange = exchangeNameList[0];
bottomRankPriceExchange = exchangeNameList[1];
} else {
// index 1번 거래소가 0번 거래소보다 더 크면
topRankPriceExchange = exchangeNameList[1];
bottomRankPriceExchange = exchangeNameList[0];
}
} else {
// 가격 정보가 모두 없을 경우 (로딩중이거나 못불러오거나) 전부 0으로 노출
topRankPriceExchange = exchangeNameList[0];
bottomRankPriceExchange = exchangeNameList[1];
_popularSpotPriceList[topRankPriceExchange][symbol][@"price"] = 0;
_popularSpotPriceList[bottomRankPriceExchange][symbol][@"price"] = 0;
}
// ****** top price spot 거래소명 및 이미지 설정 ****** //
((UIImageView *)_exchangeLogoHighImageList[i]).image = [UIImage imageNamed:[DefaultLoader sharedInstance].exchangeInfo[topRankPriceExchange][@"image"]];;
// top price 가격 확보 실패시
if (_popularSpotPriceList[topRankPriceExchange][symbol][@"price"]) {
// 데이터가 정상적으로 있는 경우
((UILabel *)_cryptoPriceHighLabelList[i]).text = _popularSpotPriceList[topRankPriceExchange][symbol][@"price"];
NSString *changePricePercent24 = [_popularSpotPriceList[topRankPriceExchange][symbol][@"changePricePercent24"] stringByAppendingString:@"%"];
((UILabel *)_changePricePercent24HighLabelList[i]).text = changePricePercent24;
if ([changePricePercent24 hasPrefix:@"+"]) {
((UILabel *)_changePricePercent24HighLabelList[i]).textColor = [UIColor redColor];
} else {
((UILabel *)_changePricePercent24HighLabelList[i]).textColor = [UIColor blueColor];
}
} else {
// 데이터가 없는 경우 : 비정상 상태는 아니고, 0개의 거래소에 상장되어있는 경우 데이터가 아예 없어서 이런 현상 발생
((UILabel *)_cryptoPriceHighLabelList[i]).text = @" - ";
((UILabel *)_changePricePercent24HighLabelList[i]).text = @" - ";
}
// ****** bottom price spot 거래소 이미지 설정 ****** //
((UIImageView *)_exchangeLogoLowImageList[i]).image = [UIImage imageNamed:[DefaultLoader sharedInstance].exchangeInfo[bottomRankPriceExchange][@"image"]];
// bottom price 가격 확보 실패시
if (_popularSpotPriceList[bottomRankPriceExchange][symbol][@"price"]) {
// 데이터가 정상적으로 있는 경우
((UILabel *)_cryptoPriceLowLabelList[i]).text = _popularSpotPriceList[bottomRankPriceExchange][symbol][@"price"];
NSString *changePricePercent24 = [_popularSpotPriceList[bottomRankPriceExchange][symbol][@"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 = [_popularSpotPriceList[topRankPriceExchange][symbol][@"price"] floatValue];
float bottomPrice = [_popularSpotPriceList[bottomRankPriceExchange][symbol][@"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][@"image"]];
((UIImageView *)_maxPremiumTabLowExchangeImageList[i]).image = [UIImage imageNamed:[DefaultLoader sharedInstance].exchangeInfo[bottomRankPriceExchange][@"image"]];
}
// **************************************** [End] 카드뷰 목록 쭉 만들기 **************************************** //
}
@end