Display token image in Wallet tab #1920

pull/1921/head
Vladyslav shepitko 4 years ago committed by Hwee-Boon Yar
parent e7959b00c8
commit f43adb1b37
  1. 21
      AlphaWallet/Assets.xcassets/token-artis.imageset/Contents.json
  2. BIN
      AlphaWallet/Assets.xcassets/token-artis.imageset/token-artis.pdf
  3. 21
      AlphaWallet/Assets.xcassets/token-callisto.imageset/Contents.json
  4. BIN
      AlphaWallet/Assets.xcassets/token-callisto.imageset/token-callisto.pdf
  5. 21
      AlphaWallet/Assets.xcassets/token-etc.imageset/Contents.json
  6. BIN
      AlphaWallet/Assets.xcassets/token-etc.imageset/token-etc.pdf
  7. 21
      AlphaWallet/Assets.xcassets/token-poa.imageset/Contents.json
  8. BIN
      AlphaWallet/Assets.xcassets/token-poa.imageset/token-poa.pdf
  9. 20
      AlphaWallet/Extensions/UIView.swift
  10. 19
      AlphaWallet/Settings/Types/RPCServers.swift
  11. 25
      AlphaWallet/Tokens/Types/TokenObject.swift
  12. 30
      AlphaWallet/Tokens/ViewModels/EthTokenViewCellViewModel.swift
  13. 24
      AlphaWallet/Tokens/ViewModels/FungibleTokenViewCellViewModel.swift
  14. 22
      AlphaWallet/Tokens/ViewModels/NonFungibleTokenViewCellViewModel.swift
  15. 29
      AlphaWallet/Tokens/Views/EthTokenViewCell.swift
  16. 32
      AlphaWallet/Tokens/Views/FungibleTokenViewCell.swift
  17. 28
      AlphaWallet/Tokens/Views/NonFungibleTokenViewCell.swift

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "token-artis.pdf",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "token-callisto.pdf",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "token-etc.pdf",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "token-poa.pdf",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

