1. 이전 포스팅 확인하기

 

https://growingsaja.tistory.com/948

 

[Objective-C] 앱 만들기 입문 - 40 : 성능 개선 (메모리 문제 해결) - Model 구조 추가를 위한 프로젝트

1. 이전 포스팅 확인하기 https://growingsaja.tistory.com/945 [Objective-C] 앱 만들기 입문 - 39 : 선물 futures 괴리율 개요 서비스 개발 3 - 정보 가공해 노출 1. 이전 포스팅 확인하기 https://growingsaja.tistory.com/944

growingsaja.tistory.com

 

 

 

 

 

2. 기획

 

   a. 1번째 메뉴탭 : USDT 기준 Crypto Currency 개요 정보

   b. 2번째 메뉴탭 : USD(s) 기준 Crypto Currency 개요 정보 (USDC, FDUSD, DAI, TUSD)

   c. 3번째 메뉴탭 : BUSD 기준 Crypto Currency 개요 정보

   d. 4번째 메뉴탭 : Fiat currency 기준 Crypto Currency 개요 정보

   e. 5번째 메뉴탭 : 환율 정보

 

 

 

 

 

3. default.json 파일 내용 수정

 

각 탭에서 기준이 되는 popularList 데이터들을 작성해줍니다.

 

{
    "popularList_description": [
        ["isAvailable", "Fullname", "codeName", "paymentCurrency", "searchKeyword"],
        ["활성화 여부", "전체이름", "코드 또는 축약이름", "지급 화폐 단위", "검색 키워드"]
    ],
    "popularList": [
        [true, "Bitcoin", "BTC", "USDT", "BTCUSDT"],
        [true, "Ethereum", "ETH", "USDT", "ETHUSDT"],
        [true, "Ethereum Classic", "ETC", "USDT", "ETCUSDT"],
        [true, "Ripple", "XRP", "USDT", "XRPUSDT"],
        [true, "TRON", "TRX", "USDT", "TRXUSDT"],
        [true, "Binance Coin", "BNB", "USDT", "BNBUSDT"],
        [true, "AXSUSDT", "AXS", "USDT", "AXSUSDT"],
        [true, "Waves", "WAVES", "USDT", "WAVESUSDT"],
        [true, "Stellar", "XLM", "USDT", "XLMUSDT"],
        [true, "Dogecoin", "DOGE", "USDT", "DOGEUSDT"],
        [true, "Polkadot", "DOT", "USDT", "DOTUSDT"],
        [true, "Binance USD", "BUSD", "USDT", "BUSDUSDT"],
        [true, "USDCUSDT", "USDC", "USDT", "USDCUSDT"],
        [true, "TrueUSD", "TUSD", "USDT", "TUSDUSDT"],
        [true, "DAIUSDT", "DAI", "USDT", "DAIUSDT"],
        [true, "ICON", "ICX", "USDT", "ICXUSDT"],
        [true, "Dash", "DASH", "USDT", "DASHUSDT"],
        [true, "REN", "REN", "USDT", "RENUSDT"],
        [true, "REPUSDT", "REP", "USDT", "REPUSDT"],
        [true, "SCUSDT", "SC", "USDT", "SCUSDT"],
        [true, "RUNEUSDT", "RUNE", "USDT", "RUNEUSDT"],
        [true, "PSGUSDT", "PSG", "USDT", "PSGUSDT"],
        [true, "ACMUSDT", "ACM", "USDT", "ACMUSDT"],
        [true, "MOVRUSDT", "MOVR", "USDT", "MOVRUSDT"],
        [true, "CITYUSDT", "CITY", "USDT", "CITYUSDT"],
        [true, "SRMUSDT", "SRM", "USDT", "SRMUSDT"],
        [true, "JUVUSDT", "JUV", "USDT", "JUVUSDT"],
        [true, "XNOUSDT", "XNO", "USDT", "XNOUSDT"],
        [true, "NEXOUSDT", "NEXO", "USDT", "NEXOUSDT"],
        [true, "OMUSDT", "OM", "USDT", "OMUSDT"],
        [true, "TUSDT", "T", "USDT", "TUSDT"],
        [true, "PENDLEUSDT", "PENDLE", "USDT", "PENDLEUSDT"],
        [true, "SEIUSDT", "SEI", "USDT", "SEIUSDT"],
        [true, "RPLUSDT", "RPL", "USDT", "RPLUSDT"],
        [true, "CYBERUSDT", "CYBER", "USDT", "CYBERUSDT"],
        [true, "Zcash", "ZEC", "USDT", "ZECUSDT"],
        [true, "Uniswap", "UNI", "USDT", "UNIUSDT"],
        [true, "VeChain", "VET", "USDT", "VETUSDT"],
        [true, "EOS", "EOS", "USDT", "EOSUSDT"],
        [true, "Bitcoin Cash", "BCH", "USDT", "BCHUSDT"],
        [true, "Litecoin", "LTC", "USDT", "LTCUSDT"],
        [true, "Cardano", "ADA", "USDT", "ADAUSDT"],
        [true, "Chainlink", "LINK", "USDT", "LINKUSDT"],
        [true, "Monero", "XMR", "USDT", "XMRUSDT"],
        [true, "NEOUSDT", "NEO", "USDT", "NEOUSDT"],
        [true, "Qtum", "QTUM", "USDT", "QTUMUSDT"],
        [true, "IOTA", "IOTA", "USDT", "IOTAUSDT"],
        [true, "Ontology", "ONT", "USDT", "ONTUSDT"],
        [true, "Ontology Gas", "ONG", "USDT", "ONGUSDT"],
        [true, "Holo", "HOT", "USDT", "HOTUSDT"],
        [true, "Zilliqa", "ZIL", "USDT", "ZILUSDT"],
        [true, "ZRXUSDT", "ZRX", "USDT", "ZRXUSDT"],
        [true, "Fetch.ai", "FET", "USDT", "FETUSDT"],
        [true, "Basic Attention Token", "BAT", "USDT", "BATUSDT"],
        [true, "IOST", "IOST", "USDT", "IOSTUSDT"],
        [true, "Celer Network", "CELR", "USDT", "CELRUSDT"],
        [true, "OmiseGO", "OMG", "USDT", "OMGUSDT"],
        [true, "Theta", "THETA", "USDT", "THETAUSDT"],
        [true, "Enjin", "ENJ", "USDT", "ENJUSDT"],
        [true, "Mithril", "MITH", "USDT", "MITHUSDT"],
        [true, "Matic Network", "MATIC", "USDT", "MATICUSDT"],
        [true, "Cosmos", "ATOM", "USDT", "ATOMUSDT"],
        [true, "Theta Fuel", "TFUEL", "USDT", "TFUELUSDT"],
        [true, "Harmony", "ONE", "USDT", "ONEUSDT"],
        [true, "Fantom", "FTM", "USDT", "FTMUSDT"],
        [true, "Algorand", "ALGO", "USDT", "ALGOUSDT"],
        [true, "USD Coin", "USDC", "USDT", "USDSBUSDT"],
        [true, "Dusk Network", "DUSK", "USDT", "DUSKUSDT"],
        [true, "Ankr", "ANKR", "USDT", "ANKRUSDT"],
        [true, "WINk", "WIN", "USDT", "WINUSDT"],
        [true, "Cosmos", "COS", "USDT", "COSUSDT"],
        [true, "Metal", "MTL", "USDT", "MTLUSDT"],
        [true, "TomoChain", "TOMO", "USDT", "TOMOUSDT"],
        [true, "Perlin", "PERL", "USDT", "PERLUSDT"],
        [true, "Dent", "DENT", "USDT", "DENTUSDT"],
        [true, "SelfKey", "KEY", "USDT", "KEYUSDT"],
        [true, "StormX", "STMX", "USDT", "STORMUSDT"],
        [true, "Dock", "DOCK", "USDT", "DOCKUSDT"],
        [true, "Wanchain", "WAN", "USDT", "WANUSDT"],
        [true, "FunFair", "FUN", "USDT", "FUNUSDT"],
        [true, "Civic", "CVC", "USDT", "CVCUSDT"],
        [true, "Chiliz", "CHZ", "USDT", "CHZUSDT"],
        [true, "Band Protocol", "BAND", "USDT", "BANDUSDT"],
        [true, "Tezos", "XTZ", "USDT", "XTZUSDT"],
        [true, "Ravencoin", "RVN", "USDT", "RVNUSDT"],
        [true, "HyperCash", "HC", "USDT", "HCUSDT"],
        [true, "Hedera Hashgraph", "HBAR", "USDT", "HBARUSDT"],
        [true, "NKN", "NKN", "USDT", "NKNUSDT"],
        [true, "Stacks", "STX", "USDT", "STXUSDT"],
        [true, "Kava", "KAVA", "USDT", "KAVAUSDT"],
        [true, "Araplanet", "ARPA", "USDT", "ARPAUSDT"],
        [true, "IoTeX", "IOTX", "USDT", "IOTXUSDT"],
        [true, "Cortex", "CTXC", "USDT", "CTXCUSDT"],
        [true, "Bitcoin Cash", "BCH", "USDT", "BCHUSDT"],
        [true, "Troy", "TROY", "USDT", "TROYUSDT"],
        [true, "VITE", "VITE", "USDT", "VITEUSDT"],
        [true, "FTX Token", "FTT", "USDT", "FTTUSDT"],
        [true, "Origin Protocol", "OGN", "USDT", "OGNUSDT"],
        [true, "DREPUSDT", "DREP", "USDT", "DREPUSDT"],
        [true, "WRXUSDT", "WRX", "USDT", "WRXUSDT"],
        [true, "BTSUSDT", "BTS", "USDT", "BTSUSDT"],
        [true, "LSKUSDT", "LSK", "USDT", "LSKUSDT"],
        [true, "BNTUSDT", "BNT", "USDT", "BNTUSDT"],
        [true, "LTOUSDT", "LTO", "USDT", "LTOUSDT"],
        [true, "MBLUSDT", "MBL", "USDT", "MBLUSDT"],
        [true, "COTIUSDT", "COTI", "USDT", "COTIUSDT"],
        [true, "STPTUSDT", "STPT", "USDT", "STPTUSDT"],
        [true, "WTCUSDT", "WTC", "USDT", "WTCUSDT"],
        [true, "DATAUSDT", "DATA", "USDT", "DATAUSDT"],
        [true, "SOLUSDT", "SOL", "USDT", "SOLUSDT"],
        [true, "CTSIUSDT", "CTSI", "USDT", "CTSIUSDT"],
        [true, "HIVEUSDT", "HIVE", "USDT", "HIVEUSDT"],
        [true, "CHRUSDT", "CHR", "USDT", "CHRUSDT"],
        [true, "ARDRUSDT", "ARDR", "USDT", "ARDRUSDT"],
        [true, "MDTUSDT", "MDT", "USDT", "MDTUSDT"],
        [true, "STMXUSDT", "STMX", "USDT", "STMXUSDT"],
        [true, "KNCUSDT", "KNC", "USDT", "KNCUSDT"],
        [true, "LRCUSDT", "LRC", "USDT", "LRCUSDT"],
        [true, "PNTUSDT", "PNT", "USDT", "PNTUSDT"],
        [true, "COMPUSDT", "COMP", "USDT", "COMPUSDT"],
        [true, "ZENUSDT", "ZEN", "USDT", "ZENUSDT"],
        [true, "SNXUSDT", "SNX", "USDT", "SNXUSDT"],
        [true, "VTHOUSDT", "VTHO", "USDT", "VTHOUSDT"],
        [true, "DGBUSDT", "DGB", "USDT", "DGBUSDT"],
        [true, "GBPUSDT", "GBP", "USDT", "GBPUSDT"],
        [true, "SXPUSDT", "SXP", "USDT", "SXPUSDT"],
        [true, "MKRUSDT", "MKR", "USDT", "MKRUSDT"],
        [true, "DCRUSDT", "DCR", "USDT", "DCRUSDT"],
        [true, "STORJUSDT", "STORJ", "USDT", "STORJUSDT"],
        [true, "MANAUSDT", "MANA", "USDT", "MANAUSDT"],
        [true, "YFIUSDT", "YFI", "USDT", "YFIUSDT"],
        [true, "BALUSDT", "BAL", "USDT", "BALUSDT"],
        [true, "BLZUSDT", "BLZ", "USDT", "BLZUSDT"],
        [true, "IRISUSDT", "IRIS", "USDT", "IRISUSDT"],
        [true, "KMDUSDT", "KMD", "USDT", "KMDUSDT"],
        [true, "JSTUSDT", "JST", "USDT", "JSTUSDT"],
        [true, "ANTUSDT", "ANT", "USDT", "ANTUSDT"],
        [true, "CRVUSDT", "CRV", "USDT", "CRVUSDT"],
        [true, "SANDUSDT", "SAND", "USDT", "SANDUSDT"],
        [true, "OCEANUSDT", "OCEAN", "USDT", "OCEANUSDT"],
        [true, "NMRUSDT", "NMR", "USDT", "NMRUSDT"],
        [true, "LUNAUSDT", "LUNA", "USDT", "LUNAUSDT"],
        [true, "RSRUSDT", "RSR", "USDT", "RSRUSDT"],
        [true, "PAXGUSDT", "PAXG", "USDT", "PAXGUSDT"],
        [true, "WNXMUSDT", "WNXM", "USDT", "WNXMUSDT"],
        [true, "TRBUSDT", "TRB", "USDT", "TRBUSDT"],
        [true, "SUSHIUSDT", "SUSHI", "USDT", "SUSHIUSDT"],
        [true, "YFIIUSDT", "YFII", "USDT", "YFIIUSDT"],
        [true, "KSMUSDT", "KSM", "USDT", "KSMUSDT"],
        [true, "EGLDUSDT", "EGLD", "USDT", "EGLDUSDT"],
        [true, "DIAUSDT", "DIA", "USDT", "DIAUSDT"],
        [true, "FIOUSDT", "FIO", "USDT", "FIOUSDT"],
        [true, "UMAUSDT", "UMA", "USDT", "UMAUSDT"],
        [true, "BELUSDT", "BEL", "USDT", "BELUSDT"],
        [true, "WINGUSDT", "WING", "USDT", "WINGUSDT"],
        [true, "OXTUSDT", "OXT", "USDT", "OXTUSDT"],
        [true, "SUNUSDT", "SUN", "USDT", "SUNUSDT"],
        [true, "AVAXUSDT", "AVAX", "USDT", "AVAXUSDT"],
        [true, "HNTUSDT", "HNT", "USDT", "HNTUSDT"],
        [true, "FLMUSDT", "FLM", "USDT", "FLMUSDT"],
        [true, "ORNUSDT", "ORN", "USDT", "ORNUSDT"],
        [true, "UTKUSDT", "UTK", "USDT", "UTKUSDT"],
        [true, "XVSUSDT", "XVS", "USDT", "XVSUSDT"],
        [true, "ALPHAUSDT", "ALPHA", "USDT", "ALPHAUSDT"],
        [true, "AAVEUSDT", "AAVE", "USDT", "AAVEUSDT"],
        [true, "NEARUSDT", "NEAR", "USDT", "NEARUSDT"],
        [true, "FILUSDT", "FIL", "USDT", "FILUSDT"],
        [true, "INJUSDT", "INJ", "USDT", "INJUSDT"],
        [true, "AUDIOUSDT", "AUDIO", "USDT", "AUDIOUSDT"],
        [true, "CTKUSDT", "CTK", "USDT", "CTKUSDT"],
        [true, "AKROUSDT", "AKRO", "USDT", "AKROUSDT"],
        [true, "HARDUSDT", "HARD", "USDT", "HARDUSDT"],
        [true, "STRAXUSDT", "STRAX", "USDT", "STRAXUSDT"],
        [true, "UNFIUSDT", "UNFI", "USDT", "UNFIUSDT"],
        [true, "ROSEUSDT", "ROSE", "USDT", "ROSEUSDT"],
        [true, "AVAUSDT", "AVA", "USDT", "AVAUSDT"],
        [true, "XEMUSDT", "XEM", "USDT", "XEMUSDT"],
        [true, "SKLUSDT", "SKL", "USDT", "SKLUSDT"],
        [true, "GRTUSDT", "GRT", "USDT", "GRTUSDT"],
        [true, "1INCHUSDT", "1INCH", "USDT", "1INCHUSDT"],
        [true, "REEFUSDT", "REEF", "USDT", "REEFUSDT"],
        [true, "OGUSDT", "OG", "USDT", "OGUSDT"],
        [true, "ATMUSDT", "ATM", "USDT", "ATMUSDT"],
        [true, "ASRUSDT", "ASR", "USDT", "ASRUSDT"],
        [true, "CELOUSDT", "CELO", "USDT", "CELOUSDT"],
        [true, "RIFUSDT", "RIF", "USDT", "RIFUSDT"],
        [true, "BTCSTUSDT", "BTCST", "USDT", "BTCSTUSDT"],
        [true, "TRUUSDT", "TRU", "USDT", "TRUUSDT"],
        [true, "CKBUSDT", "CKB", "USDT", "CKBUSDT"],
        [true, "TWTUSDT", "TWT", "USDT", "TWTUSDT"],
        [true, "FIROUSDT", "FIRO", "USDT", "FIROUSDT"],
        [true, "LITUSDT", "LIT", "USDT", "LITUSDT"],
        [true, "SFPUSDT", "SFP", "USDT", "SFPUSDT"],
        [true, "DODOUSDT", "DODO", "USDT", "DODOUSDT"],
        [true, "CAKEUSDT", "CAKE", "USDT", "CAKEUSDT"],
        [true, "PHAUSDT", "PHA", "USDT", "PHAUSDT"],
        [true, "BADGERUSDT", "BADGER", "USDT", "BADGERUSDT"],
        [true, "FISUSDT", "FIS", "USDT", "FISUSDT"],
        [true, "PONDUSDT", "POND", "USDT", "PONDUSDT"],
        [true, "DEGOUSDT", "DEGO", "USDT", "DEGOUSDT"],
        [true, "ALICEUSDT", "ALICE", "USDT", "ALICEUSDT"],
        [true, "LINAUSDT", "LINA", "USDT", "LINAUSDT"],
        [true, "PERPUSDT", "PERP", "USDT", "PERPUSDT"],
        [true, "SUPERUSDT", "SUPER", "USDT", "SUPERUSDT"],
        [true, "CFXUSDT", "CFX", "USDT", "CFXUSDT"],
        [true, "TKOUSDT", "TKO", "USDT", "TKOUSDT"],
        [true, "PUNDIXUSDT", "PUNDIX", "USDT", "PUNDIXUSDT"],
        [true, "TLMUSDT", "TLM", "USDT", "TLMUSDT"],
        [true, "BTGUSDT", "BTG", "USDT", "BTGUSDT"],
        [true, "BAKEUSDT", "BAKE", "USDT", "BAKEUSDT"],
        [true, "BURGERUSDT", "BURGER", "USDT", "BURGERUSDT"],
        [true, "SLPUSDT", "SLP", "USDT", "SLPUSDT"],
        [true, "SHIBUSDT", "SHIB", "USDT", "SHIBUSDT"],
        [true, "ICPUSDT", "ICP", "USDT", "ICPUSDT"],
        [true, "ARUSDT", "AR", "USDT", "ARUSDT"],
        [true, "POLSUSDT", "POLS", "USDT", "POLSUSDT"],
        [true, "MDXUSDT", "MDX", "USDT", "MDXUSDT"],
        [true, "MASKUSDT", "MASK", "USDT", "MASKUSDT"],
        [true, "LPTUSDT", "LPT", "USDT", "LPTUSDT"],
        [true, "XVGUSDT", "XVG", "USDT", "XVGUSDT"],
        [true, "ATAUSDT", "ATA", "USDT", "ATAUSDT"],
        [true, "GTCUSDT", "GTC", "USDT", "GTCUSDT"],
        [true, "ERNUSDT", "ERN", "USDT", "ERNUSDT"],
        [true, "KLAYUSDT", "KLAY", "USDT", "KLAYUSDT"],
        [true, "PHAUSDT", "PHA", "USDT", "PHAUSDT"],
        [true, "BONDUSDT", "BOND", "USDT", "BONDUSDT"],
        [true, "MLNUSDT", "MLN", "USDT", "MLNUSDT"],
        [true, "DEXEUSDT", "DEXE", "USDT", "DEXEUSDT"],
        [true, "C98USDT", "C98", "USDT", "C98USDT"],
        [true, "CLVUSDT", "CLV", "USDT", "CLVUSDT"],
        [true, "QNTUSDT", "QNT", "USDT", "QNTUSDT"],
        [true, "FLOWUSDT", "FLOW", "USDT", "FLOWUSDT"],
        [true, "TVKUSDT", "TVK", "USDT", "TVKUSDT"],
        [true, "MINAUSDT", "MINA", "USDT", "MINAUSDT"],
        [true, "RAYUSDT", "RAY", "USDT", "RAYUSDT"],
        [true, "FARMUSDT", "FARM", "USDT", "FARMUSDT"],
        [true, "ALPACAUSDT", "ALPACA", "USDT", "ALPACAUSDT"],
        [true, "QUICKUSDT", "QUICK", "USDT", "QUICKUSDT"],
        [true, "MBOXUSDT", "MBOX", "USDT", "MBOXUSDT"],
        [true, "FORUSDT", "FOR", "USDT", "FORUSDT"],
        [true, "REQUSDT", "REQ", "USDT", "REQUSDT"],
        [true, "GHSTUSDT", "GHST", "USDT", "GHSTUSDT"],
        [true, "WAXPUSDT", "WAXP", "USDT", "WAXPUSDT"],
        [true, "TRIBEUSDT", "TRIBE", "USDT", "TRIBEUSDT"],
        [true, "GNOUSDT", "GNO", "USDT", "GNOUSDT"],
        [true, "XECUSDT", "XEC", "USDT", "XECUSDT"],
        [true, "ELFUSDT", "ELF", "USDT", "ELFUSDT"],
        [true, "DYDXUSDT", "DYDX", "USDT", "DYDXUSDT"],
        [true, "IDEXUSDT", "IDEX", "USDT", "IDEXUSDT"],
        [true, "VIDTUSDT", "VIDT", "USDT", "VIDTUSDT"],
        [true, "USDPUSDT", "USDP", "USDT", "USDPUSDT"],
        [true, "GALAUSDT", "GALA", "USDT", "GALAUSDT"],
        [true, "ILVUSDT", "ILV", "USDT", "ILVUSDT"],
        [true, "YGGUSDT", "YGG", "USDT", "YGGUSDT"],
        [true, "SYSUSDT", "SYS", "USDT", "SYSUSDT"],
        [true, "DFUSDT", "DF", "USDT", "DFUSDT"],
        [true, "FIDAUSDT", "FIDA", "USDT", "FIDAUSDT"],
        [true, "FRONTUSDT", "FRONT", "USDT", "FRONTUSDT"],
        [true, "CVPUSDT", "CVP", "USDT", "CVPUSDT"],
        [true, "AGLDUSDT", "AGLD", "USDT", "AGLDUSDT"],
        [true, "RADUSDT", "RAD", "USDT", "RADUSDT"],
        [true, "BETAUSDT", "BETA", "USDT", "BETAUSDT"],
        [true, "RAREUSDT", "RARE", "USDT", "RAREUSDT"],
        [true, "LAZIOUSDT", "LAZIO", "USDT", "LAZIOUSDT"],
        [true, "CHESSUSDT", "CHESS", "USDT", "CHESSUSDT"],
        [true, "ADXUSDT", "ADX", "USDT", "ADXUSDT"],
        [true, "AUCTIONUSDT", "AUCTION", "USDT", "AUCTIONUSDT"],
        [true, "DARUSDT", "DAR", "USDT", "DARUSDT"],
        [true, "BNXUSDT", "BNX", "USDT", "BNXUSDT"],
        [true, "ENSUSDT", "ENS", "USDT", "ENSUSDT"],
        [true, "KP3RUSDT", "KP3R", "USDT", "KP3RUSDT"],
        [true, "QIUSDT", "QI", "USDT", "QIUSDT"],
        [true, "PORTOUSDT", "PORTO", "USDT", "PORTOUSDT"],
        [true, "POWRUSDT", "POWR", "USDT", "POWRUSDT"],
        [true, "VGXUSDT", "VGX", "USDT", "VGXUSDT"],
        [true, "JASMYUSDT", "JASMY", "USDT", "JASMYUSDT"],
        [true, "AMPUSDT", "AMP", "USDT", "AMPUSDT"],
        [true, "PLAUSDT", "PLA", "USDT", "PLAUSDT"],
        [true, "PYRUSDT", "PYR", "USDT", "PYRUSDT"],
        [true, "RNDRUSDT", "RNDR", "USDT", "RNDRUSDT"],
        [true, "ALCXUSDT", "ALCX", "USDT", "ALCXUSDT"],
        [true, "SANTOSUSDT", "SANTOS", "USDT", "SANTOSUSDT"],
        [true, "MCUSDT", "MC", "USDT", "MCUSDT"],
        [true, "BICOUSDT", "BICO", "USDT", "BICOUSDT"],
        [true, "FLUXUSDT", "FLUX", "USDT", "FLUXUSDT"],
        [true, "FXSUSDT", "FXS", "USDT", "FXSUSDT"],
        [true, "VOXELUSDT", "VOXEL", "USDT", "VOXELUSDT"],
        [true, "HIGHUSDT", "HIGH", "USDT", "HIGHUSDT"],
        [true, "CVXUSDT", "CVX", "USDT", "CVXUSDT"],
        [true, "PEOPLEUSDT", "PEOPLE", "USDT", "PEOPLEUSDT"],
        [true, "OOKIUSDT", "OOKI", "USDT", "OOKIUSDT"],
        [true, "SPELLUSDT", "SPELL", "USDT", "SPELLUSDT"],
        [true, "JOEUSDT", "JOE", "USDT", "JOEUSDT"],
        [true, "ACHUSDT", "ACH", "USDT", "ACHUSDT"],
        [true, "IMXUSDT", "IMX", "USDT", "IMXUSDT"],
        [true, "GLMRUSDT", "GLMR", "USDT", "GLMRUSDT"],
        [true, "LOKAUSDT", "LOKA", "USDT", "LOKAUSDT"],
        [true, "Secret", "SCRT", "USDT", "SCRTUSDT"],
        [true, "API3", "API3", "USDT", "API3USDT"],
        [true, "BTTCUSDT", "BTTC", "USDT", "BTTCUSDT"],
        [true, "ACAUSDT", "ACA", "USDT", "ACAUSDT"],
        [true, "WOOUSDT", "WOO", "USDT", "WOOUSDT"],
        [true, "ALPINEUSDT", "ALPINE", "USDT", "ALPINEUSDT"],
        [true, "ASTRUSDT", "ASTR", "USDT", "ASTRUSDT"],
        [true, "NBTUSDT", "NBT", "USDT", "NBTUSDT"],
        [true, "GMTUSDT", "GMT", "USDT", "GMTUSDT"],
        [true, "KDAUSDT", "KDA", "USDT", "KDAUSDT"],
        [true, "APEUSDT", "APE", "USDT", "APEUSDT"],
        [true, "BSWUSDT", "BSW", "USDT", "BSWUSDT"],
        [true, "BIFIUSDT", "BIFI", "USDT", "BIFIUSDT"],
        [true, "MULTIUSDT", "MULTI", "USDT", "MULTIUSDT"],
        [true, "STEEMUSDT", "STEEM", "USDT", "STEEMUSDT"],
        [true, "MOBUSDT", "MOB", "USDT", "MOBUSDT"],
        [true, "REIUSDT", "REI", "USDT", "REIUSDT"],
        [true, "GALUSDT", "GAL", "USDT", "GALUSDT"],
        [true, "LDOUSDT", "LDO", "USDT", "LDOUSDT"],
        [true, "EPXUSDT", "EPX", "USDT", "EPXUSDT"],
        [true, "OPUSDT", "OP", "USDT", "OPUSDT"],
        [true, "LEVERUSDT", "LEVER", "USDT", "LEVERUSDT"],
        [true, "STGUSDT", "STG", "USDT", "STGUSDT"],
        [true, "LUNCUSDT", "LUNC", "USDT", "LUNCUSDT"],
        [true, "GMXUSDT", "GMX", "USDT", "GMXUSDT"],
        [true, "NEBLUSDT", "NEBL", "USDT", "NEBLUSDT"],
        [true, "POLYXUSDT", "POLYX", "USDT", "POLYXUSDT"],
        [true, "APTUSDT", "APT", "USDT", "APTUSDT"],
        [true, "OSMOUSDT", "OSMO", "USDT", "OSMOUSDT"],
        [true, "HFTUSDT", "HFT", "USDT", "HFTUSDT"],
        [true, "PHBUSDT", "PHB", "USDT", "PHBUSDT"],
        [true, "HOOKUSDT", "HOOK", "USDT", "HOOKUSDT"],
        [true, "MAGICUSDT", "MAGIC", "USDT", "MAGICUSDT"],
        [true, "HIFIUSDT", "HIFI", "USDT", "HIFIUSDT"],
        [true, "PROSUSDT", "PROS", "USDT", "PROSUSDT"],
        [true, "AGIXUSDT", "AGIX", "USDT", "AGIXUSDT"],
        [true, "GNSUSDT", "GNS", "USDT", "GNSUSDT"],
        [true, "SYNUSDT", "SYN", "USDT", "SYNUSDT"],
        [true, "VIBUSDT", "VIB", "USDT", "VIBUSDT"],
        [true, "SSVUSDT", "SSV", "USDT", "SSVUSDT"],
        [true, "LQTYUSDT", "LQTY", "USDT", "LQTYUSDT"],
        [true, "AMBUSDT", "AMB", "USDT", "AMBUSDT"],
        [true, "BETHUSDT", "BETH", "USDT", "BETHUSDT"],
        [true, "USTCUSDT", "USTC", "USDT", "USTCUSDT"],
        [true, "GASUSDT", "GAS", "USDT", "GASUSDT"],
        [true, "GLMUSDT", "GLM", "USDT", "GLMUSDT"],
        [true, "PROMUSDT", "PROM", "USDT", "PROMUSDT"],
        [true, "QKCUSDT", "QKC", "USDT", "QKCUSDT"],
        [true, "UFTUSDT", "UFT", "USDT", "UFTUSDT"],
        [true, "IDUSDT", "ID", "USDT", "IDUSDT"],
        [true, "ARBUSDT", "ARB", "USDT", "ARBUSDT"],
        [true, "LOOMUSDT", "LOOM", "USDT", "LOOMUSDT"],
        [true, "OAXUSDT", "OAX", "USDT", "OAXUSDT"],
        [true, "RDNTUSDT", "RDNT", "USDT", "RDNTUSDT"],
        [true, "WBTCUSDT", "WBTC", "USDT", "WBTCUSDT"],
        [true, "EDUUSDT", "EDU", "USDT", "EDUUSDT"],
        [true, "SUIUSDT", "SUI", "USDT", "SUIUSDT"],
        [true, "AERGOUSDT", "AERGO", "USDT", "AERGOUSDT"],
        [true, "PEPEUSDT", "PEPE", "USDT", "PEPEUSDT"],
        [true, "FLOKIUSDT", "FLOKI", "USDT", "FLOKIUSDT"],
        [true, "ASTUSDT", "AST", "USDT", "ASTUSDT"],
        [true, "SNTUSDT", "SNT", "USDT", "SNTUSDT"],
        [true, "COMBOUSDT", "COMBO", "USDT", "COMBOUSDT"],
        [true, "MAVUSDT", "MAV", "USDT", "MAVUSDT"],
        [true, "ARKMUSDT", "ARKM", "USDT", "ARKMUSDT"],
        [true, "WBETHUSDT", "WBETH", "USDT", "WBETHUSDT"],
        [true, "WLDUSDT", "WLD", "USDT", "WLDUSDT"],
        [true, "FDUSDUSDT", "FDUSD", "USDT", "FDUSDUSDT"]
    ],
    "popularList_USDs": [
        [true, "Bitcoin", "BTC", "USDC", "BTCUSDC"],
        [true, "BTCDAI", "BTC", "DAI", "BTCDAI"],
        [true, "BTCFDUSD", "BTC", "FDUSD", "BTCFDUSD"],
        [true, "Ethereum", "ETH", "USDC", "ETHUSDC"],
        [true, "ETHDAI", "ETH", "DAI", "ETHDAI"],
        [true, "ETHFDUSD", "ETH", "FDUSD", "ETHFDUSD"],
        [true, "Binance Coin", "BNB", "USDC", "BNBUSDC"],
        [true, "BNBDAI", "BNB", "DAI", "BNBDAI"],
        [true, "BNBFDUSD", "BNB", "FDUSD", "BNBFDUSD"],
        [true, "SEIFDUSD", "SEI", "FDUSD", "SEIFDUSD"],
        [true, "CYBERFDUSD", "CYBER", "FDUSD", "CYBERFDUSD"],
        [true, "USDTDAI", "USDT", "DAI", "USDTDAI"],
        [true, "BUSDDAI", "BUSD", "DAI", "BUSDDAI"],
        [true, "Ripple", "XRP", "USDC", "XRPUSDC"],
        [true, "EOS", "EOS", "USDC", "EOSUSDC"],
        [true, "Stellar", "XLM", "USDC", "XLMUSDC"],
        [true, "Chainlink", "LINK", "USDC", "LINKUSDC"],
        [true, "Waves", "WAVES", "USDC", "WAVESUSDC"],
        [true, "Litecoin", "LTC", "USDC", "LTCUSDC"],
        [true, "TRON", "TRX", "USDC", "TRXUSDC"],
        [true, "MATICTUSD", "MATIC", "TUSD", "MATICTUSD"],
        [true, "OPTUSD", "OP", "TUSD", "OPTUSD"],
        [true, "SOLTUSD", "SOL", "TUSD", "SOLTUSD"],
        [true, "Zcash", "ZEC", "USDC", "ZECUSDC"],
        [true, "Cardano", "ADA", "USDC", "ADAUSDC"],
        [true, "Cosmos", "ATOM", "USDC", "ATOMUSDC"],
        [true, "Ethereum Classic", "ETC", "USDC", "ETCUSDC"],
        [true, "Basic Attention Token", "BAT", "USDC", "BATUSDC"],
        [true, "Fantom", "FTM", "USDC", "FTMUSDC"],
        [true, "Algorand", "ALGO", "USDC", "ALGOUSDC"],
        [true, "Dogecoin", "DOGE", "USDC", "DOGEUSDC"],
        [true, "Bitcoin Cash", "BCH", "USDC", "BCHUSDC"],
        [true, "SOLUSDC", "SOL", "USDC", "SOLUSDC"],
        [true, "Binance Coin", "BNB", "TUSD", "BNBTUSD"],
        [true, "Ripple", "XRP", "TUSD", "XRPTUSD"],
        [true, "Cardano", "ADA", "TUSD", "ADATUSD"],
        [true, "Litecoin", "LTC", "TUSD", "LTCTUSD"],
        [true, "Bitcoin Cash", "BCH", "TUSD", "BCHTUSD"],
        [true, "RDNTTUSD", "RDNT", "TUSD", "RDNTTUSD"],
        [true, "DOGETUSD", "DOGE", "TRY", "DOGETUSD"],
        [true, "EDUTUSD", "EDU", "TUSD", "EDUTUSD"],
        [true, "SUITUSD", "SUI", "TUSD", "SUITUSD"],
        [true, "PEPETUSD", "PEPE", "TUSD", "PEPETUSD"],
        [true, "FLOKITUSD", "FLOKI", "TUSD", "FLOKITUSD"],
        [true, "MAVTUSD", "MAV", "TUSD", "MAVTUSD"],
        [true, "CFXTUSD", "CFX", "TUSD", "CFXTUSD"],
        [true, "PENDLETUSD", "PENDLE", "TUSD", "PENDLETUSD"],
        [true, "XVGTUSD", "XVG", "TUSD", "XVGTUSD"],
        [true, "ARKMTUSD", "ARKM", "TUSD", "ARKMTUSD"],
        [true, "AVAXTUSD", "AVAX", "TUSD", "AVAXTUSD"],
        [true, "COMPTUSD", "COMP", "TUSD", "COMPTUSD"],
        [true, "QUICKTUSD", "QUICK", "TUSD", "QUICKTUSD"],
        [true, "ARBTUSD", "ARB", "TUSD", "ARBTUSD"],
        [true, "IDTUSD", "ID", "TUSD", "IDTUSD"],
        [true, "LDOTUSD", "LDO", "TUSD", "LDOTUSD"],
        [true, "SSVTUSD", "SSV", "TUSD", "SSVTUSD"]
    ],
    "popularList_BUSD": [
        [true, "Bitcoin", "BTC", "BUSD", "BTCBUSD"],
        [true, "Ethereum", "ETH", "BUSD", "ETHBUSD"],
        [true, "XRP", "XRP", "BUSD", "XRPBUSD"],
        [true, "Binance Coin", "BNB", "BUSD", "BNBBUSD"],
        [true, "TUSDBUSD", "TUSD", "BUSD", "TUSDBUSD"],
        [true, "FDUSDBUSD", "FDUSD", "BUSD", "FDUSDBUSD"],
        [true, "Litecoin", "LTC", "BUSD", "LTCBUSD"],
        [true, "Chainlink", "LINK", "BUSD", "LINKBUSD"],
        [true, "Ethereum Classic", "ETC", "BUSD", "ETCBUSD"],
        [true, "TRON", "TRX", "BUSD", "TRXBUSD"],
        [true, "EOS", "EOS", "BUSD", "EOSBUSD"],
        [true, "Stellar", "XLM", "BUSD", "XLMBUSD"],
        [true, "Cardano", "ADA", "BUSD", "ADABUSD"],
        [true, "Bitcoin Cash", "BCH", "BUSD", "BCHBUSD"],
        [true, "Qtum", "QTUM", "BUSD", "QTUMBUSD"],
        [true, "VeChain", "VET", "BUSD", "VETBUSD"],
        [true, "ICXBUSD", "ICX", "BUSD", "ICXBUSD"],
        [true, "BNTBUSD", "BNT", "BUSD", "BNTBUSD"],
        [true, "ATOMBUSD", "ATOM", "BUSD", "ATOMBUSD"],
        [true, "DASHBUSD", "DASH", "BUSD", "DASHBUSD"],
        [true, "NEOBUSD", "NEO", "BUSD", "NEOBUSD"],
        [true, "WAVESBUSD", "WAVES", "BUSD", "WAVESBUSD"],
        [true, "XTZBUSD", "XTZ", "BUSD", "XTZBUSD"],
        [true, "BATBUSD", "BAT", "BUSD", "BATBUSD"],
        [true, "ENJBUSD", "ENJ", "BUSD", "ENJBUSD"],
        [true, "ONTBUSD", "ONT", "BUSD", "ONTBUSD"],
        [true, "RVNBUSD", "RVN", "BUSD", "RVNBUSD"],
        [true, "ALGOBUSD", "ALGO", "BUSD", "ALGOBUSD"],
        [true, "TOMOBUSD", "TOMO", "BUSD", "TOMOBUSD"],
        [true, "XMRBUSD", "XMR", "BUSD", "XMRBUSD"],
        [true, "ZECBUSD", "ZEC", "BUSD", "ZECBUSD"],
        [true, "DATABUSD", "DATA", "BUSD", "DATABUSD"],
        [true, "SOLBUSD", "SOL", "BUSD", "SOLBUSD"],
        [true, "CTSIBUSD", "CTSI", "BUSD", "CTSIBUSD"],
        [true, "HBARBUSD", "HBAR", "BUSD", "HBARBUSD"],
        [true, "MATICBUSD", "MATIC", "BUSD", "MATICBUSD"],
        [true, "WRXBUSD", "WRX", "BUSD", "WRXBUSD"],
        [true, "ZILBUSD", "ZIL", "BUSD", "ZILBUSD"],
        [true, "KNCBUSD", "KNC", "BUSD", "KNCBUSD"],
        [true, "LRCBUSD", "LRC", "BUSD", "LRCBUSD"],
        [true, "IQBUSD", "IQ", "BUSD", "IQBUSD"],
        [true, "GBPBUSD", "GBP", "BUSD", "GBPBUSD"],
        [true, "DGBBUSD", "DGB", "BUSD", "DGBBUSD"],
        [true, "COMPBUSD", "COMP", "BUSD", "COMPBUSD"],
        [true, "SXPBUSD", "SXP", "BUSD", "SXPBUSD"],
        [true, "SNXBUSD", "SNX", "BUSD", "SNXBUSD"],
        [true, "STORJBUSD", "STORJ", "BUSD", "STORJBUSD"],
        [true, "MKRBUSD", "MKR", "BUSD", "MKRBUSD"],
        [true, "RUNEBUSD", "RUNE", "BUSD", "RUNEBUSD"],
        [true, "MANABUSD", "MANA", "BUSD", "MANABUSD"],
        [true, "DOGEBUSD", "DOGE", "BUSD", "DOGEBUSD"],
        [true, "ZRXBUSD", "ZRX", "BUSD", "ZRXBUSD"],
        [true, "AVABUSD", "AVA", "BUSD", "AVABUSD"],
        [true, "IOTABUSD", "IOTA", "BUSD", "IOTABUSD"],
        [true, "BALBUSD", "BAL", "BUSD", "BALBUSD"],
        [true, "YFIBUSD", "YFI", "BUSD", "YFIBUSD"],
        [true, "JSTBUSD", "JST", "BUSD", "JSTBUSD"],
        [true, "SRMBUSD", "SRM", "BUSD", "SRMBUSD"],
        [true, "ANTBUSD", "ANT", "BUSD", "ANTBUSD"],
        [true, "CRVBUSD", "CRV", "BUSD", "CRVBUSD"],
        [true, "SANDBUSD", "SAND", "BUSD", "SANDBUSD"],
        [true, "OCEANBUSD", "OCEAN", "BUSD", "OCEANBUSD"],
        [true, "NMRBUSD", "NMR", "BUSD", "NMRBUSD"],
        [true, "DOTBUSD", "DOT", "BUSD", "DOTBUSD"],
        [true, "LUNABUSD", "LUNA", "BUSD", "LUNABUSD"],
        [true, "IDEXBUSD", "IDEX", "BUSD", "IDEXBUSD"],
        [true, "RSRBUSD", "RSR", "BUSD", "RSRBUSD"],
        [true, "PAXGBUSD", "PAXG", "BUSD", "PAXGBUSD"],
        [true, "TRBBUSD", "TRB", "BUSD", "TRBBUSD"],
        [true, "SUSHIBUSD", "SUSHI", "BUSD", "SUSHIBUSD"],
        [true, "KSMBUSD", "KSM", "BUSD", "KSMBUSD"],
        [true, "EGLDBUSD", "EGLD", "BUSD", "EGLDBUSD"],
        [true, "DIABUSD", "DIA", "BUSD", "DIABUSD"],
        [true, "BELBUSD", "BEL", "BUSD", "BELBUSD"],
        [true, "WINGBUSD", "WING", "BUSD", "WINGBUSD"],
        [true, "CREAMBUSD", "CREAM", "BUSD", "CREAMBUSD"],
        [true, "UNIBUSD", "UNI", "BUSD", "UNIBUSD"],
        [true, "AVAXBUSD", "AVAX", "BUSD", "AVAXBUSD"],
        [true, "CAKEBUSD", "CAKE", "BUSD", "CAKEBUSD"],
        [true, "XVSBUSD", "XVS", "BUSD", "XVSBUSD"],
        [true, "ALPHABUSD", "ALPHA", "BUSD", "ALPHABUSD"],
        [true, "VIDTBUSD", "VIDT", "BUSD", "VIDTBUSD"],
        [true, "AAVEBUSD", "AAVE", "BUSD", "AAVEBUSD"],
        [true, "NEARBUSD", "NEAR", "BUSD", "NEARBUSD"],
        [true, "FILBUSD", "FIL", "BUSD", "FILBUSD"],
        [true, "INJBUSD", "INJ", "BUSD", "INJBUSD"],
        [true, "AERGOBUSD", "AERGO", "BUSD", "AERGOBUSD"],
        [true, "ONEBUSD", "ONE", "BUSD", "ONEBUSD"],
        [true, "AUDIOBUSD", "AUDIO", "BUSD", "AUDIOBUSD"],
        [true, "CTKBUSD", "CTK", "BUSD", "CTKBUSD"],
        [true, "KP3RBUSD", "KP3R", "BUSD", "KP3RBUSD"],
        [true, "AXSBUSD", "AXS", "BUSD", "AXSBUSD"],
        [true, "HARDBUSD", "HARD", "BUSD", "HARDBUSD"],
        [true, "CVPBUSD", "CVP", "BUSD", "CVPBUSD"],
        [true, "STRAXBUSD", "STRAX", "BUSD", "STRAXBUSD"],
        [true, "FORBUSD", "FOR", "BUSD", "FORBUSD"],
        [true, "UNFIBUSD", "UNFI", "BUSD", "UNFIBUSD"],
        [true, "FRONTBUSD", "FRONT", "BUSD", "FRONTBUSD"],
        [true, "ROSEBUSD", "ROSE", "BUSD", "ROSEBUSD"],
        [true, "SYSBUSD", "SYS", "BUSD", "SYSBUSD"],
        [true, "PROMBUSD", "PROM", "BUSD", "PROMBUSD"],
        [true, "SKLBUSD", "SKL", "BUSD", "SKLBUSD"],
        [true, "DFBUSD", "DF", "BUSD", "DFBUSD"],
        [true, "JUVBUSD", "JUV", "BUSD", "JUVBUSD"],
        [true, "PSGBUSD", "PSG", "BUSD", "PSGBUSD"],
        [true, "DEXEBUSD", "DEXE", "BUSD", "DEXEBUSD"],
        [true, "CKBBUSD", "CKB", "BUSD", "CKBBUSD"],
        [true, "TWTBUSD", "TWT", "BUSD", "TWTBUSD"],
        [true, "LITBUSD", "LIT", "BUSD", "LITBUSD"],
        [true, "SFPBUSD", "SFP", "BUSD", "SFPBUSD"],
        [true, "FXSBUSD", "FXS", "BUSD", "FXSBUSD"],
        [true, "DODOBUSD", "DODO", "BUSD", "DODOBUSD"],
        [true, "BAKEBUSD", "BAKE", "BUSD", "BAKEBUSD"],
        [true, "UFTBUSD", "UFT", "BUSD", "UFTBUSD"],
        [true, "1INCHBUSD", "1INCH", "BUSD", "1INCHBUSD"],
        [true, "BANDBUSD", "BAND", "BUSD", "BANDBUSD"],
        [true, "GRTBUSD", "GRT", "BUSD", "GRTBUSD"],
        [true, "IOSTBUSD", "IOST", "BUSD", "IOSTBUSD"],
        [true, "OMGBUSD", "OMG", "BUSD", "OMGBUSD"],
        [true, "REEFBUSD", "REEF", "BUSD", "REEFBUSD"],
        [true, "ACMBUSD", "ACM", "BUSD", "ACMBUSD"],
        [true, "AUCTIONBUSD", "AUCTION", "BUSD", "AUCTIONBUSD"],
        [true, "PHABUSD", "PHA", "BUSD", "PHABUSD"],
        [true, "TVKBUSD", "TVK", "BUSD", "TVKBUSD"],
        [true, "BADGERBUSD", "BADGER", "BUSD", "BADGERBUSD"],
        [true, "FISBUSD", "FIS", "BUSD", "FISBUSD"],
        [true, "OMBUSD", "OM", "BUSD", "OMBUSD"],
        [true, "PONDBUSD", "POND", "BUSD", "PONDBUSD"],
        [true, "DEGOBUSD", "DEGO", "BUSD", "DEGOBUSD"],
        [true, "ALICEBUSD", "ALICE", "BUSD", "ALICEBUSD"],
        [true, "CHZBUSD", "CHZ", "BUSD", "CHZBUSD"],
        [true, "LINABUSD", "LINA", "BUSD", "LINABUSD"],
        [true, "PERPBUSD", "PERP", "BUSD", "PERPBUSD"],
        [true, "SUPERBUSD", "SUPER", "BUSD", "SUPERBUSD"],
        [true, "CFXBUSD", "CFX", "BUSD", "CFXBUSD"],
        [true, "XVGBUSD", "XVG", "BUSD", "XVGBUSD"],
        [true, "TKOBUSD", "TKO", "BUSD", "TKOBUSD"],
        [true, "TLMBUSD", "TLM", "BUSD", "TLMBUSD"],
        [true, "BURGERBUSD", "BURGER", "BUSD", "BURGERBUSD"],
        [true, "SLPBUSD", "SLP", "BUSD", "SLPBUSD"],
        [true, "SHIBBUSD", "SHIB", "BUSD", "SHIBBUSD"],
        [true, "ICPBUSD", "ICP", "BUSD", "ICPBUSD"],
        [true, "ARBUSD", "AR", "BUSD", "ARBUSD"],
        [true, "POLSBUSD", "POLS", "BUSD", "POLSBUSD"],
        [true, "MDXBUSD", "MDX", "BUSD", "MDXBUSD"],
        [true, "MASKBUSD", "MASK", "BUSD", "MASKBUSD"],
        [true, "LPTBUSD", "LPT", "BUSD", "LPTBUSD"],
        [true, "CELRBUSD", "CELR", "BUSD", "CELRBUSD"],
        [true, "ATMBUSD", "ATOM", "BUSD", "ATMBUSD"],
        [true, "ZENBUSD", "ZEN", "BUSD", "ZENBUSD"],
        [true, "FTMBUSD", "FTM", "BUSD", "FTMBUSD"],
        [true, "THETABUSD", "THETA", "BUSD", "THETABUSD"],
        [true, "KAVABUSD", "KAVA", "BUSD", "KAVABUSD"],
        [true, "ATABUSD", "ATA", "BUSD", "ATABUSD"],
        [true, "GTCBUSD", "GTC", "BUSD", "GTCBUSD"],
        [true, "TORNBUSD", "TORN", "BUSD", "TORNBUSD"],
        [true, "Coti", "COTI", "BUSD", "COTIBUSD"],
        [true, "Chromia", "CHR", "BUSD", "CHRBUSD"],
        [true, "StormX", "STMX", "BUSD", "STMXBUSD"],
        [true, "FTTBUSD", "FTT", "BUSD", "FTTBUSD"],
        [true, "DOCKBUSD", "DOCK", "BUSD", "DOCKBUSD"],
        [true, "ERNBUSD", "ERN", "BUSD", "ERNBUSD"],
        [true, "KLAYBUSD", "KLAY", "BUSD", "KLAYBUSD"],
        [true, "UTKBUSD", "UTK", "BUSD", "UTKBUSD"],
        [true, "IOTXBUSD", "IOTX", "BUSD", "IOTXBUSD"],
        [true, "BONDBUSD", "BOND", "BUSD", "BONDBUSD"],
        [true, "LTOBUSD", "LTO", "BUSD", "LTOBUSD"],
        [true, "ADXBUSD", "ADX", "BUSD", "ADXBUSD"],
        [true, "C98BUSD", "C98", "BUSD", "C98BUSD"],
        [true, "CLVBUSD", "CLV", "BUSD", "CLVBUSD"],
        [true, "QNTBUSD", "QNT", "BUSD", "QNTBUSD"],
        [true, "FLOWBUSD", "FLOW", "BUSD", "FLOWBUSD"],
        [true, "XECBUSD", "XEC", "BUSD", "XECBUSD"],
        [true, "MINABUSD", "MINA", "BUSD", "MINABUSD"],
        [true, "RAYBUSD", "RAY", "BUSD", "RAYBUSD"],
        [true, "FARMBUSD", "FARM", "BUSD", "FARMBUSD"],
        [true, "ALPACABUSD", "ALPACA", "BUSD", "ALPACABUSD"],
        [true, "ORNBUSD", "ORN", "BUSD", "ORNBUSD"],
        [true, "MBOXBUSD", "MBOX", "BUSD", "MBOXBUSD"],
        [true, "WAXPBUSD", "WAXP", "BUSD", "WAXPBUSD"],
        [true, "MTLBUSD", "MTL", "BUSD", "MTLBUSD"],
        [true, "OGNBUSD", "OGN", "BUSD", "OGNBUSD"],
        [true, "DYDXBUSD", "DYDX", "BUSD", "DYDXBUSD"],
        [true, "ELFBUSD", "ELF", "BUSD", "ELFBUSD"],
        [true, "GALABUSD", "GALA", "BUSD", "GALABUSD"],
        [true, "SUNBUSD", "SUN", "BUSD", "SUNBUSD"],
        [true, "ILVBUSD", "ILV", "BUSD", "ILVBUSD"],
        [true, "RENBUSD", "REN", "BUSD", "RENBUSD"],
        [true, "YGGBUSD", "YGG", "BUSD", "YGGBUSD"],
        [true, "STXBUSD", "STX", "BUSD", "STXBUSD"],
        [true, "FETBUSD", "FET", "BUSD", "FETBUSD"],
        [true, "ARPABUSD", "ARPA", "BUSD", "ARPABUSD"],
        [true, "LSKBUSD", "LSK", "BUSD", "LSKBUSD"],
        [true, "FIDABUSD", "FIDA", "BUSD", "FIDABUSD"],
        [true, "DENTBUSD", "DENT", "BUSD", "DENTBUSD"],
        [true, "AGLDBUSD", "AGLD", "BUSD", "AGLDBUSD"],
        [true, "RADBUSD", "RAD", "BUSD", "RADBUSD"],
        [true, "HIVEBUSD", "HIVE", "BUSD", "HIVEBUSD"],
        [true, "BETABUSD", "BETA", "BUSD", "BETABUSD"],
        [true, "RAREBUSD", "RARE", "BUSD", "RAREBUSD"],
        [true, "TROYBUSD", "TROY", "BUSD", "TROYBUSD"],
        [true, "CHESSBUSD", "CHESS", "BUSD", "CHESSBUSD"],
        [true, "SCRTBUSD", "SCRT", "BUSD", "SCRTBUSD"],
        [true, "CELOBUSD", "CELO", "BUSD", "CELOBUSD"],
        [true, "DARBUSD", "DAR", "BUSD", "DARBUSD"],
        [true, "BNXBUSD", "BNX", "BUSD", "BNXBUSD"],
        [true, "LAZIOBUSD", "LAZIO", "BUSD", "LAZIOBUSD"],
        [true, "MOVRBUSD", "MOVR", "BUSD", "MOVRBUSD"],
        [true, "CITYBUSD", "CITY", "BUSD", "CITYBUSD"],
        [true, "ENSBUSD", "ENS", "BUSD", "ENSBUSD"],
        [true, "ANKRBUSD", "ANKR", "BUSD", "ANKRBUSD"],
        [true, "QIBUSD", "QI", "BUSD", "QIBUSD"],
        [true, "JASMYBUSD", "JASMY", "BUSD", "JASMYBUSD"],
        [true, "AMPBUSD", "AMP", "BUSD", "AMPBUSD"],
        [true, "PLABUSD", "PLA", "BUSD", "PLABUSD"],
        [true, "PYRBUSD", "PYR", "BUSD", "PYRBUSD"],
        [true, "RNDRBUSD", "RNDR", "BUSD", "RNDRBUSD"],
        [true, "ALCXBUSD", "ALCX", "BUSD", "ALCXBUSD"],
        [true, "MCBUSD", "MC", "BUSD", "MCBUSD"],
        [true, "BICOBUSD", "BICO", "BUSD", "BICOBUSD"],
        [true, "FLUXBUSD", "FLUX", "BUSD", "FLUXBUSD"],
        [true, "REQBUSD", "REQ", "BUSD", "REQBUSD"],
        [true, "VOXELBUSD", "VOXEL", "BUSD", "VOXELBUSD"],
        [true, "COSBUSD", "COS", "BUSD", "COSBUSD"],
        [true, "CTXCBUSD", "CTXC", "BUSD", "CTXCBUSD"],
        [true, "HIGHBUSD", "HIGH", "BUSD", "HIGHBUSD"],
        [true, "CVXBUSD", "CVX", "BUSD", "CVXBUSD"],
        [true, "PEOPLEBUSD", "PEOPLE", "BUSD", "PEOPLEBUSD"],
        [true, "OOKIBUSD", "OOKI", "BUSD", "OOKIBUSD"],
        [true, "MDTBUSD", "MDT", "BUSD", "MDTBUSD"],
        [true, "NULSBUSD", "NULS", "BUSD", "NULSBUSD"],
        [true, "SPELLBUSD", "SPELL", "BUSD", "SPELLBUSD"],
        [true, "JOEBUSD", "JOE", "BUSD", "JOEBUSD"],
        [true, "DUSKBUSD", "DUSK", "BUSD", "DUSKBUSD"],
        [true, "ACHBUSD", "ACH", "BUSD", "ACHBUSD"],
        [true, "IMXBUSD", "IMX", "BUSD", "IMXBUSD"],
        [true, "GLMRBUSD", "GLMR", "BUSD", "GLMRBUSD"],
        [true, "UMABUSD", "UMA", "BUSD", "UMABUSD"],
        [true, "LOKABUSD", "LOKA", "BUSD", "LOKABUSD"],
        [true, "XNOBUSD", "XNO", "BUSD", "XNOBUSD"],
        [true, "WOOBUSD", "WOO", "BUSD", "WOOBUSD"],
        [true, "TFUELBUSD", "TFUEL", "BUSD", "TFUELBUSD"],
        [true, "TBUSD", "T", "BUSD", "TBUSD"],
        [true, "ASTRBUSD", "ASTR", "BUSD", "ASTRBUSD"],
        [true, "GMTBUSD", "GMT", "BUSD", "GMTBUSD"],
        [true, "KDABUSD", "KDA", "BUSD", "KDABUSD"],
        [true, "APEBUSD", "APE", "BUSD", "APEBUSD"],
        [true, "ALPINEBUSD", "ALPINE", "BUSD", "ALPINEBUSD"],
        [true, "BSWBUSD", "BSW", "BUSD", "BSWBUSD"],
        [true, "SANTOSBUSD", "SANTOS", "BUSD", "SANTOSBUSD"],
        [true, "MULTIBUSD", "MULTI", "BUSD", "MULTIBUSD"],
        [true, "PORTOBUSD", "PORTO", "BUSD", "PORTOBUSD"],
        [true, "BTTCBUSD", "BTTC", "BUSD", "BTTCBUSD"],
        [true, "MBLBUSD", "MBL", "BUSD", "MBLBUSD"],
        [true, "MOBBUSD", "MOB", "BUSD", "MOBBUSD"],
        [true, "GALBUSD", "GAL", "BUSD", "GALBUSD"],
        [true, "LDOBUSD", "LDO", "BUSD", "LDOBUSD"],
        [true, "EPXBUSD", "EPX", "BUSD", "EPXBUSD"],
        [true, "CVCBUSD", "CVC", "BUSD", "CVCBUSD"],
        [true, "REIBUSD", "REI", "BUSD", "REIBUSD"],
        [true, "DREPBUSD", "DREP", "BUSD", "DREPBUSD"],
        [true, "AKROBUSD", "AKRO", "BUSD", "AKROBUSD"],
        [true, "PUNDIXBUSD", "PUNDIX", "BUSD", "PUNDIXBUSD"],
        [true, "LUNCBUSD", "LUNC", "BUSD", "LUNCBUSD"],
        [true, "USTCBUSD", "USTC", "BUSD", "USTCBUSD"],
        [true, "OPBUSD", "OP", "BUSD", "OPBUSD"],
        [true, "OGBUSD", "OG", "BUSD", "OGBUSD"],
        [true, "KEYBUSD", "KEY", "BUSD", "KEYBUSD"],
        [true, "ASRBUSD", "ASR", "BUSD", "ASRBUSD"],
        [true, "FIROBUSD", "FIRO", "BUSD", "FIROBUSD"],
        [true, "NKNBUSD", "NKN", "BUSD", "NKNBUSD"],
        [true, "WBTCBUSD", "WBTC", "BUSD", "WBTCBUSD"],
        [true, "LEVERBUSD", "LEVER", "BUSD", "LEVERBUSD"],
        [true, "GLMBUSD", "GLM", "BUSD", "GLMBUSD"],
        [true, "SSVBUSD", "SSV", "BUSD", "SSVBUSD"],
        [true, "STGBUSD", "STG", "BUSD", "STGBUSD"],
        [true, "ARKBUSD", "ARK", "BUSD", "ARKBUSD"],
        [true, "BETHBUSD", "BETH", "BUSD", "BETHBUSD"],
        [true, "LOOMBUSD", "LOOM", "BUSD", "LOOMBUSD"],
        [true, "SNMBUSD", "SNM", "BUSD", "SNMBUSD"],
        [true, "AMBBUSD", "AMB", "BUSD", "AMBBUSD"],
        [true, "PHBBUSD", "PHB", "BUSD", "PHBBUSD"],
        [true, "GASBUSD", "GAS", "BUSD", "GASBUSD"],
        [true, "PROSBUSD", "PROS", "BUSD", "PROSBUSD"],
        [true, "VIBBUSD", "VIB", "BUSD", "VIBBUSD"],
        [true, "GMXBUSD", "GMX", "BUSD", "GMXBUSD"],
        [true, "AGIXBUSD", "AGIX", "BUSD", "AGIXBUSD"],
        [true, "SNTBUSD", "SNT", "BUSD", "SNTBUSD"],
        [true, "POLYXBUSD", "POLYX", "BUSD", "POLYXBUSD"],
        [true, "APTBUSD", "APT", "BUSD", "APTBUSD"],
        [true, "OSMOBUSD", "OSMO", "BUSD", "OSMOBUSD"],
        [true, "HFTBUSD", "HFT", "BUSD", "HFTBUSD"],
        [true, "HOOKBUSD", "HOOK", "BUSD", "HOOKBUSD"],
        [true, "MAGICBUSD", "MAGIC", "BUSD", "MAGICBUSD"],
        [true, "RPLBUSD", "RPL", "BUSD", "RPLBUSD"],
        [true, "GFTBUSD", "GFT", "BUSD", "GFTBUSD"],
        [true, "ACABUSD", "ACA", "BUSD", "ACABUSD"]
    ],
    "popularList_Fiat": [
        [true, "BTCGBP", "BTC", "GBP", "BTCGBP"],
        [true, "ETHGBP", "ETH", "GBP", "ETHGBP"],
        [true, "Bitcoin", "BTC", "EUR", "BTCEUR"],
        [true, "Ethereum", "ETH", "EUR", "ETHEUR"],
        [true, "Binance Coin", "BNB", "EUR", "BNBEUR"],
        [true, "Ripple", "XRP", "EUR", "XRPEUR"],
        [true, "Tether", "USDT", "RUB", "USDTRUB"],
        [true, "Binance USD", "BUSD", "RUB", "BUSDRUB"],
        [true, "USDTZAR", "USDT", "ZAR", "USDTZAR"],
        [true, "BUSDZAR", "BUSD", "ZAR", "BUSDZAR"],
        [true, "Tether", "USDT", "TRY", "USDTTRY"],
        [true, "Binance USD", "BUSD", "TRY", "BUSDTRY"],
        [true, "TUSDTRY", "TUSD", "TRY", "TUSDTRY"],
        [true, "USDTBRL", "USDT", "BRL", "USDTBRL"],
        [true, "BUSDBRL", "BUSD", "BRL", "BUSDBRL"],
        [true, "USDTBIDR", "USDT", "BIDR", "USDTBIDR"],
        [true, "BUSDBIDR", "BUSD", "BIDR", "BUSDBIDR"],
        [true, "USDTRON", "USDT", "RON", "USDTRON"],
        [true, "LINKEUR", "LINK", "EUR", "LINKEUR"],
        [true, "DOTEUR", "DOT", "EUR", "DOTEUR"],
        [true, "LTCEUR", "LTC", "EUR", "LTCEUR"],
        [true, "ADAEUR", "ADA", "EUR", "ADAEUR"],
        [true, "BCHEUR", "BCH", "EUR", "BCHEUR"],
        [true, "YFIEUR", "YFI", "EUR", "YFIEUR"],
        [true, "XLMEUR", "XLM", "EUR", "XLMEUR"],
        [true, "GRTEUR", "GRT", "EUR", "GRTEUR"],
        [true, "EOSEUR", "EOS", "EUR", "EOSEUR"],
        [true, "DOGEEUR", "DOGE", "EUR", "DOGEEUR"],
        [true, "EGLDEUR", "EGLD", "EUR", "EGLDEUR"],
        [true, "AVAXEUR", "AVAX", "EUR", "AVAXEUR"],
        [true, "CHZEUR", "CHZ", "EUR", "CHZEUR"],
        [true, "ENJEUR", "ENJ", "EUR", "ENJEUR"],
        [true, "MATICEUR", "MATIC", "EUR", "MATICEUR"],
        [true, "THETAEUR", "THETA", "EUR", "THETAEUR"],
        [true, "WINEUR", "WIN", "EUR", "WINEUR"],
        [true, "TRXEUR", "TRX", "EUR", "TRXEUR"],
        [true, "SHIBEUR", "SHIB", "EUR", "SHIBEUR"],
        [true, "ETCEUR", "ETC", "EUR", "ETCEUR"],
        [true, "SOLEUR", "SOL", "EUR", "SOLEUR"],
        [true, "ICPEUR", "ICP", "EUR", "ICPEUR"],
        [true, "RUNEEUR", "RUNE", "EUR", "RUNEEUR"],
        [true, "LAZIOEUR", "LAZIO", "EUR", "LAZIOEUR"],
        [true, "PORTOEUR", "PORTO", "EUR", "PORTOEUR"],
        [true, "ALPINEEUR", "ALPINE", "EUR", "ALPINEEUR"],
        [true, "ATOMEUR", "ATOM", "EUR", "ATOMEUR"],
        [true, "GALAEUR", "GALA", "EUR", "GALAEUR"],
        [true, "NEAREUR", "NEAR", "EUR", "NEAREUR"],
        [true, "WAVESEUR", "WAVES", "EUR", "WAVESEUR"],
        [true, "APEEUR", "APE", "EUR", "APEEUR"],
        [true, "GMTEUR", "GMT", "EUR", "GMTEUR"],
        [true, "FTMEUR", "FTM", "EUR", "FTMEUR"],
        [true, "GALEUR", "GALA", "EUR", "GALEUR"],
        [true, "DAREUR", "DAR", "EUR", "DAREUR"],
        [true, "OPEUR", "OP", "EUR", "OPEUR"],
        [true, "APTEUR", "APT", "EUR", "APTEUR"],
        [true, "ARBEUR", "ARB", "EUR", "ARBEUR"],
        [true, "EDUEUR", "EDU", "EUR", "EDUEUR"],
        [true, "SUIEUR", "SUI", "EUR", "SUIEUR"],
        [true, "XRPGBP", "XRP", "GBP", "XRPGBP"],
        [true, "BNBGBP", "BNB", "GBP", "BNBGBP"],
        [true, "LINKGBP", "LINK", "GBP", "LINKGBP"],
        [true, "DOGEGBP", "DOGE", "GBP", "DOGEGBP"],
        [true, "DOTGBP", "DOT", "GBP", "DOTGBP"],
        [true, "ADAGBP", "ADA", "GBP", "ADAGBP"],
        [true, "CHZGBP", "CHZ", "GBP", "CHZGBP"],
        [true, "LTCGBP", "LTC", "GBP", "LTCGBP"],
        [true, "MATICGBP", "MATIC", "GBP", "MATICGBP"],
        [true, "SOLGBP", "SOL", "GBP", "SOLGBP"],
        [true, "THORChain", "RUNE", "GBP", "RUNEGBP"],
        [true, "Bitcoin", "BTC", "RUB", "BTCRUB"],
        [true, "Ethereum", "ETH", "RUB", "ETHRUB"],
        [true, "Ripple", "XRP", "RUB", "XRPRUB"],
        [true, "Binance Coin", "BNB", "RUB", "BNBRUB"],
        [true, "LTCRUB", "LTC", "RUB", "LTCRUB"],
        [true, "ADARUB", "ADA", "RUB", "ADARUB"],
        [true, "MATICRUB", "MATIC", "RUB", "MATICRUB"],
        [true, "DOTRUB", "DOT", "RUB", "DOTRUB"],
        [true, "SOLRUB", "SOL", "RUB", "SOLRUB"],
        [true, "ARPARUB", "ARPA", "RUB", "ARPARUB"],
        [true, "ALGORUB", "ALGO", "RUB", "ALGORUB"],
        [true, "NEORUB", "NEO", "RUB", "NEORUB"],
        [true, "NEAR Protocol", "NEAR", "RUB", "NEARRUB"],
        [true, "ARBRUB", "ARB", "RUB", "ARBRUB"],
        [true, "ARKMRUB", "ARKM", "RUB", "ARKMRUB"],
        [true, "WLDRUB", "WLD", "RUB", "WLDRUB"],
        [true, "BTCZAR", "BTC", "ZAR", "BTCZAR"],
        [true, "ETHZAR", "ETH", "ZAR", "ETHZAR"],
        [true, "Bitcoin", "BTC", "TRY", "BTCTRY"],
        [true, "Binance Coin", "BNB", "TRY", "BNBTRY"],
        [true, "Ethereum", "ETH", "TRY", "ETHTRY"],
        [true, "Ripple", "XRP", "TRY", "XRPTRY"],
        [true, "LINKTRY", "LINK", "TRY", "LINKTRY"],
        [true, "SXPTRY", "SXP", "TRY", "SXPTRY"],
        [true, "AVAXTRY", "AVAX", "TRY", "AVAXTRY"],
        [true, "TRXTRY", "TRX", "TRY", "TRXTRY"],
        [true, "CHZTRY", "CHZ", "TRY", "CHZTRY"],
        [true, "XLMTRY", "XLM", "TRY", "XLMTRY"],
        [true, "DOGETRY", "DOGE", "TRY", "DOGETRY"],
        [true, "DOTTRY", "DOT", "TRY", "DOTTRY"],
        [true, "ADATRY", "ADA", "TRY", "ADATRY"],
        [true, "HOTTRY", "HOT", "TRY", "HOTTRY"],
        [true, "NEOTRY", "NEO", "TRY", "NEOTRY"],
        [true, "EOSTRY", "EOS", "TRY", "EOSTRY"],
        [true, "RVNTRY", "RVN", "TRY", "RVNTRY"],
        [true, "VETTRY", "VET", "TRY", "VETTRY"],
        [true, "SHIBTRY", "SHIB", "TRY", "SHIBTRY"],
        [true, "MATICTRY", "MATIC", "TRY", "MATICTRY"],
        [true, "Solana", "SOL", "TRY", "SOLTRY"],
        [true, "GRTTRY", "GRT", "TRY", "GRTTRY"],
        [true, "TLMTRY", "TLM", "TRY", "TLMTRY"],
        [true, "ARPATRY", "ARPA", "TRY", "ARPATRY"],
        [true, "LAZIOTRY", "LAZIO", "TRY", "LAZIOTRY"],
        [true, "REEFTRY", "REEF", "TRY", "REEFTRY"],
        [true, "MANATRY", "MANA", "TRY", "MANATRY"],
        [true, "SANDTRY", "SAND", "TRY", "SANDTRY"],
        [true, "PORTOTRY", "PORTO", "TRY", "PORTOTRY"],
        [true, "SLPTRY", "SLP", "TRY", "SLPTRY"],
        [true, "LRCTRY", "LRC", "TRY", "LRCTRY"],
        [true, "SANTOSTRY", "SANTOS", "TRY", "SANTOSTRY"],
        [true, "BELTRY", "BEL", "TRY", "BELTRY"],
        [true, "DENTTRY", "DENT", "TRY", "DENTTRY"],
        [true, "ENJTRY", "ENJ", "TRY", "ENJTRY"],
        [true, "ALICETRY", "ALICE", "TRY", "ALICETRY"],
        [true, "GALATRY", "GALA", "TRY", "GALATRY"],
        [true, "LUNATRY", "LUNA", "TRY", "LUNATRY"],
        [true, "FTMTRY", "FTM", "TRY", "FTMTRY"],
        [true, "MINATRY", "MINA", "TRY", "MINATRY"],
        [true, "XTZTRY", "XTZ", "TRY", "XTZTRY"],
        [true, "ATOMTRY", "ATOM", "TRY", "ATOMTRY"],
        [true, "ICPTRY", "ICP", "TRY", "ICPTRY"],
        [true, "Oasis Network", "ROSE", "TRY", "ROSETRY"],
        [true, "BTTCTRY", "BTTC", "TRY", "BTTCTRY"],
        [true, "COSTRY", "COS", "TRY", "COSTRY"],
        [true, "ONETRY", "ONE", "TRY", "ONETRY"],
        [true, "SPELLTRY", "SPELL", "TRY", "SPELLTRY"],
        [true, "AXSTRY", "AXS", "TRY", "AXSTRY"],
        [true, "DARTRY", "DAR", "TRY", "DARTRY"],
        [true, "NEARTRY", "NEAR", "TRY", "NEARTRY"],
        [true, "ALPINETRY", "ALPINE", "TRY", "ALPINETRY"],
        [true, "INJTRY", "INJ", "TRY", "INJTRY"],
        [true, "API3TRY", "API3", "TRY", "API3TRY"],
        [true, "MBOXTRY", "MBOX", "TRY", "MBOXTRY"],
        [true, "UMATRY", "UMA", "TRY", "UMATRY"],
        [true, "TWTTRY", "TWT", "TRY", "TWTTRY"],
        [true, "APETRY", "APE", "TRY", "APETRY"],
        [true, "JASMYTRY", "JASMY", "TRY", "JASMYTRY"],
        [true, "FILTRY", "FIL", "TRY", "FILTRY"],
        [true, "ZILTRY", "ZIL", "TRY", "ZILTRY"],
        [true, "GMTTRY", "GMT", "TRY", "GMTTRY"],
        [true, "WAVESTRY", "WAVES", "TRY", "WAVESTRY"],
        [true, "BSWTRY", "BSW", "TRY", "BSWTRY"],
        [true, "AUDIOTRY", "AUDIO", "TRY", "AUDIOTRY"],
        [true, "GALTRY", "GALA", "TRY", "GALTRY"],
        [true, "ENSTRY", "ENS", "TRY", "ENSTRY"],
        [true, "ALGOTRY", "ALGO", "TRY", "ALGOTRY"],
        [true, "STORJTRY", "STORJ", "TRY", "STORJTRY"],
        [true, "ETCTRY", "ETC", "TRY", "ETCTRY"],
        [true, "ANKRTRY", "ANKR", "TRY", "ANKRTRY"],
        [true, "APTTRY", "APT", "TRY", "APTTRY"],
        [true, "FETTRY", "FET", "TRY", "FETTRY"],
        [true, "CFXTRY", "CFX", "TRY", "CFXTRY"],
        [true, "STXTRY", "STX", "TRY", "STXTRY"],
        [true, "AGIXTRY", "AGIX", "TRY", "AGIXTRY"],
        [true, "ARBTRY", "ARB", "TRY", "ARBTRY"],
        [true, "IDTRY", "ID", "TRY", "IDTRY"],
        [true, "JOETRY", "JOE", "TRY", "JOETRY"],
        [true, "MAGICTRY", "MAGIC", "TRY", "MAGICTRY"],
        [true, "ACHTRY", "ACH", "TRY", "ACHTRY"],
        [true, "XVSTRY", "XVS", "TRY", "XVSTRY"],
        [true, "EDUTRY", "EDU", "TRY", "EDUTRY"],
        [true, "SUITRY", "SUI", "TRY", "SUITRY"],
        [true, "RNDRTRY", "RNDR", "TRY", "RNDRTRY"],
        [true, "OGTRY", "OG", "TRY", "OGTRY"],
        [true, "PEPETRY", "PEPE", "TRY", "PEPETRY"],
        [true, "FLOKITRY", "FLOKI", "TRY", "FLOKITRY"],
        [true, "CITYTRY", "CITY", "TRY", "CITYTRY"],
        [true, "COMBOTRY", "COMBO", "TRY", "COMBOTRY"],
        [true, "LTCTRY", "LTC", "TRY", "LTCTRY"],
        [true, "RADTRY", "RAD", "TRY", "RADTRY"],
        [true, "OPTRY", "OP", "TRY", "OPTRY"],
        [true, "PAXGTRY", "PAXG", "TRY", "PAXGTRY"],
        [true, "MAVTRY", "MAV", "TRY", "MAVTRY"],
        [true, "OCEANTRY", "OCEAN", "TRY", "OCEANTRY"],
        [true, "BCHTRY", "BCH", "TRY", "BCHTRY"],
        [true, "XVGTRY", "XVG", "TRY", "XVGTRY"],
        [true, "ARKMTRY", "ARKM", "TRY", "ARKMTRY"],
        [true, "ACATRY", "ACA", "TRY", "ACATRY"],
        [true, "COMPTRY", "COMP", "TRY", "COMPTRY"],
        [true, "XECTRY", "XEC", "TRY", "XECTRY"],
        [true, "WLDTRY", "WLD", "TRY", "WLDTRY"],
        [true, "AMPTRY", "AMP", "TRY", "AMPTRY"],
        [true, "OGNTRY", "OGN", "TRY", "OGNTRY"],
        [true, "ASRTRY", "ASR", "TRY", "ASRTRY"],
        [true, "ATMTRY", "ATM", "TRY", "ATMTRY"],
        [true, "ACMTRY", "ACM", "TRY", "ACMTRY"],
        [true, "BARTRY", "BAR", "TRY", "BARTRY"],
        [true, "JUVTRY", "JUV", "TRY", "JUVTRY"],
        [true, "PSGTRY", "PSG", "TRY", "PSGTRY"],
        [true, "SEITRY", "SEI", "TRY", "SEITRY"],
        [true, "CYBERTRY", "CYBER", "TRY", "CYBERTRY"],
        [true, "BTCBRL", "BTC", "BRL", "BTCBRL"],
        [true, "ETHBRL", "ETH", "BRL", "ETHBRL"],
        [true, "BNBBRL", "BNB", "BRL", "BNBBRL"],
        [true, "XRPBRL", "XRP", "BRL", "XRPBRL"],
        [true, "LINKBRL", "LINK", "BRL", "LINKBRL"],
        [true, "LTCBRL", "LTC", "BRL", "LTCBRL"],
        [true, "DOGEBRL", "DOGE", "BRL", "DOGEBRL"],
        [true, "ADABRL", "ADA", "BRL", "ADABRL"],
        [true, "DOTBRL", "DOT", "BRL", "DOTBRL"],
        [true, "CHZBRL", "CHZ", "BRL", "CHZBRL"],
        [true, "ENJBRL", "ENJ", "BRL", "ENJBRL"],
        [true, "MATICBRL", "MATIC", "BRL", "MATICBRL"],
        [true, "SHIBBRL", "SHIB", "BRL", "SHIBBRL"],
        [true, "Solana", "SOL", "BRL", "SOLBRL"],
        [true, "AVAXBRL", "AVAX", "BRL", "AVAXBRL"],
        [true, "MANABRL", "MANA", "BRL", "MANABRL"],
        [true, "GALABRL", "GALA", "BRL", "GALABRL"],
        [true, "SANDBRL", "SAND", "BRL", "SANDBRL"],
        [true, "GALBRL", "GALA", "BRL", "GALBRL"],
        [true, "BUSDRON", "BUSD", "RON", "BUSDRON"],
        [true, "BTCRON", "BTC", "RON", "BTCRON"],
        [true, "EGLDRON", "EGLD", "RON", "EGLDRON"],
        [true, "BTCBIDR", "BTC", "BIDR", "BTCBIDR"],
        [true, "ETHBIDR", "ETH", "BIDR", "ETHBIDR"],
        [true, "BNBBIDR", "BNB", "BIDR", "BNBBIDR"],
        [true, "ZILBIDR", "ZIL", "BIDR", "ZILBIDR"],
        [true, "TKOBIDR", "TKO", "BIDR", "TKOBIDR"],
        [true, "DOGEBIDR", "DOGE", "BIDR", "DOGEBIDR"],
        [true, "MATICBIDR", "MATIC", "BIDR", "MATICBIDR"],
        [true, "ADABIDR", "ADA", "BIDR", "ADABIDR"],
        [true, "XRPBIDR", "XRP", "BIDR", "XRPBIDR"],
        [true, "SOLBIDR", "SOL", "BIDR", "SOLBIDR"],
        [true, "AXSBIDR", "AXS", "BIDR", "AXSBIDR"],
        [true, "NBTBIDR", "NBT", "BIDR", "NBTBIDR"]
    ],
    "ratesInfo": {
        "apiInfo": {
            "OpenExchangeRates":  {
                "key": "d2jo3a893ghlwiugbn4go38qy2456vds",
                "url": "https://openexchangerates.org/api/latest.json",
                "image": "apiOpenExchangeRates.png"
            },
            "KoreaExim": {
                "key": "f3j092guay839yt832htgaogl3gwg",
                "url": "https://www.koreaexim.go.kr/site/program/financial/exchangeJSON",
                "image": ""
            },
            "DunamuQuotation": {
                "key": "",
                "url": "https://quotation-api-cdn.dunamu.com/v1/forex/recent?codes=FRX.",
                "image": "apiHanaBank.png"
            }
        },
        "fiatList": ["EUR", "JPY", "GBP", "CHF", "CAD", "AUD", "CNY", "HKD", "SEK", "NZD", "KRW", "SGD", "NOK", "MXN", "INR", "RUB", "ZAR", "TRY", "BRL", "AED", "BHD", "BND", "CNH", "CZK", "DKK", "IDR", "ILS", "MYR", "QAR", "SAR", "THB", "TWD", "CLP", "COP", "EGP", "HKD", "HUF", "KWD", "OMR", "PHP", "PLN", "PKR", "RON", "BDT", "DZD", "ETB", "FJD", "JOD", "KES", "KHR", "KZT", "LKR", "LYD", "MMK", "MNT", "MOP", "NPR", "TZS", "UZS", "VND"]
    },
    "exchangeInfo": {
        "Binance": {
            "isActive": {
                "spot": true,
                "futures": false
            },
            "information": {
                "image": "exchangeBinance.png",
                "keyList": [],
                "publicApi": {
                    "ticker": "/api/v3/ticker/24hr",
                    "orderbook": ""
                },
                "baseUrlList": [
                    "https://api.binance.com",
                    "https://api-gcp.binance.com",
                    "https://fapi.binance/fapi/v1/ticker/24hr"
                ]
            },
            "spot": {
                "stable": {
                    "category": "spot",
                    "paymentCurrencyList": ["USDT", "TUSD", "BUSD", "USDC", "DAI", "FDUSD"]
                },
                "bitcoin": {
                    "category": "spot",
                    "paymentCurrencyList": ["BTC"]
                },
                "alt": {
                    "category": "spot",
                    "paymentCurrencyList": ["BNB", "ETH", "DOGE", "XRP", "VAI", "TRX", "DOT"]
                },
                "fiat": {
                    "category": "spot",
                    "paymentCurrencyList": ["TRY", "EUR", "BRL", "ARS", "BIDR", "GBP", "IDRT", "NGN", "PLN", "RON", "RUB", "UAH", "ZAR"]
                }
            },
            "futures": {
                "stableMargin" : {
                    "category": "usd(s)-m",
                    "paymentCurrencyList": ["USDT", "BUSD"]
                },
                "coinMargin": {
                    "category": "coin-m",
                    "paymentCurrencyList": ["USD"]
                }
            }
        },
        "Bybit": {
            "isActive": {
                "spot": true,
                "futures": true
            },
            "information": {
                "image": "exchangeBybit.jpeg",
                "keyList": [],
                "baseUrlList": [
                    "https://api.bybit.com",
                    "https://api.bytick.com"
                ],
                "publicApi": {
                    "ticker": "/v5/market/tickers",
                    "orderbook": ""
                }
            },
            "spot": {
                "stable": {
                    "category": "spot",
                    "paymentCurrencyList": ["USDT", "USDC", "DAI"]
                },
                "bitcoin": {
                    "category": "spot",
                    "paymentCurrencyList": ["BTC"]
                },
                "alt": {
                    "category": "spot",
                    "paymentCurrencyList": ["BRZ"]
                },
                "fiat": {
                    "category": "spot",
                    "paymentCurrencyList": ["EUR"]
                }
            },
            "futures": {
                "stableMargin" : {
                    "category": "linear",
                    "paymentCurrencyList": ["USDT", "USDC"]
                },
                "coinMargin": {
                    "category": "inverse",
                    "paymentCurrencyList": ["USD"]
                }
            }
        },
        "Bitget": {
            "isActive": {
                "spot": true,
                "futures": true
            },
            "information": {
                "image": "exchangeBitget.png",
                "keyList": [],
                "baseUrlList": [
                    "https://api.bitget.com"
                ],
                "publicApi": {
                    "ticker": "/api/spot/v1/market/tickers",
                    "orderbook": "",
                    "ticker_futures": "/api/mix/v1/market/tickers",
                    "orderbook_futures": ""
                }
            },
            "spot": {
                "stable": {
                    "category": "spot",
                    "paymentCurrencyList": ["USDT", "USDC"]
                },
                "bitcoin": {
                    "category": "spot",
                    "paymentCurrencyList": ["BTC"]
                },
                "alt": {
                    "category": "spot",
                    "paymentCurrencyList": ["ETH"]
                },
                "fiat": {
                    "category": "spot",
                    "paymentCurrencyList": ["EUR", "RUB", "UAH", "BRL", "GBP"]
                }
            },
            "futures": {
                "stableMargin" : {
                    "category": "usd(s)-m",
                    "paymentCurrencyList": ["USDT", "USDC"],
                    "description": "umcbl = USDT perpetual contract, cmcbl = USDC perpetual contract"
                },
                "coinMargin": {
                    "category": "coin-m",
                    "paymentCurrencyList": ["USD"],
                    "description": "dmcbl = Universal margin perpetual contract"
                }
            }
        },
        "Okx": {
            "isActive": {
                "spot": true,
                "futures": false
            },
            "information": {
                "image": "exchangeOkx.jpeg",
                "keyList": [],
                "baseUrlList": [
                    "https://www.okx.com",
                    "https://aws.okx.com"
                ],
                "publicApi": {
                    "ticker": "/api/v5/market/tickers",
                    "orderbook": ""
                }
            },
            "spot": {
                "stable": {
                    "category": "spot",
                    "paymentCurrencyList": ["USDT", "USDC", "DAI"]
                },
                "bitcoin": {
                    "category": "spot",
                    "paymentCurrencyList": ["BTC"]
                },
                "alt": {
                    "category": "spot",
                    "paymentCurrencyList": ["ETH", "OKB", "DOT", "EURT"]
                },
                "fiat": {
                    "category": "spot",
                    "paymentCurrencyList": ["EUR", "USD", "GBP"]
                }
            },
            "futures": {
                "stableMargin" : {
                    "category": "SWAP",
                    "paymentCurrencyList": ["USDT", "USDC"]
                },
                "coinMargin": {
                    "category": "SWAP",
                    "paymentCurrencyList": ["USD"]
                }
            }
        },
        "Kraken": {
            "isActive": {
                "spot": false,
                "futures": false
            },
            "information": {
                "image": "exchangeKraken.png",
                "keyList": [],
                "baseUrlList": [
                    "https://api.kraken.com"
                ],
                "publicApi": {
                    "ticker": "/0/public/Ticker",
                    "orderbook": ""
                }
            },
            "spot": {
                "stable": {
                    "category": "spot",
                    "paymentCurrencyList": ["USDT", "USDC"]
                },
                "bitcoin": {
                    "category": "spot",
                    "paymentCurrencyList": ["BTC"]
                },
                "alt": {
                    "category": "spot",
                    "paymentCurrencyList": ["ETH"]
                },
                "fiat": {
                    "category": "spot",
                    "paymentCurrencyList": ["EUR", "USD", "GBP"]
                }
            },
            "futures": {
                "stableMargin" : {
                    "category": "futures",
                    "paymentCurrencyList": []
                },
                "coinMargin": {
                    "category": "futures",
                    "paymentCurrencyList": []
                }
            }
        },
        "Bithumb": {
            "isActive": {
                "spot": false,
                "futures": false
            },
            "information": {
                "keyList": [],
                "baseUrlList": [
                    "https://api.bithumb.com"
                ],
                "publicApi": {
                    "ticker": "/public/ticker/ALL_",
                    "orderbook": "/public/orderbook/ALL_"
                }
            },
            "spot": {
                "bitcoin": {
                    "category": "spot",
                    "paymentCurrencyList": ["BTC"]
                },
                "fiat": {
                    "category": "spot",
                    "paymentCurrencyList": ["KRW"]
                }
            },
            "futures": {}
        }
    },
    "camsApi": {
        "http": "142.115.212.201",
        "https": "142.115.212.201",
        "port": "80"
    }
}

 

 

 

 

 

