1. 이전 포스팅 확인하기
https://growingsaja.tistory.com/948
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