@ -4,7 +4,23 @@ import Foundation
import UIKit import UIKit
extension UIView { extension UIView {
static var tokenSymbolBackgroundImageCache: [UIColor: UIImage] = .init()
static func tokenSymbolBackgroundImage(backgroundColor: UIColor) -> UIImage {
if let cachedValue = tokenSymbolBackgroundImageCache[backgroundColor] {
return cachedValue
}
let size = CGSize(width: 40, height: 40)
let rect = CGRect(origin: .zero, size: size)
let renderer = UIGraphicsImageRenderer(size: size)
let image = renderer.image { ctx in
ctx.cgContext.setFillColor(backgroundColor.cgColor)
ctx.cgContext.addEllipse(in: rect)
ctx.cgContext.drawPath(using: .fill)
}
tokenSymbolBackgroundImageCache[backgroundColor] = image
return image
}
func dropShadow(color: UIColor, opacity: Float = 0.5, offSet: CGSize = .zero, radius: CGFloat = 1, scale: Bool = true, shouldRasterize: Bool = true) { func dropShadow(color: UIColor, opacity: Float = 0.5, offSet: CGSize = .zero, radius: CGFloat = 1, scale: Bool = true, shouldRasterize: Bool = true) {
layer.masksToBounds = false layer.masksToBounds = false
layer.shadowColor = color.cgColor layer.shadowColor = color.cgColor
@ -16,7 +32,7 @@ extension UIView {
layer.shouldRasterize = shouldRasterize layer.shouldRasterize = shouldRasterize
layer.rasterizationScale = scale ? UIScreen.main.scale : 1 layer.rasterizationScale = scale ? UIScreen.main.scale : 1
} }
func anchorsConstraint(to view: UIView, edgeInsets: UIEdgeInsets = .zero) -> [NSLayoutConstraint] { func anchorsConstraint(to view: UIView, edgeInsets: UIEdgeInsets = .zero) -> [NSLayoutConstraint] {
return [ return [
leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: edgeInsets.left), leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: edgeInsets.left),

@ -342,6 +342,25 @@ enum RPCServer: Hashable, CaseIterable {
} }
} }
var iconImage: UIImage? {
switch self {
case .main:
return R.image.eth()!
case .xDai:
return R.image.xDai()!
case .poa:
return R.image.tokenPoa()!
case .classic:
return R.image.tokenEtc()!
case .callisto:
return R.image.tokenCallisto()!
case .artis_sigma1:
return R.image.tokenArtis()!
case .kovan, .ropsten, .rinkeby, .sokol, .goerli, .artis_tau1, .custom:
return nil
}
}
init(name: String) { init(name: String) {
self = { self = {
switch name { switch name {

@ -122,6 +122,31 @@ class TokenObject: Object {
var server: RPCServer { var server: RPCServer {
return .init(chainID: chainId) return .init(chainID: chainId)
} }
var iconImage: UIImage? {
switch type {
case .nativeCryptocurrency:
return server.iconImage
case .erc20, .erc875, .erc721, .erc721ForTickets:
return nil
}
}
var symbolBackgroundColor: UIColor {
if contractAddress.sameContract(as: Constants.nativeCryptoAddressInDatabase) {
return server.blockChainNameColor
} else {
let colors = [R.color.radical()!, R.color.cerulean()!, R.color.emerald()!, R.color.indigo()!, R.color.azure()!, R.color.pumpkin()!]
let index: Int
//We just need a random number from the contract. The LSBs are more random than the MSBs
if let i = Int(contractAddress.eip55String.substring(from: 37), radix: 16) {
index = i % colors.count
} else {
index = 0
}
return colors[index]
}
}
} }
func isNonZeroBalance(_ balance: String) -> Bool { func isNonZeroBalance(_ balance: String) -> Bool {

@ -5,6 +5,8 @@ import UIKit
import BigInt import BigInt
struct EthTokenViewCellViewModel { struct EthTokenViewCellViewModel {
static let numberOfCharactersOfSymbolToShow = 4
private let shortFormatter = EtherNumberFormatter.short private let shortFormatter = EtherNumberFormatter.short
private let token: TokenObject private let token: TokenObject
private let currencyAmount: String? private let currencyAmount: String?
@ -37,7 +39,7 @@ struct EthTokenViewCellViewModel {
var amount: String { var amount: String {
return shortFormatter.string(from: BigInt(token.value) ?? BigInt(), decimals: token.decimals) return shortFormatter.string(from: BigInt(token.value) ?? BigInt(), decimals: token.decimals)
} }
var backgroundColor: UIColor { var backgroundColor: UIColor {
return Screen.TokenCard.Color.background return Screen.TokenCard.Color.background
@ -97,14 +99,14 @@ struct EthTokenViewCellViewModel {
return "-" return "-"
} }
} }
var valueChange: String? { var valueChange: String? {
return EthCurrencyHelper(ticker: ticker).valueChanged24h(currencyAmountWithoutSymbol: currencyAmountWithoutSymbol) return EthCurrencyHelper(ticker: ticker).valueChanged24h(currencyAmountWithoutSymbol: currencyAmountWithoutSymbol)
} }
var value: String? { var value: String? {
return currencyAmount return currencyAmount
} }
var blockChainLabelHidden: Bool { var blockChainLabelHidden: Bool {
return currencyAmount != nil return currencyAmount != nil
@ -113,4 +115,26 @@ struct EthTokenViewCellViewModel {
var alpha: CGFloat { var alpha: CGFloat {
return isVisible ? 1.0 : 0.4 return isVisible ? 1.0 : 0.4
} }
var iconImage: UIImage? {
if let image = token.iconImage {
return image
} else {
return UIView.tokenSymbolBackgroundImage(backgroundColor: token.symbolBackgroundColor)
}
}
var symbolInIcon: String {
guard token.iconImage == nil else { return "" }
let i = [EthTokenViewCellViewModel.numberOfCharactersOfSymbolToShow, token.symbol.count].min()!
return token.symbol.substring(to: i)
}
var symbolColor: UIColor {
Colors.appWhite
}
var symbolFont: UIFont {
UIFont.systemFont(ofSize: 13)
}
} }

@ -36,7 +36,7 @@ struct FungibleTokenViewCellViewModel {
var contentsBackgroundColor: UIColor { var contentsBackgroundColor: UIColor {
return Screen.TokenCard.Color.background return Screen.TokenCard.Color.background
} }
var titleColor: UIColor { var titleColor: UIColor {
return Screen.TokenCard.Color.title return Screen.TokenCard.Color.title
@ -57,4 +57,26 @@ struct FungibleTokenViewCellViewModel {
var alpha: CGFloat { var alpha: CGFloat {
return isVisible ? 1.0 : 0.4 return isVisible ? 1.0 : 0.4
} }
var iconImage: UIImage? {
if let image = token.iconImage {
return image
} else {
return UIView.tokenSymbolBackgroundImage(backgroundColor: token.symbolBackgroundColor)
}
}
var symbolInIcon: String {
guard token.iconImage == nil else { return "" }
let i = [EthTokenViewCellViewModel.numberOfCharactersOfSymbolToShow, token.symbol.count].min()!
return token.symbol.substring(to: i)
}
var symbolColor: UIColor {
Colors.appWhite
}
var symbolFont: UIFont {
UIFont.systemFont(ofSize: 13)
}
} }

@ -85,4 +85,26 @@ struct NonFungibleTokenViewCellViewModel {
var alpha: CGFloat { var alpha: CGFloat {
return isVisible ? 1.0 : 0.4 return isVisible ? 1.0 : 0.4
} }
var iconImage: UIImage? {
if let image = token.iconImage {
return image
} else {
return UIView.tokenSymbolBackgroundImage(backgroundColor: token.symbolBackgroundColor)
}
}
var symbolInIcon: String {
guard token.iconImage == nil else { return "" }
let i = [EthTokenViewCellViewModel.numberOfCharactersOfSymbolToShow, token.symbol.count].min()!
return token.symbol.substring(to: i)
}
var symbolColor: UIColor {
Colors.appWhite
}
var symbolFont: UIFont {
UIFont.systemFont(ofSize: 13)
}
} }

@ -9,6 +9,7 @@ class EthTokenViewCell: UITableViewCell {
private let background = UIView() private let background = UIView()
private let titleLabel = UILabel() private let titleLabel = UILabel()
private let symbolLabel = UILabel()
private let valuePercentageChangeValueLabel = UILabel() private let valuePercentageChangeValueLabel = UILabel()
private let valuePercentageChangePeriodLabel = UILabel() private let valuePercentageChangePeriodLabel = UILabel()
private let valueChangeLabel = UILabel() private let valueChangeLabel = UILabel()
@ -17,6 +18,12 @@ class EthTokenViewCell: UITableViewCell {
private var viewsWithContent: [UIView] { private var viewsWithContent: [UIView] {
[titleLabel, valuePercentageChangeValueLabel, valuePercentageChangePeriodLabel, valueChangeLabel] [titleLabel, valuePercentageChangeValueLabel, valuePercentageChangePeriodLabel, valueChangeLabel]
} }
private var tokenIconImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
@ -28,14 +35,23 @@ class EthTokenViewCell: UITableViewCell {
valueChangeLabel.textAlignment = .center valueChangeLabel.textAlignment = .center
valueLabel.textAlignment = .center valueLabel.textAlignment = .center
let stackView = [ let col0 = tokenIconImageView
let col1 = [
titleLabel, titleLabel,
[blockchainLabel, valueLabel, UIView.spacerWidth(flexible: true), valueChangeLabel, valuePercentageChangeValueLabel].asStackView(spacing: 5) [blockchainLabel, valueLabel, UIView.spacerWidth(flexible: true), valueChangeLabel, valuePercentageChangeValueLabel].asStackView(spacing: 5)
].asStackView(axis: .vertical) ].asStackView(axis: .vertical)
let stackView = [col0, col1].asStackView(spacing: 12)
stackView.translatesAutoresizingMaskIntoConstraints = false stackView.translatesAutoresizingMaskIntoConstraints = false
background.addSubview(stackView) background.addSubview(stackView)
symbolLabel.translatesAutoresizingMaskIntoConstraints = false
background.addSubview(symbolLabel)
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
symbolLabel.anchorsConstraint(to: tokenIconImageView),
tokenIconImageView.heightAnchor.constraint(equalToConstant: 40),
tokenIconImageView.widthAnchor.constraint(equalToConstant: 40),
stackView.anchorsConstraint(to: background, edgeInsets: .init(top: 16, left: 20, bottom: 16, right: 16)), stackView.anchorsConstraint(to: background, edgeInsets: .init(top: 16, left: 20, bottom: 16, right: 16)),
background.anchorsConstraint(to: contentView) background.anchorsConstraint(to: contentView)
]) ])
@ -56,7 +72,13 @@ class EthTokenViewCell: UITableViewCell {
titleLabel.textColor = viewModel.titleColor titleLabel.textColor = viewModel.titleColor
titleLabel.font = viewModel.titleFont titleLabel.font = viewModel.titleFont
titleLabel.text = "\(viewModel.amount) \(viewModel.title)" titleLabel.text = "\(viewModel.amount) \(viewModel.title)"
titleLabel.baselineAdjustment = .alignCenters titleLabel.baselineAdjustment = .alignCenters
symbolLabel.textColor = viewModel.symbolColor
symbolLabel.font = viewModel.symbolFont
symbolLabel.textAlignment = .center
symbolLabel.adjustsFontSizeToFitWidth = true
symbolLabel.text = viewModel.symbolInIcon
valuePercentageChangeValueLabel.textColor = viewModel.valuePercentageChangeColor valuePercentageChangeValueLabel.textColor = viewModel.valuePercentageChangeColor
valuePercentageChangeValueLabel.font = viewModel.textValueFont valuePercentageChangeValueLabel.font = viewModel.textValueFont
@ -74,9 +96,10 @@ class EthTokenViewCell: UITableViewCell {
blockchainLabel.font = viewModel.subtitleFont blockchainLabel.font = viewModel.subtitleFont
blockchainLabel.text = viewModel.blockChainName blockchainLabel.text = viewModel.blockChainName
blockchainLabel.isHidden = viewModel.blockChainLabelHidden blockchainLabel.isHidden = viewModel.blockChainLabelHidden
viewsWithContent.forEach { viewsWithContent.forEach {
$0.alpha = viewModel.alpha $0.alpha = viewModel.alpha
} }
tokenIconImageView.image = viewModel.iconImage
} }
} }

@ -9,25 +9,41 @@ class FungibleTokenViewCell: UITableViewCell {
private let background = UIView() private let background = UIView()
private let titleLabel = UILabel() private let titleLabel = UILabel()
private let blockchainLabel = UILabel() private let symbolLabel = UILabel()
private let blockchainLabel = UILabel()
private var viewsWithContent: [UIView] { private var viewsWithContent: [UIView] {
[self.titleLabel, blockchainLabel] [self.titleLabel, blockchainLabel]
} }
private var tokenIconImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(background) contentView.addSubview(background)
background.translatesAutoresizingMaskIntoConstraints = false background.translatesAutoresizingMaskIntoConstraints = false
let bottomRowStack = [blockchainLabel, UIView.spacerWidth(flexible: true)].asStackView(spacing: 15) let col0 = tokenIconImageView
let stackView = [ let col1 = [
titleLabel, titleLabel,
bottomRowStack, [blockchainLabel, UIView.spacerWidth(flexible: true)].asStackView(spacing: 15)
].asStackView(axis: .vertical) ].asStackView(axis: .vertical)
let stackView = [col0, col1].asStackView(spacing: 12)
stackView.translatesAutoresizingMaskIntoConstraints = false stackView.translatesAutoresizingMaskIntoConstraints = false
background.addSubview(stackView) background.addSubview(stackView)
symbolLabel.translatesAutoresizingMaskIntoConstraints = false
background.addSubview(symbolLabel)
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
symbolLabel.anchorsConstraint(to: tokenIconImageView),
tokenIconImageView.heightAnchor.constraint(equalToConstant: 40),
tokenIconImageView.widthAnchor.constraint(equalToConstant: 40),
stackView.anchorsConstraint(to: background, edgeInsets: .init(top: 16, left: 20, bottom: 16, right: 16)), stackView.anchorsConstraint(to: background, edgeInsets: .init(top: 16, left: 20, bottom: 16, right: 16)),
background.anchorsConstraint(to: contentView) background.anchorsConstraint(to: contentView)
]) ])
@ -50,6 +66,12 @@ class FungibleTokenViewCell: UITableViewCell {
titleLabel.text = "\(viewModel.amount) \(viewModel.title)" titleLabel.text = "\(viewModel.amount) \(viewModel.title)"
titleLabel.baselineAdjustment = .alignCenters titleLabel.baselineAdjustment = .alignCenters
symbolLabel.textColor = viewModel.symbolColor
symbolLabel.font = viewModel.symbolFont
symbolLabel.textAlignment = .center
symbolLabel.adjustsFontSizeToFitWidth = true
symbolLabel.text = viewModel.symbolInIcon
blockchainLabel.textColor = viewModel.subtitleColor blockchainLabel.textColor = viewModel.subtitleColor
blockchainLabel.font = viewModel.subtitleFont blockchainLabel.font = viewModel.subtitleFont
blockchainLabel.text = viewModel.blockChainName blockchainLabel.text = viewModel.blockChainName
@ -57,5 +79,7 @@ class FungibleTokenViewCell: UITableViewCell {
viewsWithContent.forEach { viewsWithContent.forEach {
$0.alpha = viewModel.alpha $0.alpha = viewModel.alpha
} }
tokenIconImageView.image = viewModel.iconImage
} }
} }

@ -9,24 +9,41 @@ class NonFungibleTokenViewCell: UITableViewCell {
private let background = UIView() private let background = UIView()
private let titleLabel = UILabel() private let titleLabel = UILabel()
private let symbolLabel = UILabel()
private let blockchainLabel = UILabel() private let blockchainLabel = UILabel()
private var viewsWithContent: [UIView] { private var viewsWithContent: [UIView] {
[self.titleLabel, self.blockchainLabel] [self.titleLabel, self.blockchainLabel]
} }
private var tokenIconImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier) super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(background) contentView.addSubview(background)
background.translatesAutoresizingMaskIntoConstraints = false background.translatesAutoresizingMaskIntoConstraints = false
let stackView = [
let col0 = tokenIconImageView
let col1 = [
titleLabel, titleLabel,
[blockchainLabel, UIView.spacerWidth(flexible: true)].asStackView(spacing: 15), [blockchainLabel, UIView.spacerWidth(flexible: true)].asStackView(spacing: 15)
].asStackView(axis: .vertical) ].asStackView(axis: .vertical)
let stackView = [col0, col1].asStackView(spacing: 12)
stackView.translatesAutoresizingMaskIntoConstraints = false stackView.translatesAutoresizingMaskIntoConstraints = false
background.addSubview(stackView) background.addSubview(stackView)
symbolLabel.translatesAutoresizingMaskIntoConstraints = false
background.addSubview(symbolLabel)
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
symbolLabel.anchorsConstraint(to: tokenIconImageView),
tokenIconImageView.heightAnchor.constraint(equalToConstant: 40),
tokenIconImageView.widthAnchor.constraint(equalToConstant: 40),
stackView.anchorsConstraint(to: background, edgeInsets: .init(top: 16, left: 20, bottom: 16, right: 16)), stackView.anchorsConstraint(to: background, edgeInsets: .init(top: 16, left: 20, bottom: 16, right: 16)),
background.anchorsConstraint(to: contentView) background.anchorsConstraint(to: contentView)
]) ])
@ -49,6 +66,12 @@ class NonFungibleTokenViewCell: UITableViewCell {
titleLabel.text = "\(viewModel.amount) \(viewModel.title)" titleLabel.text = "\(viewModel.amount) \(viewModel.title)"
titleLabel.baselineAdjustment = .alignCenters titleLabel.baselineAdjustment = .alignCenters
symbolLabel.textColor = viewModel.symbolColor
symbolLabel.font = viewModel.symbolFont
symbolLabel.textAlignment = .center
symbolLabel.adjustsFontSizeToFitWidth = true
symbolLabel.text = viewModel.symbolInIcon
blockchainLabel.textColor = viewModel.subtitleColor blockchainLabel.textColor = viewModel.subtitleColor
blockchainLabel.font = viewModel.subtitleFont blockchainLabel.font = viewModel.subtitleFont
blockchainLabel.text = viewModel.blockChainName blockchainLabel.text = viewModel.blockChainName
@ -56,5 +79,6 @@ class NonFungibleTokenViewCell: UITableViewCell {
viewsWithContent.forEach { viewsWithContent.forEach {
$0.alpha = viewModel.alpha $0.alpha = viewModel.alpha
} }
tokenIconImageView.image = viewModel.iconImage
} }
} }

Loading…
Cancel
Save