4. default.json 파일 읽는 기능 개선

 

// vim DefaultLoader.h

@interface DefaultLoader: NSObject

@property (readwrite, strong, nonatomic) NSArray *popularList;
@property (readwrite, strong, nonatomic) NSArray *popularList_BUSD;
@property (readwrite, strong, nonatomic) NSArray *popularList_USDs;
@property (readwrite, strong, nonatomic) NSArray *popularList_fiatPagging;
@property (readwrite, strong, nonatomic) NSArray *popularList_fiat;

@property (readwrite, strong, nonatomic) NSArray *fiatList;
@property (readwrite, strong, nonatomic) NSDictionary *ratesApi;

@property (readwrite, strong, nonatomic) NSDictionary *exchangeInfo;
@property (readwrite, strong, nonatomic) NSDictionary *allExchangeInfo;
@property (readwrite, strong, nonatomic) NSArray *exchangeList;

@property (readwrite, strong, nonatomic) NSArray *spotExchangeList;
@property (readwrite, strong, nonatomic) NSArray *exchangeListFutures;

+(instancetype) sharedInstance;
-(void)fetchActiveExchangeInfo;

@end

 

// vim DefaultLoader.m

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

@implementation DefaultLoader

+(instancetype)sharedInstance {
    static DefaultLoader *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[DefaultLoader alloc] init];
        // 설정 파일을 로드하는 코드를 작성하십시오.
        CRTJSONReader *defaultData = [CRTJSONReader sharedInstance];
        [defaultData tryReadJsonFile:@"default.json"];
        // 환율
        sharedInstance.popularList = [defaultData.loadData objectForKey:@"popularList"];
        sharedInstance.popularList_BUSD = [defaultData.loadData objectForKey:@"popularList_BUSD"];
        sharedInstance.popularList_USDs = [defaultData.loadData objectForKey:@"popularList_USDs"];
        sharedInstance.popularList_fiat = [defaultData.loadData objectForKey:@"popularList_Fiat"];
        sharedInstance.popularList_fiatPagging = [defaultData.loadData objectForKey:@"popularList_FiatPagging"];
        NSDictionary *ratesInfo = [defaultData.loadData objectForKey:@"ratesInfo"];
        sharedInstance.ratesApi = [ratesInfo objectForKey:@"apiInfo"];
        sharedInstance.fiatList = [ratesInfo objectForKey:@"fiatList"];
        // 거래소
        sharedInstance.allExchangeInfo = [defaultData.loadData objectForKey:@"exchangeInfo"];
        [sharedInstance fetchActiveExchangeInfo];
    });
    return sharedInstance;
}

-(void)fetchActiveExchangeInfo {
    // 모든 exchange 정보를 저장한 후, active가 아닌 데이터 삭제하는 기능 구현
    NSArray *exchangeListAll = [_allExchangeInfo allKeys];
    NSMutableArray *spotExchangeList = [exchangeListAll mutableCopy];
    NSMutableArray *exchangeListFutures = [exchangeListAll mutableCopy];
    NSMutableDictionary *exchangeInfoTmp = [_allExchangeInfo mutableCopy];
    for (int i=0; i<exchangeListAll.count; i++) {
        if ( ! [_allExchangeInfo[exchangeListAll[i]][@"isActive"][@"spot"] boolValue] && ! [_allExchangeInfo[exchangeListAll[i]][@"isActive"][@"futures"] boolValue]) {
            // spot, futures 모두 비활성화인 경우 -> 해당 거래소 데이터 자체를 삭제
            [exchangeInfoTmp removeObjectForKey:exchangeListAll[i]];
            [spotExchangeList removeObject:exchangeListAll[i]];
            [exchangeListFutures removeObject:exchangeListAll[i]];
        } else if (! [_allExchangeInfo[exchangeListAll[i]][@"isActive"][@"spot"] boolValue]) {
            // spot만 비활성화인 경우 -> 해당 거래소의 spot 데이터만 삭제
            [exchangeInfoTmp[exchangeListAll[i]] removeObjectForKey:@"spot"];
            [spotExchangeList removeObject:exchangeListAll[i]];
        } else if ( ! [_allExchangeInfo[exchangeListAll[i]][@"isActive"][@"futures"] boolValue]) {
            // futures만 비활성화인 경우 -> 해당 거래소의 futures 데이터만 삭제
            [exchangeInfoTmp[exchangeListAll[i]] removeObjectForKey:@"futures"];
            [exchangeListFutures removeObject:exchangeListAll[i]];
        }
    }
    // exchangeTmp 세팅 완료시, exchangeInfo에 NSDictionary로 저장
    _exchangeInfo= [exchangeInfoTmp mutableCopy];
    _spotExchangeList = [spotExchangeList mutableCopy];
    _exchangeListFutures = [exchangeListFutures mutableCopy];
}

@end

 

 

 

 

 

5. 하단 메뉴 Buttons 뷰

 

// vim MyTabBarController.h

#import <UIKit/UIKit.h>

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

@end

 

// vim MyTabBarController.m

#import <Foundation/Foundation.h>

#import "MyTabBarController.h"
// 각 메뉴탭의 첫 화면 View
#import "PopluarAssetListVC.h"
#import "PopluarAssetUSDs_ListVC.h"
#import "PopluarAssetBUSD_ListVC.h"
#import "PopluarAssetFiatPagging_ListVC.h"
#import "PopluarAssetFiat_ListVC.h"
#import "GlobalRatesVC.h"

@implementation MyTabBarController

- (void)viewDidLoad {
    [super viewDidLoad];

    // FirstViewController를 생성하고 속성을 설정합니다.
    PopluarAssetListVC *firstTabMainVC = [[PopluarAssetListVC alloc] init];
    UINavigationController *naviVC1 = [[UINavigationController alloc] initWithRootViewController:firstTabMainVC];
    naviVC1.tabBarItem.title = @"Tether";
    naviVC1.tabBarItem.image = [UIImage systemImageNamed:@"bitcoinsign"];
    //naviVC1.tabBarItem.image = [UIImage systemImageNamed:@"bitcoinsign"];
    
    // SecondViewController를 생성하고 속성을 설정합니다.
    PopluarAssetUSDs_ListVC *secondTabMainVC = [[PopluarAssetUSDs_ListVC alloc] init];
    UINavigationController *naviVC2 = [[UINavigationController alloc] initWithRootViewController:secondTabMainVC];
    naviVC2.tabBarItem.title = @"USD(s)";
    naviVC2.tabBarItem.image = [UIImage systemImageNamed:@"banknote"];
    
    // ThirdViewController를 생성하고 속성을 설정합니다.
    PopluarAssetBUSD_ListVC *thirdTabMainVC = [[PopluarAssetBUSD_ListVC alloc] init];
    UINavigationController *naviVC3 = [[UINavigationController alloc] initWithRootViewController:thirdTabMainVC];
    naviVC3.tabBarItem.title = @"BUSD";
    naviVC3.tabBarItem.image = [UIImage systemImageNamed:@"building.2"];
    
    // FourthViewController를 생성하고 속성을 설정합니다.
    PopluarAssetFiat_ListVC *fourthTabMainVC = [[PopluarAssetFiat_ListVC alloc] init];
    UINavigationController *naviVC4 = [[UINavigationController alloc] initWithRootViewController:fourthTabMainVC];
    naviVC4.tabBarItem.title = @"Fiat";
    naviVC4.tabBarItem.image = [UIImage systemImageNamed:@"building.columns"];
    
    // 5번째 탭 글로벌 환율
    GlobalRatesVC *sixthTabMainVC = [[GlobalRatesVC alloc] init];
    UINavigationController *naviVC5 = [[UINavigationController alloc] initWithRootViewController:sixthTabMainVC];
    naviVC5.tabBarItem.title = @"Rates";
    naviVC5.tabBarItem.image = [UIImage systemImageNamed:@"globe.asia.australia"];

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

@end

 

 

 

 

 

6. 1번째 메뉴 탭 화면

 

// 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 "CustomizeSize.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 = DEFAULT_FONT_SIZE;
    //카드뷰 배치에 필요한 변수를 설정합니다.
    // 카드 목록 나열될 공간 세팅
    // ****************************** //
    // 카드 자체에 대한 세팅
    // 카드 높이 길이 (상하 길이) 설정
    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 = DEFAULT_IMAGE_SIZE;
    
    // ****** 글로벌 지구 이모티콘 및 시간 설정 ****** //
    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 / 14*5, 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 / 80 * 13, 0, cardViewWidth/80*22, cardViewHeight/2)];
        cryptoPriceHighLabel.textAlignment = NSTextAlignmentRight;
        cryptoPriceHighLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        
        // 최근 24시간동안의 가격 변동률 정보 제공을 위한 기본 셋
        UILabel *changePricePercent24HighLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 80 * 37, 0, cardViewWidth/6, cardViewHeight/2)];
        changePricePercent24HighLabel.textAlignment = NSTextAlignmentRight;
        changePricePercent24HighLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        
        // 거래소 이름 및 로고 레이블을 생성하고 카드뷰에 추가합니다.
        // 거래소 아이콘을 위한 기본 셋
        UIImageView *exchangeLogoHighImage = [[UIImageView alloc] initWithFrame:CGRectMake(cardViewWidth / 80 * 35, 0, miniimage, miniimage)];
        exchangeLogoHighImage.contentMode = UIViewContentModeScaleAspectFit; // 해당 옵션을 사용하여 가로세로 비율 유지 크기입니다.
        
        /* #################### Low spot 현재 가격, 거래소 정보 노출 라벨 세팅 #################### */
        // 암호화폐 가격 레이블을 생성하고 카드뷰에 추가합니다.
        // 가격위한 기본 셋
        UILabel *cryptoPriceLowLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 80 * 13, cardViewHeight/2, cardViewWidth/80*22, cardViewHeight/2)];
        cryptoPriceLowLabel.textAlignment = NSTextAlignmentRight;
        cryptoPriceLowLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        
        // 최근 24시간동안의 가격 변동률 정보 제공을 위한 기본 셋
        UILabel *changePricePercent24LowLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 80 * 37, cardViewHeight/2, cardViewWidth/6, cardViewHeight/2)];
        changePricePercent24LowLabel.textAlignment = NSTextAlignmentRight;
        changePricePercent24LowLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        
        // 거래소 이름 및 로고 레이블을 생성하고 카드뷰에 추가합니다.
        // 거래소 아이콘을 위한 기본 셋
        UIImageView *exchangeLogoLowImage = [[UIImageView alloc] initWithFrame:CGRectMake(cardViewWidth / 80 * 35, 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 / 40 * 32, 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 / 80 * 57, 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 / 40 * 32, 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 / 80 * 57, 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 / 40 * 29, 0, cardViewWidth/12*2, cardViewHeight/2)];
        disparityRatioFirstRankLabel.textAlignment = NSTextAlignmentRight;
        disparityRatioFirstRankLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        
        // 프리미엄 수치 기본 셋 - last rank
        UILabel *disparityRatioLastRankLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 40 * 29, cardViewHeight/2, cardViewWidth/12*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].spotExchangeList;
    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:@" / "];
        symbol = [symbol 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:@"%"];
            
            // 특성 적용
            // 특정 값 이상 프리미엄 발생시, 텍스트에 배경색 입히기
            NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:changePricePercent24];
            if ([changePricePercent24 floatValue] <= -15) {
                // 큰 하락
                [attributedString addAttribute:NSBackgroundColorAttributeName
                                         value:[UIColor yellowColor]
                                         range:NSMakeRange(0, changePricePercent24.length)];
            } else if ([changePricePercent24 floatValue] <= -7) {
                // 적당 하락
                [attributedString addAttribute:NSBackgroundColorAttributeName
                                         value:[UIColor purpleColor]
                                         range:NSMakeRange(0, changePricePercent24.length)];
            } else if ([changePricePercent24 floatValue] >= 15) {
                // 큰 상승
                [attributedString addAttribute:NSBackgroundColorAttributeName
                                         value:[UIColor orangeColor]
                                         range:NSMakeRange(0, changePricePercent24.length)];
            } else if ([changePricePercent24 floatValue] >= 7) {
                // 적당 상승
                if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                    // ****** 시스템 테마 설정에 따라 색 다르게 적용 ****** //
                    // 다크모드인 경우
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor darkGrayColor]
                                             range:NSMakeRange(0, changePricePercent24.length)];
                } else {
                    // 라이트모드인 경우
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor lightGrayColor]
                                             range:NSMakeRange(0, changePricePercent24.length)];
                    
                }
            } else {
                // 일반 상태의 경우 clearColor로 지정해, 기존 색을 삭제
                [attributedString addAttribute:NSBackgroundColorAttributeName
                                         value:[UIColor clearColor]
                                         range:NSMakeRange(0, changePricePercent24.length)];
            }
            
            changePricePercent24HighLabel.attributedText = attributedString;
            
            // 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 objectForKey:@"price"]) {
            // 데이터가 정상적으로 있는 경우
            cryptoPriceLowLabel.text = [bottomRankPriceData objectForKey:@"price"];
            NSString *changePricePercent24;
            if (bottomRankPriceData[@"changePricePercent24"]) {
                changePricePercent24 = [bottomRankPriceData[@"changePricePercent24"] stringByAppendingString:@"%"];
                
                // 특성 적용
                // 특정 값 이상 프리미엄 발생시, 텍스트에 배경색 입히기
                NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:changePricePercent24];
                if ([changePricePercent24 floatValue] <= -15) {
                    // 큰 하락
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor yellowColor]
                                             range:NSMakeRange(0, changePricePercent24.length)];
                } else if ([changePricePercent24 floatValue] <= -7) {
                    // 적당 하락
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor purpleColor]
                                             range:NSMakeRange(0, changePricePercent24.length)];
                } else if ([changePricePercent24 floatValue] >= 15) {
                    // 큰 상승
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor orangeColor]
                                             range:NSMakeRange(0, changePricePercent24.length)];
                } else if ([changePricePercent24 floatValue] >= 7) {
                    // 적당 상승
                    if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                        // ****** 시스템 테마 설정에 따라 색 다르게 적용 ****** //
                        // 다크모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor darkGrayColor]
                                                 range:NSMakeRange(0, changePricePercent24.length)];
                    } else {
                        // 라이트모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor lightGrayColor]
                                                 range:NSMakeRange(0, changePricePercent24.length)];
                        
                    }
                } else {
                    // 일반 상태의 경우 clearColor로 지정해, 기존 색을 삭제
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor clearColor]
                                             range:NSMakeRange(0, changePricePercent24.length)];
                }
                
                changePricePercent24LowLabel.attributedText = attributedString;
                
                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] >= 3) {
            // 3 이상인 경우 빨간색
            [attributedString addAttribute:NSBackgroundColorAttributeName
                                     value:[UIColor redColor]
                                     range:NSMakeRange(0, maxPremiumPercent.length)];
        } else if ([maxPremiumPercent floatValue] >= 2) {
            // 2 이상인 경우 주황색
            [attributedString addAttribute:NSBackgroundColorAttributeName
                                     value:[UIColor orangeColor]
                                     range:NSMakeRange(0, maxPremiumPercent.length)];
        } else if ([maxPremiumPercent floatValue] >= 1) {
            // 1 이상인 경우 주황색
            [attributedString addAttribute:NSBackgroundColorAttributeName
                                     value:[UIColor purpleColor]
                                     range:NSMakeRange(0, maxPremiumPercent.length)];
        } else if ([maxPremiumPercent floatValue] >= 0.5) {
            // 0.5 이상인 경우 회색
            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"];
                            if ([asset isEqual:@"BTT"]) {
                                NSLog(@"futuresPrice : %@", futuresPrice);
                                NSLog(@"spotPrice : %@", spotPrice);
                            }
                            if (spotPrice) {
                                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],
                                    @"disparityExist": @true
                                };
                            } else {
                                NSString *disparityPrice = @" - ";
                                NSString *disparityRatio = @" - ";
                                disparityData[exchangeListFutures[i]] = @{
                                    @"disparityPrice": disparityPrice,
                                    @"disparityRatio": disparityRatio,
                                    @"disparityExist": @false
                                };
                            }
                            
                            
                        }
                    }
                }
            }
            
            // ****** 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 {
            // 거래소 정보가 있다면
            // ****** disparity ratio - last rank 가격 설정 ****** //
            lastRankDisparityRatioData = allTicker.recentFuturesTicker[disparityRatioLastRankExchange][asset][paymentCurrency];
            if (lastRankDisparityRatioData[@"price"]) {
                futuresLastExchangeImage.image = [UIImage imageNamed:[DefaultLoader sharedInstance].exchangeInfo[disparityRatioLastRankExchange][@"information"][@"image"]];
            }
        }
        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 ([disparityData[disparityRatioFirstRankExchange][@"disparityExist"] boolValue]) {
                    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];
                        }
                    }
                } else {
                    firstRankPremiumPercent = @" - ";
                }
                
                firstRankPremiumPercent = [firstRankPremiumPercent stringByAppendingString:@"%"];
                
                // 특성 적용
                // 특정 값 이상 프리미엄 발생시, 텍스트에 배경색 입히기
                NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:firstRankPremiumPercent];
                if ([firstRankPremiumPercent floatValue] >= 5 || [firstRankPremiumPercent floatValue] <= -5) {
                    // 5 이상인 경우 노란색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor yellowColor]
                                             range:NSMakeRange(0, firstRankPremiumPercent.length)];
                } else if ([firstRankPremiumPercent floatValue] >= 3 || [firstRankPremiumPercent floatValue] <= -3) {
                    // 3 이상인 경우 빨간색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor redColor]
                                             range:NSMakeRange(0, firstRankPremiumPercent.length)];
                } else if ([firstRankPremiumPercent floatValue] >= 2 || [firstRankPremiumPercent floatValue] <= -2) {
                    // 2 이상인 경우 주황색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor orangeColor]
                                             range:NSMakeRange(0, firstRankPremiumPercent.length)];
                } else if ([firstRankPremiumPercent floatValue] >= 1 || [firstRankPremiumPercent floatValue] <= -1) {
                    // 1 이상인 경우 주황색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor purpleColor]
                                             range:NSMakeRange(0, firstRankPremiumPercent.length)];
                } else if ([firstRankPremiumPercent floatValue] >= 0.5) {
                    // 0.5 이상인 경우 회색
                    if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                        // ****** 시스템 테마 설정에 따라 색 다르게 적용 ****** //
                        // 다크모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor darkGrayColor]
                                                 range:NSMakeRange(0, firstRankPremiumPercent.length)];
                    } else {
                        // 라이트모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor lightGrayColor]
                                                 range:NSMakeRange(0, firstRankPremiumPercent.length)];
                        
                    }
                } else {
                    // 일반 상태의 경우 clearColor로 지정해, 기존 색을 삭제
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor clearColor]
                                             range:NSMakeRange(0, firstRankPremiumPercent.length)];
                }
                disparityRatioFirstRankLabel.attributedText = attributedString;
            }
            // 데이터 유무 확인
            lastRankDisparityRatioData = allTicker.recentFuturesTicker[disparityRatioLastRankExchange][asset][paymentCurrency];
            if (lastRankDisparityRatioData[@"price"]) {
                // last rank
                NSString *lastRankPremiumPercent;
                // 거래소가 2개 이상 있는 경우이므로 둘 다 노출
                UILabel *disparityRatioLastRankLabel = _disparityRatioLastRankLabelList[i];
                // 괴리율 노출
                NSString *disparityRatio = [NSString stringWithFormat:@"%.2f", [disparityData[disparityRatioLastRankExchange][@"disparityRatio"] floatValue]];
                if ([disparityData[disparityRatioFirstRankExchange][@"disparityExist"] boolValue]) {
                    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];
                        }
                    }
                } else {
                    lastRankPremiumPercent = @" - ";
                }
                
                
                lastRankPremiumPercent = [lastRankPremiumPercent stringByAppendingString:@"%"];
                
                // 특성 적용
                // 특정 값 이상 프리미엄 발생시, 텍스트에 배경색 입히기
                NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:lastRankPremiumPercent];
                if ([lastRankPremiumPercent floatValue] >= 5 || [lastRankPremiumPercent floatValue] <= -5) {
                    // 5 이상인 경우 노란색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor yellowColor]
                                             range:NSMakeRange(0, lastRankPremiumPercent.length)];
                } else if ([lastRankPremiumPercent floatValue] >= 3 || [lastRankPremiumPercent floatValue] <= -3) {
                    // 3 이상인 경우 빨간색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor redColor]
                                             range:NSMakeRange(0, lastRankPremiumPercent.length)];
                } else if ([lastRankPremiumPercent floatValue] >= 2 || [lastRankPremiumPercent floatValue] <= -2) {
                    // 2 이상인 경우 주황색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor orangeColor]
                                             range:NSMakeRange(0, lastRankPremiumPercent.length)];
                } else if ([lastRankPremiumPercent floatValue] >= 1 || [lastRankPremiumPercent floatValue] <= -1) {
                    // 1 이상인 경우 주황색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor purpleColor]
                                             range:NSMakeRange(0, lastRankPremiumPercent.length)];
                } else if ([lastRankPremiumPercent floatValue] >= 0.5) {
                    // 0.5 이상인 경우 회색
                    if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                        // ****** 시스템 테마 설정에 따라 색 다르게 적용 ****** //
                        // 다크모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor darkGrayColor]
                                                 range:NSMakeRange(0, lastRankPremiumPercent.length)];
                    } else {
                        // 라이트모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor lightGrayColor]
                                                 range:NSMakeRange(0, lastRankPremiumPercent.length)];
                        
                    }
                } else {
                    // 일반 상태의 경우 clearColor로 지정해, 기존 색을 삭제
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor clearColor]
                                             range:NSMakeRange(0, lastRankPremiumPercent.length)];
                }
                disparityRatioLastRankLabel.attributedText = attributedString;
            }
        }
    }
    // **************************************** [End] 카드뷰 목록 쭉 만들기 **************************************** //
}
@end

 

 

 

 

 

7. 2번째 메뉴 화면

 

popularList에서 가져오는 데이터만 달라지고, 나머지 소스코드는 동일합니다.

 

// vim PopluarAssetUSDs_ListVC.h

#import <UIKit/UIKit.h>

// SecondViewController라는 이름의 뷰 컨트롤러 클래스를 선언합니다.
@interface PopluarAssetUSDs_ListVC : 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 PopluarAssetUSDs_ListVC.m

#import <Foundation/Foundation.h>
#import "PopluarAssetUSDs_ListVC.h"
// default.json 데이터 읽기
#import "DefaultLoader.h"
// 수시로 변경할 수 있는 설정값
#import "CustomizeSetting.h"
#import "CustomizeSize.h"
// 현재 시간 가져오는 용도
#import "DateTime.h"
// price 정보 저장
#import "AllPriceLiveData.h"
// 환율 서비스
#import "ServiceRecentRates.h"

@implementation PopluarAssetUSDs_ListVC {
    // 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 = DEFAULT_FONT_SIZE;
    //카드뷰 배치에 필요한 변수를 설정합니다.
    // 카드 목록 나열될 공간 세팅
    // ****************************** //
    // 카드 자체에 대한 세팅
    // 카드 높이 길이 (상하 길이) 설정
    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 = DEFAULT_IMAGE_SIZE;
    
    // ****** 글로벌 지구 이모티콘 및 시간 설정 ****** //
    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 / 14*5, 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 / 80 * 13, 0, cardViewWidth/80*22, cardViewHeight/2)];
        cryptoPriceHighLabel.textAlignment = NSTextAlignmentRight;
        cryptoPriceHighLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        
        // 최근 24시간동안의 가격 변동률 정보 제공을 위한 기본 셋
        UILabel *changePricePercent24HighLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 80 * 37, 0, cardViewWidth/6, cardViewHeight/2)];
        changePricePercent24HighLabel.textAlignment = NSTextAlignmentRight;
        changePricePercent24HighLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        
        // 거래소 이름 및 로고 레이블을 생성하고 카드뷰에 추가합니다.
        // 거래소 아이콘을 위한 기본 셋
        UIImageView *exchangeLogoHighImage = [[UIImageView alloc] initWithFrame:CGRectMake(cardViewWidth / 80 * 35, 0, miniimage, miniimage)];
        exchangeLogoHighImage.contentMode = UIViewContentModeScaleAspectFit; // 해당 옵션을 사용하여 가로세로 비율 유지 크기입니다.
        
        /* #################### Low spot 현재 가격, 거래소 정보 노출 라벨 세팅 #################### */
        // 암호화폐 가격 레이블을 생성하고 카드뷰에 추가합니다.
        // 가격위한 기본 셋
        UILabel *cryptoPriceLowLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 80 * 13, cardViewHeight/2, cardViewWidth/80*22, cardViewHeight/2)];
        cryptoPriceLowLabel.textAlignment = NSTextAlignmentRight;
        cryptoPriceLowLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        
        // 최근 24시간동안의 가격 변동률 정보 제공을 위한 기본 셋
        UILabel *changePricePercent24LowLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 80 * 37, cardViewHeight/2, cardViewWidth/6, cardViewHeight/2)];
        changePricePercent24LowLabel.textAlignment = NSTextAlignmentRight;
        changePricePercent24LowLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        
        // 거래소 이름 및 로고 레이블을 생성하고 카드뷰에 추가합니다.
        // 거래소 아이콘을 위한 기본 셋
        UIImageView *exchangeLogoLowImage = [[UIImageView alloc] initWithFrame:CGRectMake(cardViewWidth / 80 * 35, 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 / 40 * 32, 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 / 80 * 57, 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 / 40 * 32, 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 / 80 * 57, 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 / 40 * 29, 0, cardViewWidth/12*2, cardViewHeight/2)];
        disparityRatioFirstRankLabel.textAlignment = NSTextAlignmentRight;
        disparityRatioFirstRankLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        
        // 프리미엄 수치 기본 셋 - last rank
        UILabel *disparityRatioLastRankLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 40 * 29, cardViewHeight/2, cardViewWidth/12*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].spotExchangeList;
    exchangeListFutures = [DefaultLoader sharedInstance].exchangeListFutures;
}

/* #################### default.json 파일의 popularList 데이터 읽기 #################### */
-(void) loadPopularList {
    // ****** default.json의 popularList 불러오고 정상인지 1차 확인하기 ****** //
    // popularList에 있는 각 element들의 개수가 같은지 확인
    // popularList에 있는 데이터들 중, isAvailable이 true인 애들만 추출하기
    NSArray *popularList_raw = [[DefaultLoader sharedInstance].popularList_USDs 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:@" / "];
        symbol = [symbol 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:@"%"];
            
            // 특성 적용
            // 특정 값 이상 프리미엄 발생시, 텍스트에 배경색 입히기
            NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:changePricePercent24];
            if ([changePricePercent24 floatValue] <= -15) {
                // 큰 하락
                [attributedString addAttribute:NSBackgroundColorAttributeName
                                         value:[UIColor yellowColor]
                                         range:NSMakeRange(0, changePricePercent24.length)];
            } else if ([changePricePercent24 floatValue] <= -7) {
                // 적당 하락
                [attributedString addAttribute:NSBackgroundColorAttributeName
                                         value:[UIColor purpleColor]
                                         range:NSMakeRange(0, changePricePercent24.length)];
            } else if ([changePricePercent24 floatValue] >= 15) {
                // 큰 상승
                [attributedString addAttribute:NSBackgroundColorAttributeName
                                         value:[UIColor orangeColor]
                                         range:NSMakeRange(0, changePricePercent24.length)];
            } else if ([changePricePercent24 floatValue] >= 7) {
                // 적당 상승
                if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                    // ****** 시스템 테마 설정에 따라 색 다르게 적용 ****** //
                    // 다크모드인 경우
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor darkGrayColor]
                                             range:NSMakeRange(0, changePricePercent24.length)];
                } else {
                    // 라이트모드인 경우
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor lightGrayColor]
                                             range:NSMakeRange(0, changePricePercent24.length)];
                    
                }
            } else {
                // 일반 상태의 경우 clearColor로 지정해, 기존 색을 삭제
                [attributedString addAttribute:NSBackgroundColorAttributeName
                                         value:[UIColor clearColor]
                                         range:NSMakeRange(0, changePricePercent24.length)];
            }
            
            changePricePercent24HighLabel.attributedText = attributedString;
            
            // 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 objectForKey:@"price"]) {
            // 데이터가 정상적으로 있는 경우
            cryptoPriceLowLabel.text = [bottomRankPriceData objectForKey:@"price"];
            NSString *changePricePercent24;
            if (bottomRankPriceData[@"changePricePercent24"]) {
                changePricePercent24 = [bottomRankPriceData[@"changePricePercent24"] stringByAppendingString:@"%"];
                
                // 특성 적용
                // 특정 값 이상 프리미엄 발생시, 텍스트에 배경색 입히기
                NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:changePricePercent24];
                if ([changePricePercent24 floatValue] <= -15) {
                    // 큰 하락
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor yellowColor]
                                             range:NSMakeRange(0, changePricePercent24.length)];
                } else if ([changePricePercent24 floatValue] <= -7) {
                    // 적당 하락
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor purpleColor]
                                             range:NSMakeRange(0, changePricePercent24.length)];
                } else if ([changePricePercent24 floatValue] >= 15) {
                    // 큰 상승
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor orangeColor]
                                             range:NSMakeRange(0, changePricePercent24.length)];
                } else if ([changePricePercent24 floatValue] >= 7) {
                    // 적당 상승
                    if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                        // ****** 시스템 테마 설정에 따라 색 다르게 적용 ****** //
                        // 다크모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor darkGrayColor]
                                                 range:NSMakeRange(0, changePricePercent24.length)];
                    } else {
                        // 라이트모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor lightGrayColor]
                                                 range:NSMakeRange(0, changePricePercent24.length)];
                        
                    }
                } else {
                    // 일반 상태의 경우 clearColor로 지정해, 기존 색을 삭제
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor clearColor]
                                             range:NSMakeRange(0, changePricePercent24.length)];
                }
                
                changePricePercent24LowLabel.attributedText = attributedString;
                
                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] >= 3) {
            // 3 이상인 경우 빨간색
            [attributedString addAttribute:NSBackgroundColorAttributeName
                                     value:[UIColor redColor]
                                     range:NSMakeRange(0, maxPremiumPercent.length)];
        } else if ([maxPremiumPercent floatValue] >= 2) {
            // 2 이상인 경우 주황색
            [attributedString addAttribute:NSBackgroundColorAttributeName
                                     value:[UIColor orangeColor]
                                     range:NSMakeRange(0, maxPremiumPercent.length)];
        } else if ([maxPremiumPercent floatValue] >= 1) {
            // 1 이상인 경우 주황색
            [attributedString addAttribute:NSBackgroundColorAttributeName
                                     value:[UIColor purpleColor]
                                     range:NSMakeRange(0, maxPremiumPercent.length)];
        } else if ([maxPremiumPercent floatValue] >= 0.5) {
            // 0.5 이상인 경우 회색
            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"];
                            if ([asset isEqual:@"BTT"]) {
                                NSLog(@"futuresPrice : %@", futuresPrice);
                                NSLog(@"spotPrice : %@", spotPrice);
                            }
                            if (spotPrice) {
                                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],
                                    @"disparityExist": @true
                                };
                            } else {
                                NSString *disparityPrice = @" - ";
                                NSString *disparityRatio = @" - ";
                                disparityData[exchangeListFutures[i]] = @{
                                    @"disparityPrice": disparityPrice,
                                    @"disparityRatio": disparityRatio,
                                    @"disparityExist": @false
                                };
                            }
                            
                            
                        }
                    }
                }
            }
            
            // ****** 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 {
            // 거래소 정보가 있다면
            // ****** disparity ratio - last rank 가격 설정 ****** //
            lastRankDisparityRatioData = allTicker.recentFuturesTicker[disparityRatioLastRankExchange][asset][paymentCurrency];
            if (lastRankDisparityRatioData[@"price"]) {
                futuresLastExchangeImage.image = [UIImage imageNamed:[DefaultLoader sharedInstance].exchangeInfo[disparityRatioLastRankExchange][@"information"][@"image"]];
            }
        }
        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 ([disparityData[disparityRatioFirstRankExchange][@"disparityExist"] boolValue]) {
                    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];
                        }
                    }
                } else {
                    firstRankPremiumPercent = @" - ";
                }
                
                firstRankPremiumPercent = [firstRankPremiumPercent stringByAppendingString:@"%"];
                
                // 특성 적용
                // 특정 값 이상 프리미엄 발생시, 텍스트에 배경색 입히기
                NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:firstRankPremiumPercent];
                if ([firstRankPremiumPercent floatValue] >= 5 || [firstRankPremiumPercent floatValue] <= -5) {
                    // 5 이상인 경우 노란색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor yellowColor]
                                             range:NSMakeRange(0, firstRankPremiumPercent.length)];
                } else if ([firstRankPremiumPercent floatValue] >= 3 || [firstRankPremiumPercent floatValue] <= -3) {
                    // 3 이상인 경우 빨간색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor redColor]
                                             range:NSMakeRange(0, firstRankPremiumPercent.length)];
                } else if ([firstRankPremiumPercent floatValue] >= 2 || [firstRankPremiumPercent floatValue] <= -2) {
                    // 2 이상인 경우 주황색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor orangeColor]
                                             range:NSMakeRange(0, firstRankPremiumPercent.length)];
                } else if ([firstRankPremiumPercent floatValue] >= 1 || [firstRankPremiumPercent floatValue] <= -1) {
                    // 1 이상인 경우 주황색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor purpleColor]
                                             range:NSMakeRange(0, firstRankPremiumPercent.length)];
                } else if ([firstRankPremiumPercent floatValue] >= 0.5) {
                    // 0.5 이상인 경우 회색
                    if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                        // ****** 시스템 테마 설정에 따라 색 다르게 적용 ****** //
                        // 다크모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor darkGrayColor]
                                                 range:NSMakeRange(0, firstRankPremiumPercent.length)];
                    } else {
                        // 라이트모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor lightGrayColor]
                                                 range:NSMakeRange(0, firstRankPremiumPercent.length)];
                        
                    }
                } else {
                    // 일반 상태의 경우 clearColor로 지정해, 기존 색을 삭제
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor clearColor]
                                             range:NSMakeRange(0, firstRankPremiumPercent.length)];
                }
                disparityRatioFirstRankLabel.attributedText = attributedString;
            }
            // 데이터 유무 확인
            lastRankDisparityRatioData = allTicker.recentFuturesTicker[disparityRatioLastRankExchange][asset][paymentCurrency];
            if (lastRankDisparityRatioData[@"price"]) {
                // last rank
                NSString *lastRankPremiumPercent;
                // 거래소가 2개 이상 있는 경우이므로 둘 다 노출
                UILabel *disparityRatioLastRankLabel = _disparityRatioLastRankLabelList[i];
                // 괴리율 노출
                NSString *disparityRatio = [NSString stringWithFormat:@"%.2f", [disparityData[disparityRatioLastRankExchange][@"disparityRatio"] floatValue]];
                if ([disparityData[disparityRatioFirstRankExchange][@"disparityExist"] boolValue]) {
                    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];
                        }
                    }
                } else {
                    lastRankPremiumPercent = @" - ";
                }
                
                
                lastRankPremiumPercent = [lastRankPremiumPercent stringByAppendingString:@"%"];
                
                // 특성 적용
                // 특정 값 이상 프리미엄 발생시, 텍스트에 배경색 입히기
                NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:lastRankPremiumPercent];
                if ([lastRankPremiumPercent floatValue] >= 5 || [lastRankPremiumPercent floatValue] <= -5) {
                    // 5 이상인 경우 노란색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor yellowColor]
                                             range:NSMakeRange(0, lastRankPremiumPercent.length)];
                } else if ([lastRankPremiumPercent floatValue] >= 3 || [lastRankPremiumPercent floatValue] <= -3) {
                    // 3 이상인 경우 빨간색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor redColor]
                                             range:NSMakeRange(0, lastRankPremiumPercent.length)];
                } else if ([lastRankPremiumPercent floatValue] >= 2 || [lastRankPremiumPercent floatValue] <= -2) {
                    // 2 이상인 경우 주황색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor orangeColor]
                                             range:NSMakeRange(0, lastRankPremiumPercent.length)];
                } else if ([lastRankPremiumPercent floatValue] >= 1 || [lastRankPremiumPercent floatValue] <= -1) {
                    // 1 이상인 경우 주황색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor purpleColor]
                                             range:NSMakeRange(0, lastRankPremiumPercent.length)];
                } else if ([lastRankPremiumPercent floatValue] >= 0.5) {
                    // 0.5 이상인 경우 회색
                    if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                        // ****** 시스템 테마 설정에 따라 색 다르게 적용 ****** //
                        // 다크모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor darkGrayColor]
                                                 range:NSMakeRange(0, lastRankPremiumPercent.length)];
                    } else {
                        // 라이트모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor lightGrayColor]
                                                 range:NSMakeRange(0, lastRankPremiumPercent.length)];
                        
                    }
                } else {
                    // 일반 상태의 경우 clearColor로 지정해, 기존 색을 삭제
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor clearColor]
                                             range:NSMakeRange(0, lastRankPremiumPercent.length)];
                }
                disparityRatioLastRankLabel.attributedText = attributedString;
            }
        }
    }
    // **************************************** [End] 카드뷰 목록 쭉 만들기 **************************************** //
}
@end

 

 

 

 

 

8. 5번째 메뉴 탭 환율 정보 뷰

 

// vim GlobalRatesVC.h

#import <UIKit/UIKit.h>

// SecondViewController라는 이름의 뷰 컨트롤러 클래스를 선언합니다.
@interface GlobalRatesVC : 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 *currencyCountryLabelList; // Label - 국가 오른쪽에 currency unit도 추가해 노출
@property (strong, nonatomic) NSMutableArray *currencyRatePriceLabelList; // Label
@property (strong, nonatomic) NSMutableArray *currencyRatePercentLabelList; // Label
@property (strong, nonatomic) NSMutableArray *currencyCodeLabelList; // Label

@property (strong, nonatomic) NSMutableArray *currencyKrwPriceLabelList; // Label
@property (strong, nonatomic) NSMutableArray *currencyKrwPercentLabelList; // Label

/* #################### 데이터 세팅 #################### */

// 카드 안의 정보들을 최신 데이터로 뷰 다시 그려주기
-(void) updateCardView;

// 해당 화면 전체 뷰 최신화하기
-(void) updateView;

-(void) loadGlobalRatesList;


@end

 

// vim GlobalRatesVC.m

#import <Foundation/Foundation.h>
#import "GlobalRatesVC.h"
// 수정 가능 기본 세팅값
#import "CustomizeSetting.h"
#import "CustomizeSize.h"
// 기본 정보 가져오기
#import "DefaultLoader.h"
// 시간 정보
#import "DateTime.h"
// x분 y초 전 정보 뽑아내기
#import "BeforeLiveTimeCalculator.h"
// 환율 정보
#import "ServiceRecentRates.h"


@implementation GlobalRatesVC {
    // json에서 가져온 paymentCurrencyList raw 데이터
    NSArray *paymentCurrencyList;
}


// **************************************** 기본 데이터 세팅하기 **************************************** //
/* #################### default.json 파일의 exchangeInfo 데이터 읽기 #################### */
-(void) loadGlobalRatesList {
    // ****** default.json의 exchangeInfo 불러오기 ****** //
    paymentCurrencyList = [DefaultLoader sharedInstance].fiatList;
}

- (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 loadGlobalRatesList];
    
    // 데이터를 표시할 레이블 생성
    // **************************************** [Start] 뷰 그리기 **************************************** //
    CGFloat defaultFontSize = DEFAULT_FONT_SIZE;
    //카드뷰 배치에 필요한 변수를 설정합니다.
    // 카드 목록 나열될 공간 세팅
    // ****************************** //
    // 카드 자체에 대한 세팅
    // 카드 높이 길이 (상하 길이) 설정
    CGFloat cardViewHeight = defaultFontSize;
    // 카드 좌우 길이 phone size 참조하여 자동 조정
    CGFloat cardViewWidth = [UIScreen mainScreen].bounds.size.width;
    // ****************************** //
    
    // **************************************** [Start] 최상단에 기타 정보 공간 **************************************** //
    /* #################### 현재 시간 정보 #################### */
    self.topInfoTab = [[UIView alloc] initWithFrame:CGRectMake(0,-cardViewHeight*2, cardViewWidth, cardViewHeight)];
    // 국기 너비 길이 (좌우 길이) 설정
    CGFloat miniimage = DEFAULT_IMAGE_SIZE;
    
    // ****** 글로벌 지구 이모티콘 및 시간 설정 ****** //
    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];
    self.currencyCountryLabelList = [@[] mutableCopy];
    self.currencyRatePriceLabelList = [@[] mutableCopy];
    self.currencyRatePercentLabelList = [@[] mutableCopy];
    self.currencyCodeLabelList = [@[] mutableCopy];
    self.currencyKrwPriceLabelList = [@[] mutableCopy];
    self.currencyKrwPercentLabelList = [@[] mutableCopy];
    
    for (int i=0; i<paymentCurrencyList.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 *currencyCountryLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, cardViewWidth / 4, cardViewHeight)];
        
        currencyCountryLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
//        systemFontOfSize:defaultFontSize
        // 생성한 currencyCountryLabel을 cardView의 서브뷰로 추가합니다. 이렇게 함으로써 레이블이 카드 뷰에 표시됩니다.
        [_currencyCountryLabelList addObject:currencyCountryLabel];
        [cardView addSubview:currencyCountryLabel];
        
        /* #################### 화폐별 usd rate 현재 가격, 변동률 정보 노출 라벨 세팅 #################### */
        // 환율 현재 가격
        UILabel *currencyRatePriceLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 5 * 1, 0, cardViewWidth / 5, cardViewHeight)];
        currencyRatePriceLabel.textAlignment = NSTextAlignmentRight;
        currencyRatePriceLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        [cardView addSubview:currencyRatePriceLabel];
        [_currencyRatePriceLabelList addObject:currencyRatePriceLabel];
        
        // 환율 변동률
        UILabel *currencyRatePercentLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 42 * 17, 0, cardViewWidth/3, cardViewHeight)];
        currencyRatePercentLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        [cardView addSubview:currencyRatePercentLabel];
        [_currencyRatePercentLabelList addObject:currencyRatePercentLabel];
        
        // 화폐 영어 code
        UILabel *currencyCodeLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 42 * 23, 0, cardViewWidth / 10, miniimage)];
        currencyCodeLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        [cardView addSubview:currencyCodeLabel];
        [_currencyCodeLabelList addObject:currencyCodeLabel];
        
        /* #################### 화폐별 krw rate 현재 가격, 변동률 정보 노출 라벨 세팅 #################### */
        // 환율 현재 가격
        UILabel *currencyKrwPriceLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 63 * 40, 0, cardViewWidth / 5, cardViewHeight)];
        currencyKrwPriceLabel.textAlignment = NSTextAlignmentRight;
        currencyKrwPriceLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        [cardView addSubview:currencyKrwPriceLabel];
        [_currencyKrwPriceLabelList addObject:currencyKrwPriceLabel];
        
        // 환율 변동률
        UILabel *currencyKrwPercentLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 63 * 53, 0, cardViewWidth/7, cardViewHeight)];
        currencyKrwPercentLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        [cardView addSubview:currencyKrwPercentLabel];
        [_currencyKrwPercentLabelList addObject:currencyKrwPercentLabel];
        
        // cardView를 self.scrollView에 추가합니다.
        [self.scrollView addSubview:cardView];
        // 레이블 세팅 완료된 cardView를 CardViewList에 넣기
        [self.cardViewList addObject: cardView];
    }
    
    // 상하 스크롤 최대치 자동 설정
    CGFloat contentHeight = paymentCurrencyList.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];
    });
}

// **************************************** 전체 화면 뷰 데이터 업데이트 **************************************** //
-(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 *ratesLive = [ServiceRecentRates sharedInstance];
    // 환율 노출
    if (ratesLive.usdkrw) {
        // 환율 변동률 가공 및 값에 따라 환율 정보 색 지정
        NSString *ratesChangeInfo = [ratesLive.changePercentUsdkrw stringByAppendingString:@"%)"];
        if ( [ratesLive.changePercentUsdkrw hasPrefix:@"-"]) {
            // 음수인 경우 이미 - 붙어있으니까 그냥 합치기
            ratesChangeInfo = [@" (" stringByAppendingString:ratesChangeInfo];
            // 음수인 경우 하락색
            self.ratesLabel.textColor = [UIColor redColor];
        } else {
            // 양수이거나 0인 경우  + 붙여서 합치기
            ratesChangeInfo = [@" (+" stringByAppendingString:ratesChangeInfo];
            if ([ratesLive.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 = ratesLive.usdkrw;
        self.ratesLabel.text = [ratesMainInfo stringByAppendingString:ratesChangeInfo];
        // ****** rates 정보 출처 provider 이미지 설정 ****** //
        if ([ratesLive.provider isEqual:@"하나은행"]) {
            self.ratesProviderImage.image = [UIImage imageNamed:[DefaultLoader sharedInstance].ratesApi[@"DunamuQuotation"][@"image"]];
        } else {
            self.ratesProviderImage.image = [UIImage imageNamed:[DefaultLoader sharedInstance].ratesApi[ratesLive.provider][@"image"]];
            if ( ! [ratesLive.provider isEqual:@"OpenExchangeRates"]) {
                // Error
                NSLog(@"[WARN] Rates Image Error!");
            }
        }
    } else {
        self.ratesLabel.text = @"-";
    }
    // 환율 최근 업데이트 일시가 어느 시간 전인지 노출
    BeforeLiveTimeCalculator *timeCounter = [BeforeLiveTimeCalculator sharedInstance];
    if (ratesLive.recentDateTime) {
        [timeCounter WhenUpdatedMinSec:ratesLive.recentDateTime];
        self.ratesUpdateDateTimeLabel.text = [NSString stringWithFormat:@"%@m %@s ago", timeCounter.min, timeCounter.sec];
    }
    
    /* #################### 환울 카드 데이터 = 카드뷰 기본 세팅 #################### */
    for (int i = 0; i<paymentCurrencyList.count; i++) {
        // ****** 국가명 + currency unit ****** //
        // country 데이터가 있는 경우
        UILabel *currencyCountryLabel = _currencyCountryLabelList[i];
        if (ratesLive.ratesData[@"country"][paymentCurrencyList[i]]) {
            // 국가명 옆에 currency unit 붙여주기! 단 1은 기본이니까 생략
            NSString *currencyUnit = [ratesLive.ratesData[@"currencyUnit"][paymentCurrencyList[i]] stringValue];
            if ([currencyUnit isEqual:@"1"]) {
                // 1은 기본이니 생략
                currencyCountryLabel.text = ratesLive.ratesData[@"country"][paymentCurrencyList[i]];
            } else {
                // 1이 아닌 경우에만 숫자 노출, 일반적으로 100 사용
                NSString *countryName = ratesLive.ratesData[@"country"][paymentCurrencyList[i]];
                NSString *unitInfo = [@" " stringByAppendingString:currencyUnit];
                currencyCountryLabel.text = [countryName stringByAppendingString:unitInfo];
            }
        } else {
            // country 데이터가 없는 경우
            currencyCountryLabel.text = @"";
        }
        // ****** 화폐별 1usd 기준 환율가격 ****** //
        UILabel *currencyRatePriceLabel = _currencyRatePriceLabelList[i];
        NSString *usdRate = ratesLive.ratesData[@"recentUsdRates"][paymentCurrencyList[i]];
        if (usdRate) {
            currencyRatePriceLabel.text = usdRate;
        } else {
            currencyRatePriceLabel.text = @"-";
        }
        // ****** 화폐별 1usd 기준 환율가격 변동률 ****** //
        UILabel *currencyRatePercentLabel = _currencyRatePercentLabelList[i];
        NSString *usdRateChangePercent = ratesLive.ratesData[@"recentUsdChangePercent"][paymentCurrencyList[i]];
        if (usdRateChangePercent) {
            // 값 존재 확인
            if ([usdRateChangePercent isEqual:@"0.00"]) {
                // 0.00이면 보합
                usdRateChangePercent = [@"+" stringByAppendingString: usdRateChangePercent];
                if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                    // 다크모드인 경우 글자색 흰색으로 원복
                    currencyRatePercentLabel.textColor = [UIColor whiteColor];
                    currencyRatePriceLabel.textColor = [UIColor whiteColor];
                } else {
                    // 라이트모드인 경우 글자색 흑색으로 원복
                    currencyRatePercentLabel.textColor = [UIColor blackColor];
                    currencyRatePriceLabel.textColor = [UIColor blackColor];
                }
            } else if ([usdRateChangePercent hasPrefix:@"-"]) {
                // 음이면 하락
                currencyRatePercentLabel.textColor = [UIColor redColor];
                currencyRatePriceLabel.textColor = [UIColor redColor];
            } else {
                // 양이면 상승
                usdRateChangePercent = [@"+" stringByAppendingString: usdRateChangePercent];
                currencyRatePercentLabel.textColor = [UIColor greenColor];
                currencyRatePriceLabel.textColor = [UIColor greenColor];
            }
            usdRateChangePercent = [usdRateChangePercent stringByAppendingString:@"%"];
            currencyRatePercentLabel.text = usdRateChangePercent;
        } else {
            // 값 없으면 빈 값으로 노출
            currencyRatePercentLabel.text = @" - %";
            if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                // 다크모드인 경우 글자색 흰색으로 원복
                currencyRatePercentLabel.textColor = [UIColor whiteColor];
            } else {
                // 라이트모드인 경우 글자색 흑색으로 원복
                currencyRatePercentLabel.textColor = [UIColor blackColor];
            }
        }
        // ****** 화폐 단위 code ****** //
        UILabel *currencyCodeLabel = _currencyCodeLabelList[i];
        currencyCodeLabel.text = paymentCurrencyList[i];
        
        // ****** 화폐별 krw 기준 1화폐당의 krw 환율 가격 ****** //
        UILabel *currencyKrwPriceLabel = _currencyKrwPriceLabelList[i];
        NSString *krwRate = ratesLive.ratesData[@"recentKrwRates"][paymentCurrencyList[i]];
        if (krwRate) {
            currencyKrwPriceLabel.text = krwRate;
        } else {
            currencyKrwPriceLabel.text = @"-";
        }
        // ****** 화폐별 krw 기준 1화폐당의 krw 환율 변동률 ****** //
        UILabel *currencyKrwPercentLabel = _currencyKrwPercentLabelList[i];
        NSString *krwRateChangePercent = ratesLive.ratesData[@"recentKrwChangePercent"][paymentCurrencyList[i]];
        if (krwRateChangePercent) {
            // 값 존재 확인
            if ([krwRateChangePercent isEqual:@"0.00"]) {
                // 0.00이면 보합
                krwRateChangePercent = [@"+" stringByAppendingString: krwRateChangePercent];
                if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                    // 다크모드인 경우 글자색 흰색으로 원복
                    currencyKrwPercentLabel.textColor = [UIColor whiteColor];
                    currencyKrwPriceLabel.textColor = [UIColor whiteColor];
                } else {
                    // 라이트모드인 경우 글자색 흑색으로 원복
                    currencyKrwPercentLabel.textColor = [UIColor blackColor];
                    currencyKrwPriceLabel.textColor = [UIColor blackColor];
                }
            } else if ([krwRateChangePercent hasPrefix:@"-"]) {
                // 음이면 하락
                currencyKrwPercentLabel.textColor = [UIColor redColor];
                currencyKrwPriceLabel.textColor = [UIColor redColor];
            } else {
                // 양이면 상승
                krwRateChangePercent = [@"+" stringByAppendingString: krwRateChangePercent];
                currencyKrwPercentLabel.textColor = [UIColor greenColor];
                currencyKrwPriceLabel.textColor = [UIColor greenColor];
            }
            krwRateChangePercent = [krwRateChangePercent stringByAppendingString:@"%"];
            currencyKrwPercentLabel.text = krwRateChangePercent;
        } else {
            // 값 없으면 빈 값으로 노출
            currencyKrwPercentLabel.text = @" - %";
            if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                // 다크모드인 경우 글자색 흰색으로 원복
                currencyKrwPercentLabel.textColor = [UIColor whiteColor];
            } else {
                // 라이트모드인 경우 글자색 흑색으로 원복
                currencyKrwPercentLabel.textColor = [UIColor blackColor];
            }
        }
        
    }
}


@end

 

 

 

 

 

9. 결과 예시 화면

 

 

 

 

 

 

10. 3, 4번째 메뉴탭 뷰 (참고)

 

3,4번째 메뉴탭 모두 1,2번째 메뉴탭과 유사한 구성입니다.

 

 - 3번째 메뉴탭 뷰

// vim Controller/PopluarAssetBUSD_ListVC.h

#import <UIKit/UIKit.h>

// SecondViewController라는 이름의 뷰 컨트롤러 클래스를 선언합니다.
@interface PopluarAssetBUSD_ListVC : 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 PopluarAssetBUSD_ListVC.m

#import <Foundation/Foundation.h>
#import "PopluarAssetBUSD_ListVC.h"
// default.json 데이터 읽기
#import "DefaultLoader.h"
// 수시로 변경할 수 있는 설정값
#import "CustomizeSetting.h"
#import "CustomizeSize.h"
// 현재 시간 가져오는 용도
#import "DateTime.h"
// price 정보 저장
#import "AllPriceLiveData.h"
// 환율 서비스
#import "ServiceRecentRates.h"

@implementation PopluarAssetBUSD_ListVC {
    // 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 = DEFAULT_FONT_SIZE;
    //카드뷰 배치에 필요한 변수를 설정합니다.
    // 카드 목록 나열될 공간 세팅
    // ****************************** //
    // 카드 자체에 대한 세팅
    // 카드 높이 길이 (상하 길이) 설정
    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 = DEFAULT_IMAGE_SIZE;
    
    // ****** 글로벌 지구 이모티콘 및 시간 설정 ****** //
    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 / 14*5, 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 / 80 * 13, 0, cardViewWidth/80*22, cardViewHeight/2)];
        cryptoPriceHighLabel.textAlignment = NSTextAlignmentRight;
        cryptoPriceHighLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        
        // 최근 24시간동안의 가격 변동률 정보 제공을 위한 기본 셋
        UILabel *changePricePercent24HighLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 80 * 37, 0, cardViewWidth/6, cardViewHeight/2)];
        changePricePercent24HighLabel.textAlignment = NSTextAlignmentRight;
        changePricePercent24HighLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        
        // 거래소 이름 및 로고 레이블을 생성하고 카드뷰에 추가합니다.
        // 거래소 아이콘을 위한 기본 셋
        UIImageView *exchangeLogoHighImage = [[UIImageView alloc] initWithFrame:CGRectMake(cardViewWidth / 80 * 35, 0, miniimage, miniimage)];
        exchangeLogoHighImage.contentMode = UIViewContentModeScaleAspectFit; // 해당 옵션을 사용하여 가로세로 비율 유지 크기입니다.
        
        /* #################### Low spot 현재 가격, 거래소 정보 노출 라벨 세팅 #################### */
        // 암호화폐 가격 레이블을 생성하고 카드뷰에 추가합니다.
        // 가격위한 기본 셋
        UILabel *cryptoPriceLowLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 80 * 13, cardViewHeight/2, cardViewWidth/80*22, cardViewHeight/2)];
        cryptoPriceLowLabel.textAlignment = NSTextAlignmentRight;
        cryptoPriceLowLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        
        // 최근 24시간동안의 가격 변동률 정보 제공을 위한 기본 셋
        UILabel *changePricePercent24LowLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 80 * 37, cardViewHeight/2, cardViewWidth/6, cardViewHeight/2)];
        changePricePercent24LowLabel.textAlignment = NSTextAlignmentRight;
        changePricePercent24LowLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        
        // 거래소 이름 및 로고 레이블을 생성하고 카드뷰에 추가합니다.
        // 거래소 아이콘을 위한 기본 셋
        UIImageView *exchangeLogoLowImage = [[UIImageView alloc] initWithFrame:CGRectMake(cardViewWidth / 80 * 35, 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 / 40 * 32, 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 / 80 * 57, 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 / 40 * 32, 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 / 80 * 57, 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 / 40 * 29, 0, cardViewWidth/12*2, cardViewHeight/2)];
        disparityRatioFirstRankLabel.textAlignment = NSTextAlignmentRight;
        disparityRatioFirstRankLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        
        // 프리미엄 수치 기본 셋 - last rank
        UILabel *disparityRatioLastRankLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 40 * 29, cardViewHeight/2, cardViewWidth/12*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].spotExchangeList;
    exchangeListFutures = [DefaultLoader sharedInstance].exchangeListFutures;
}

/* #################### default.json 파일의 popularList 데이터 읽기 #################### */
-(void) loadPopularList {
    // ****** default.json의 popularList 불러오고 정상인지 1차 확인하기 ****** //
    // popularList에 있는 각 element들의 개수가 같은지 확인
    // popularList에 있는 데이터들 중, isAvailable이 true인 애들만 추출하기
    NSArray *popularList_raw = [[DefaultLoader sharedInstance].popularList_BUSD 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:@" / "];
        symbol = [symbol 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 objectForKey:@"price"]) {
            // 데이터가 정상적으로 있는 경우
            cryptoPriceLowLabel.text = [bottomRankPriceData objectForKey:@"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] >= 3) {
            // 3 이상인 경우 빨간색
            [attributedString addAttribute:NSBackgroundColorAttributeName
                                     value:[UIColor redColor]
                                     range:NSMakeRange(0, maxPremiumPercent.length)];
        } else if ([maxPremiumPercent floatValue] >= 2) {
            // 2 이상인 경우 주황색
            [attributedString addAttribute:NSBackgroundColorAttributeName
                                     value:[UIColor orangeColor]
                                     range:NSMakeRange(0, maxPremiumPercent.length)];
        } else if ([maxPremiumPercent floatValue] >= 1) {
            // 1 이상인 경우 주황색
            [attributedString addAttribute:NSBackgroundColorAttributeName
                                     value:[UIColor purpleColor]
                                     range:NSMakeRange(0, maxPremiumPercent.length)];
        } else if ([maxPremiumPercent floatValue] >= 0.5) {
            // 0.5 이상인 경우 회색
            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 {
            // 거래소 정보가 있다면
            // ****** disparity ratio - last rank 가격 설정 ****** //
            lastRankDisparityRatioData = allTicker.recentFuturesTicker[disparityRatioLastRankExchange][asset][paymentCurrency];
            if (lastRankDisparityRatioData[@"price"]) {
                futuresLastExchangeImage.image = [UIImage imageNamed:[DefaultLoader sharedInstance].exchangeInfo[disparityRatioLastRankExchange][@"information"][@"image"]];
            }
        }
        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];
                    }
                }
                firstRankPremiumPercent = [firstRankPremiumPercent stringByAppendingString:@"%"];
                
                // 특성 적용
                // 특정 값 이상 프리미엄 발생시, 텍스트에 배경색 입히기
                NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:firstRankPremiumPercent];
                if ([firstRankPremiumPercent floatValue] >= 5 || [firstRankPremiumPercent floatValue] <= -5) {
                    // 5 이상인 경우 노란색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor yellowColor]
                                             range:NSMakeRange(0, firstRankPremiumPercent.length)];
                } else if ([firstRankPremiumPercent floatValue] >= 3 || [firstRankPremiumPercent floatValue] <= -3) {
                    // 3 이상인 경우 빨간색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor redColor]
                                             range:NSMakeRange(0, firstRankPremiumPercent.length)];
                } else if ([firstRankPremiumPercent floatValue] >= 2 || [firstRankPremiumPercent floatValue] <= -2) {
                    // 2 이상인 경우 주황색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor orangeColor]
                                             range:NSMakeRange(0, firstRankPremiumPercent.length)];
                } else if ([firstRankPremiumPercent floatValue] >= 1 || [firstRankPremiumPercent floatValue] <= -1) {
                    // 1 이상인 경우 주황색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor purpleColor]
                                             range:NSMakeRange(0, firstRankPremiumPercent.length)];
                } else if ([firstRankPremiumPercent floatValue] >= 0.5) {
                    // 0.5 이상인 경우 회색
                    if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                        // ****** 시스템 테마 설정에 따라 색 다르게 적용 ****** //
                        // 다크모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor darkGrayColor]
                                                 range:NSMakeRange(0, firstRankPremiumPercent.length)];
                    } else {
                        // 라이트모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor lightGrayColor]
                                                 range:NSMakeRange(0, firstRankPremiumPercent.length)];
                        
                    }
                } else {
                    // 일반 상태의 경우 clearColor로 지정해, 기존 색을 삭제
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor clearColor]
                                             range:NSMakeRange(0, firstRankPremiumPercent.length)];
                }
                disparityRatioFirstRankLabel.attributedText = attributedString;
            }
            // 데이터 유무 확인
            lastRankDisparityRatioData = allTicker.recentFuturesTicker[disparityRatioLastRankExchange][asset][paymentCurrency];
            if (lastRankDisparityRatioData[@"price"]) {
                // 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];
                    }
                }
                lastRankPremiumPercent = [lastRankPremiumPercent stringByAppendingString:@"%"];
                
                // 특성 적용
                // 특정 값 이상 프리미엄 발생시, 텍스트에 배경색 입히기
                NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:lastRankPremiumPercent];
                if ([lastRankPremiumPercent floatValue] >= 5 || [lastRankPremiumPercent floatValue] <= -5) {
                    // 5 이상인 경우 노란색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor yellowColor]
                                             range:NSMakeRange(0, lastRankPremiumPercent.length)];
                } else if ([lastRankPremiumPercent floatValue] >= 3 || [lastRankPremiumPercent floatValue] <= -3) {
                    // 3 이상인 경우 빨간색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor redColor]
                                             range:NSMakeRange(0, lastRankPremiumPercent.length)];
                } else if ([lastRankPremiumPercent floatValue] >= 2 || [lastRankPremiumPercent floatValue] <= -2) {
                    // 2 이상인 경우 주황색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor orangeColor]
                                             range:NSMakeRange(0, lastRankPremiumPercent.length)];
                } else if ([lastRankPremiumPercent floatValue] >= 1 || [lastRankPremiumPercent floatValue] <= -1) {
                    // 1 이상인 경우 주황색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor purpleColor]
                                             range:NSMakeRange(0, lastRankPremiumPercent.length)];
                } else if ([lastRankPremiumPercent floatValue] >= 0.5) {
                    // 0.5 이상인 경우 회색
                    if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                        // ****** 시스템 테마 설정에 따라 색 다르게 적용 ****** //
                        // 다크모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor darkGrayColor]
                                                 range:NSMakeRange(0, lastRankPremiumPercent.length)];
                    } else {
                        // 라이트모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor lightGrayColor]
                                                 range:NSMakeRange(0, lastRankPremiumPercent.length)];
                        
                    }
                } else {
                    // 일반 상태의 경우 clearColor로 지정해, 기존 색을 삭제
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor clearColor]
                                             range:NSMakeRange(0, lastRankPremiumPercent.length)];
                }
                disparityRatioLastRankLabel.attributedText = attributedString;
            }
        }
    }
    // **************************************** [End] 카드뷰 목록 쭉 만들기 **************************************** //
}
@end

 

 

 

 - 4번째 메뉴탭 뷰

 

// vim PopluarAssetFiat_ListVC.h

#import <UIKit/UIKit.h>

// SecondViewController라는 이름의 뷰 컨트롤러 클래스를 선언합니다.
@interface PopluarAssetFiat_ListVC : 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 PopluarAssetFiat_ListVC.m

#import <Foundation/Foundation.h>
#import "PopluarAssetFiat_ListVC.h"
// default.json 데이터 읽기
#import "DefaultLoader.h"
// 수시로 변경할 수 있는 설정값
#import "CustomizeSetting.h"
#import "CustomizeSize.h"
// 현재 시간 가져오는 용도
#import "DateTime.h"
// price 정보 저장
#import "AllPriceLiveData.h"
// 환율 서비스
#import "ServiceRecentRates.h"

@implementation PopluarAssetFiat_ListVC {
    // 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 = DEFAULT_FONT_SIZE;
    //카드뷰 배치에 필요한 변수를 설정합니다.
    // 카드 목록 나열될 공간 세팅
    // ****************************** //
    // 카드 자체에 대한 세팅
    // 카드 높이 길이 (상하 길이) 설정
    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 = DEFAULT_IMAGE_SIZE;
    
    // ****** 글로벌 지구 이모티콘 및 시간 설정 ****** //
    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 / 14*5, 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 / 80 * 13, 0, cardViewWidth/80*22, cardViewHeight/2)];
        cryptoPriceHighLabel.textAlignment = NSTextAlignmentRight;
        cryptoPriceHighLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        
        // 최근 24시간동안의 가격 변동률 정보 제공을 위한 기본 셋
        UILabel *changePricePercent24HighLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 80 * 37, 0, cardViewWidth/6, cardViewHeight/2)];
        changePricePercent24HighLabel.textAlignment = NSTextAlignmentRight;
        changePricePercent24HighLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        
        // 거래소 이름 및 로고 레이블을 생성하고 카드뷰에 추가합니다.
        // 거래소 아이콘을 위한 기본 셋
        UIImageView *exchangeLogoHighImage = [[UIImageView alloc] initWithFrame:CGRectMake(cardViewWidth / 80 * 35, 0, miniimage, miniimage)];
        exchangeLogoHighImage.contentMode = UIViewContentModeScaleAspectFit; // 해당 옵션을 사용하여 가로세로 비율 유지 크기입니다.
        
        /* #################### Low spot 현재 가격, 거래소 정보 노출 라벨 세팅 #################### */
        // 암호화폐 가격 레이블을 생성하고 카드뷰에 추가합니다.
        // 가격위한 기본 셋
        UILabel *cryptoPriceLowLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 80 * 13, cardViewHeight/2, cardViewWidth/80*22, cardViewHeight/2)];
        cryptoPriceLowLabel.textAlignment = NSTextAlignmentRight;
        cryptoPriceLowLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        
        // 최근 24시간동안의 가격 변동률 정보 제공을 위한 기본 셋
        UILabel *changePricePercent24LowLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 80 * 37, cardViewHeight/2, cardViewWidth/6, cardViewHeight/2)];
        changePricePercent24LowLabel.textAlignment = NSTextAlignmentRight;
        changePricePercent24LowLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        
        // 거래소 이름 및 로고 레이블을 생성하고 카드뷰에 추가합니다.
        // 거래소 아이콘을 위한 기본 셋
        UIImageView *exchangeLogoLowImage = [[UIImageView alloc] initWithFrame:CGRectMake(cardViewWidth / 80 * 35, 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 / 40 * 32, 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 / 80 * 57, 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 / 40 * 32, 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 / 80 * 57, 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 / 40 * 29, 0, cardViewWidth/12*2, cardViewHeight/2)];
        disparityRatioFirstRankLabel.textAlignment = NSTextAlignmentRight;
        disparityRatioFirstRankLabel.font = [UIFont fontWithName:@"Pretendard-Regular" size:defaultFontSize];
        
        // 프리미엄 수치 기본 셋 - last rank
        UILabel *disparityRatioLastRankLabel = [[UILabel alloc] initWithFrame:CGRectMake(cardViewWidth / 40 * 29, cardViewHeight/2, cardViewWidth/12*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].spotExchangeList;
    exchangeListFutures = [DefaultLoader sharedInstance].exchangeListFutures;
}

/* #################### default.json 파일의 popularList 데이터 읽기 #################### */
-(void) loadPopularList {
    // ****** default.json의 popularList 불러오고 정상인지 1차 확인하기 ****** //
    // popularList에 있는 각 element들의 개수가 같은지 확인
    // popularList에 있는 데이터들 중, isAvailable이 true인 애들만 추출하기
    NSArray *popularList_raw = [[DefaultLoader sharedInstance].popularList_fiat 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:@" / "];
        symbol = [symbol 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:@"%"];
            
            // 특성 적용
            // 특정 값 이상 프리미엄 발생시, 텍스트에 배경색 입히기
            NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:changePricePercent24];
            if ([changePricePercent24 floatValue] <= -15) {
                // 큰 하락
                [attributedString addAttribute:NSBackgroundColorAttributeName
                                         value:[UIColor yellowColor]
                                         range:NSMakeRange(0, changePricePercent24.length)];
            } else if ([changePricePercent24 floatValue] <= -7) {
                // 적당 하락
                [attributedString addAttribute:NSBackgroundColorAttributeName
                                         value:[UIColor purpleColor]
                                         range:NSMakeRange(0, changePricePercent24.length)];
            } else if ([changePricePercent24 floatValue] >= 15) {
                // 큰 상승
                [attributedString addAttribute:NSBackgroundColorAttributeName
                                         value:[UIColor orangeColor]
                                         range:NSMakeRange(0, changePricePercent24.length)];
            } else if ([changePricePercent24 floatValue] >= 7) {
                // 적당 상승
                if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                    // ****** 시스템 테마 설정에 따라 색 다르게 적용 ****** //
                    // 다크모드인 경우
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor darkGrayColor]
                                             range:NSMakeRange(0, changePricePercent24.length)];
                } else {
                    // 라이트모드인 경우
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor lightGrayColor]
                                             range:NSMakeRange(0, changePricePercent24.length)];
                    
                }
            } else {
                // 일반 상태의 경우 clearColor로 지정해, 기존 색을 삭제
                [attributedString addAttribute:NSBackgroundColorAttributeName
                                         value:[UIColor clearColor]
                                         range:NSMakeRange(0, changePricePercent24.length)];
            }
            
            changePricePercent24HighLabel.attributedText = attributedString;
            
            // 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 objectForKey:@"price"]) {
            // 데이터가 정상적으로 있는 경우
            cryptoPriceLowLabel.text = [bottomRankPriceData objectForKey:@"price"];
            NSString *changePricePercent24;
            if (bottomRankPriceData[@"changePricePercent24"]) {
                changePricePercent24 = [bottomRankPriceData[@"changePricePercent24"] stringByAppendingString:@"%"];
                
                // 특성 적용
                // 특정 값 이상 프리미엄 발생시, 텍스트에 배경색 입히기
                NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:changePricePercent24];
                if ([changePricePercent24 floatValue] <= -15) {
                    // 큰 하락
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor yellowColor]
                                             range:NSMakeRange(0, changePricePercent24.length)];
                } else if ([changePricePercent24 floatValue] <= -7) {
                    // 적당 하락
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor purpleColor]
                                             range:NSMakeRange(0, changePricePercent24.length)];
                } else if ([changePricePercent24 floatValue] >= 15) {
                    // 큰 상승
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor orangeColor]
                                             range:NSMakeRange(0, changePricePercent24.length)];
                } else if ([changePricePercent24 floatValue] >= 7) {
                    // 적당 상승
                    if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                        // ****** 시스템 테마 설정에 따라 색 다르게 적용 ****** //
                        // 다크모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor darkGrayColor]
                                                 range:NSMakeRange(0, changePricePercent24.length)];
                    } else {
                        // 라이트모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor lightGrayColor]
                                                 range:NSMakeRange(0, changePricePercent24.length)];
                        
                    }
                } else {
                    // 일반 상태의 경우 clearColor로 지정해, 기존 색을 삭제
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor clearColor]
                                             range:NSMakeRange(0, changePricePercent24.length)];
                }
                
                changePricePercent24LowLabel.attributedText = attributedString;
                
                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] >= 3) {
            // 3 이상인 경우 빨간색
            [attributedString addAttribute:NSBackgroundColorAttributeName
                                     value:[UIColor redColor]
                                     range:NSMakeRange(0, maxPremiumPercent.length)];
        } else if ([maxPremiumPercent floatValue] >= 2) {
            // 2 이상인 경우 주황색
            [attributedString addAttribute:NSBackgroundColorAttributeName
                                     value:[UIColor orangeColor]
                                     range:NSMakeRange(0, maxPremiumPercent.length)];
        } else if ([maxPremiumPercent floatValue] >= 1) {
            // 1 이상인 경우 주황색
            [attributedString addAttribute:NSBackgroundColorAttributeName
                                     value:[UIColor purpleColor]
                                     range:NSMakeRange(0, maxPremiumPercent.length)];
        } else if ([maxPremiumPercent floatValue] >= 0.5) {
            // 0.5 이상인 경우 회색
            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"];
                            if ([asset isEqual:@"BTT"]) {
                                NSLog(@"futuresPrice : %@", futuresPrice);
                                NSLog(@"spotPrice : %@", spotPrice);
                            }
                            if (spotPrice) {
                                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],
                                    @"disparityExist": @true
                                };
                            } else {
                                NSString *disparityPrice = @" - ";
                                NSString *disparityRatio = @" - ";
                                disparityData[exchangeListFutures[i]] = @{
                                    @"disparityPrice": disparityPrice,
                                    @"disparityRatio": disparityRatio,
                                    @"disparityExist": @false
                                };
                            }
                            
                            
                        }
                    }
                }
            }
            
            // ****** 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 {
            // 거래소 정보가 있다면
            // ****** disparity ratio - last rank 가격 설정 ****** //
            lastRankDisparityRatioData = allTicker.recentFuturesTicker[disparityRatioLastRankExchange][asset][paymentCurrency];
            if (lastRankDisparityRatioData[@"price"]) {
                futuresLastExchangeImage.image = [UIImage imageNamed:[DefaultLoader sharedInstance].exchangeInfo[disparityRatioLastRankExchange][@"information"][@"image"]];
            }
        }
        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 ([disparityData[disparityRatioFirstRankExchange][@"disparityExist"] boolValue]) {
                    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];
                        }
                    }
                } else {
                    firstRankPremiumPercent = @" - ";
                }
                
                firstRankPremiumPercent = [firstRankPremiumPercent stringByAppendingString:@"%"];
                
                // 특성 적용
                // 특정 값 이상 프리미엄 발생시, 텍스트에 배경색 입히기
                NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:firstRankPremiumPercent];
                if ([firstRankPremiumPercent floatValue] >= 5 || [firstRankPremiumPercent floatValue] <= -5) {
                    // 5 이상인 경우 노란색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor yellowColor]
                                             range:NSMakeRange(0, firstRankPremiumPercent.length)];
                } else if ([firstRankPremiumPercent floatValue] >= 3 || [firstRankPremiumPercent floatValue] <= -3) {
                    // 3 이상인 경우 빨간색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor redColor]
                                             range:NSMakeRange(0, firstRankPremiumPercent.length)];
                } else if ([firstRankPremiumPercent floatValue] >= 2 || [firstRankPremiumPercent floatValue] <= -2) {
                    // 2 이상인 경우 주황색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor orangeColor]
                                             range:NSMakeRange(0, firstRankPremiumPercent.length)];
                } else if ([firstRankPremiumPercent floatValue] >= 1 || [firstRankPremiumPercent floatValue] <= -1) {
                    // 1 이상인 경우 주황색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor purpleColor]
                                             range:NSMakeRange(0, firstRankPremiumPercent.length)];
                } else if ([firstRankPremiumPercent floatValue] >= 0.5) {
                    // 0.5 이상인 경우 회색
                    if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                        // ****** 시스템 테마 설정에 따라 색 다르게 적용 ****** //
                        // 다크모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor darkGrayColor]
                                                 range:NSMakeRange(0, firstRankPremiumPercent.length)];
                    } else {
                        // 라이트모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor lightGrayColor]
                                                 range:NSMakeRange(0, firstRankPremiumPercent.length)];
                        
                    }
                } else {
                    // 일반 상태의 경우 clearColor로 지정해, 기존 색을 삭제
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor clearColor]
                                             range:NSMakeRange(0, firstRankPremiumPercent.length)];
                }
                disparityRatioFirstRankLabel.attributedText = attributedString;
            }
            // 데이터 유무 확인
            lastRankDisparityRatioData = allTicker.recentFuturesTicker[disparityRatioLastRankExchange][asset][paymentCurrency];
            if (lastRankDisparityRatioData[@"price"]) {
                // last rank
                NSString *lastRankPremiumPercent;
                // 거래소가 2개 이상 있는 경우이므로 둘 다 노출
                UILabel *disparityRatioLastRankLabel = _disparityRatioLastRankLabelList[i];
                // 괴리율 노출
                NSString *disparityRatio = [NSString stringWithFormat:@"%.2f", [disparityData[disparityRatioLastRankExchange][@"disparityRatio"] floatValue]];
                if ([disparityData[disparityRatioFirstRankExchange][@"disparityExist"] boolValue]) {
                    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];
                        }
                    }
                } else {
                    lastRankPremiumPercent = @" - ";
                }
                
                
                lastRankPremiumPercent = [lastRankPremiumPercent stringByAppendingString:@"%"];
                
                // 특성 적용
                // 특정 값 이상 프리미엄 발생시, 텍스트에 배경색 입히기
                NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:lastRankPremiumPercent];
                if ([lastRankPremiumPercent floatValue] >= 5 || [lastRankPremiumPercent floatValue] <= -5) {
                    // 5 이상인 경우 노란색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor yellowColor]
                                             range:NSMakeRange(0, lastRankPremiumPercent.length)];
                } else if ([lastRankPremiumPercent floatValue] >= 3 || [lastRankPremiumPercent floatValue] <= -3) {
                    // 3 이상인 경우 빨간색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor redColor]
                                             range:NSMakeRange(0, lastRankPremiumPercent.length)];
                } else if ([lastRankPremiumPercent floatValue] >= 2 || [lastRankPremiumPercent floatValue] <= -2) {
                    // 2 이상인 경우 주황색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor orangeColor]
                                             range:NSMakeRange(0, lastRankPremiumPercent.length)];
                } else if ([lastRankPremiumPercent floatValue] >= 1 || [lastRankPremiumPercent floatValue] <= -1) {
                    // 1 이상인 경우 주황색
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor purpleColor]
                                             range:NSMakeRange(0, lastRankPremiumPercent.length)];
                } else if ([lastRankPremiumPercent floatValue] >= 0.5) {
                    // 0.5 이상인 경우 회색
                    if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                        // ****** 시스템 테마 설정에 따라 색 다르게 적용 ****** //
                        // 다크모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor darkGrayColor]
                                                 range:NSMakeRange(0, lastRankPremiumPercent.length)];
                    } else {
                        // 라이트모드인 경우
                        [attributedString addAttribute:NSBackgroundColorAttributeName
                                                 value:[UIColor lightGrayColor]
                                                 range:NSMakeRange(0, lastRankPremiumPercent.length)];
                        
                    }
                } else {
                    // 일반 상태의 경우 clearColor로 지정해, 기존 색을 삭제
                    [attributedString addAttribute:NSBackgroundColorAttributeName
                                             value:[UIColor clearColor]
                                             range:NSMakeRange(0, lastRankPremiumPercent.length)];
                }
                disparityRatioLastRankLabel.attributedText = attributedString;
            }
        }
    }
    // **************************************** [End] 카드뷰 목록 쭉 만들기 **************************************** //
}
@end

 

 

 

 

+ Recent posts