mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Fix qr code rendering
This commit is contained in:
parent
a960811eac
commit
a271b90793
@ -12,7 +12,33 @@ public enum QrCodeIcon {
|
|||||||
case custom(UIImage?)
|
case custom(UIImage?)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func qrCode(string: String, color: UIColor, backgroundColor: UIColor? = nil, icon: QrCodeIcon, ecl: String = "M") -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
private func floorToContextPixels(_ value: CGFloat, scale: CGFloat? = UIScreenScale) -> CGFloat {
|
||||||
|
var scale = scale ?? UIScreenScale
|
||||||
|
return floor(value * scale) / scale
|
||||||
|
}
|
||||||
|
|
||||||
|
private func roundToContextPixels(_ value: CGFloat, scale: CGFloat? = UIScreenScale) -> CGFloat {
|
||||||
|
var scale = scale ?? UIScreenScale
|
||||||
|
return round(value * scale) / scale
|
||||||
|
}
|
||||||
|
|
||||||
|
public func qrCodeCutout(size: Int, dimensions: CGSize, scale: CGFloat?) -> (Int, CGRect, CGFloat) {
|
||||||
|
var cutoutSize = Int(round(CGFloat(size) * 0.297))
|
||||||
|
if size == 39 {
|
||||||
|
cutoutSize = 11
|
||||||
|
} else if cutoutSize % 2 == 0 {
|
||||||
|
cutoutSize += 1
|
||||||
|
}
|
||||||
|
cutoutSize = min(23, cutoutSize)
|
||||||
|
|
||||||
|
let quadSize = floorToContextPixels(dimensions.width / CGFloat(size), scale: scale)
|
||||||
|
let cutoutSide = quadSize * CGFloat(cutoutSize - 2)
|
||||||
|
let cutoutRect = CGRect(x: floorToContextPixels((dimensions.width - cutoutSide) / 2.0, scale: scale), y: floorToContextPixels((dimensions.height - cutoutSide) / 2.0, scale: scale), width: cutoutSide, height: cutoutSide)
|
||||||
|
|
||||||
|
return (cutoutSize, cutoutRect, quadSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func qrCode(string: String, color: UIColor, backgroundColor: UIColor? = nil, icon: QrCodeIcon, ecl: String = "M") -> Signal<(Int, (TransformImageArguments) -> DrawingContext?), NoError> {
|
||||||
return Signal<(Data, Int, Int), NoError> { subscriber in
|
return Signal<(Data, Int, Int), NoError> { subscriber in
|
||||||
if let data = string.data(using: .isoLatin1, allowLossyConversion: false), let filter = CIFilter(name: "CIQRCodeGenerator") {
|
if let data = string.data(using: .isoLatin1, allowLossyConversion: false), let filter = CIFilter(name: "CIQRCodeGenerator") {
|
||||||
filter.setValue(data, forKey: "inputMessage")
|
filter.setValue(data, forKey: "inputMessage")
|
||||||
@ -43,29 +69,20 @@ public func qrCode(string: String, color: UIColor, backgroundColor: UIColor? = n
|
|||||||
return EmptyDisposable
|
return EmptyDisposable
|
||||||
}
|
}
|
||||||
|> map { data, size, bytesPerRow in
|
|> map { data, size, bytesPerRow in
|
||||||
return { arguments in
|
return (size, { arguments in
|
||||||
let context = DrawingContext(size: arguments.drawingSize, scale: arguments.scale ?? 0.0, clear: true)
|
let context = DrawingContext(size: arguments.drawingSize, scale: arguments.scale ?? 0.0, clear: true)
|
||||||
let side = floorToScreenPixels(arguments.drawingSize.width / CGFloat(size))
|
|
||||||
let padding: CGFloat = floor((arguments.drawingSize.width - CGFloat(side * CGFloat(size))) / 2.0)
|
|
||||||
|
|
||||||
let drawingRect = arguments.drawingRect
|
let drawingRect = arguments.drawingRect
|
||||||
let fittedSize = arguments.imageSize.aspectFilled(arguments.boundingSize).fitted(arguments.imageSize)
|
let fittedSize = arguments.imageSize.aspectFilled(arguments.boundingSize).fitted(arguments.imageSize)
|
||||||
let fittedRect = CGRect(origin: CGPoint(x: drawingRect.origin.x + (drawingRect.size.width - fittedSize.width) / 2.0, y: drawingRect.origin.y + (drawingRect.size.height - fittedSize.height) / 2.0), size: fittedSize)
|
let fittedRect = CGRect(origin: CGPoint(x: drawingRect.origin.x + (drawingRect.size.width - fittedSize.width) / 2.0, y: drawingRect.origin.y + (drawingRect.size.height - fittedSize.height) / 2.0), size: fittedSize)
|
||||||
|
|
||||||
let codeScale: CGFloat = 1.10256
|
let (cutoutSize, clipRect, side) = qrCodeCutout(size: size, dimensions: fittedSize, scale: arguments.scale)
|
||||||
let clipSide = fittedRect.width * 0.23124
|
let padding: CGFloat = roundToContextPixels((arguments.drawingSize.width - CGFloat(side * CGFloat(size))) / 2.0, scale: arguments.scale)
|
||||||
let clipRect = CGRect(x: fittedRect.midX - clipSide / 2.0, y: fittedRect.midY - clipSide / 2.0, width: clipSide, height: clipSide)
|
|
||||||
|
|
||||||
let cutout: (Int, Int)?
|
let cutout: (Int, Int)?
|
||||||
if case .none = icon {
|
if case .none = icon {
|
||||||
cutout = nil
|
cutout = nil
|
||||||
} else {
|
} else {
|
||||||
var cutoutSize = Int(ceil((clipSide + side * 2.0) / side))
|
|
||||||
if size == 39 {
|
|
||||||
cutoutSize = 11
|
|
||||||
} else if cutoutSize % 2 == 0 {
|
|
||||||
cutoutSize += 1
|
|
||||||
}
|
|
||||||
let start = (size - cutoutSize) / 2
|
let start = (size - cutoutSize) / 2
|
||||||
cutout = (start, start + cutoutSize - 1)
|
cutout = (start, start + cutoutSize - 1)
|
||||||
}
|
}
|
||||||
@ -211,7 +228,7 @@ public func qrCode(string: String, color: UIColor, backgroundColor: UIColor? = n
|
|||||||
c.setStrokeColor(color.cgColor)
|
c.setStrokeColor(color.cgColor)
|
||||||
c.setFillColor(color.cgColor)
|
c.setFillColor(color.cgColor)
|
||||||
|
|
||||||
let markerSide = floorToScreenPixels(CGFloat(markerSize - 1) * squareSize.width * 1.05)
|
let markerSide = floorToContextPixels(CGFloat(markerSize - 1) * squareSize.width * 1.05, scale: arguments.scale)
|
||||||
|
|
||||||
func drawMarker(x: CGFloat, y: CGFloat) {
|
func drawMarker(x: CGFloat, y: CGFloat) {
|
||||||
var path = UIBezierPath(roundedRect: CGRect(x: x + squareSize.width / 2.0, y: y + squareSize.width / 2.0, width: markerSide, height: markerSide), cornerRadius: markerSide / 3.5)
|
var path = UIBezierPath(roundedRect: CGRect(x: x + squareSize.width / 2.0, y: y + squareSize.width / 2.0, width: markerSide, height: markerSide), cornerRadius: markerSide / 3.5)
|
||||||
@ -233,7 +250,7 @@ public func qrCode(string: String, color: UIColor, backgroundColor: UIColor? = n
|
|||||||
|
|
||||||
switch icon {
|
switch icon {
|
||||||
case .proxy:
|
case .proxy:
|
||||||
let iconScale = fittedRect.width / 420.0 * codeScale
|
let iconScale = clipRect.size.width * 0.01111
|
||||||
let iconSize = CGSize(width: 65.0 * iconScale, height: 79.0 * iconScale)
|
let iconSize = CGSize(width: 65.0 * iconScale, height: 79.0 * iconScale)
|
||||||
let point = CGPoint(x: fittedRect.midX - iconSize.width / 2.0, y: fittedRect.midY - iconSize.height / 2.0)
|
let point = CGPoint(x: fittedRect.midX - iconSize.width / 2.0, y: fittedRect.midY - iconSize.height / 2.0)
|
||||||
c.translateBy(x: point.x, y: point.y)
|
c.translateBy(x: point.x, y: point.y)
|
||||||
@ -268,6 +285,6 @@ public func qrCode(string: String, color: UIColor, backgroundColor: UIColor? = n
|
|||||||
}
|
}
|
||||||
|
|
||||||
return context
|
return context
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,8 +41,8 @@ public final class ShareProxyServerActionSheetController: ActionSheetController
|
|||||||
items.append(ActionSheetButtonItem(title: strings.SocksProxySetup_ShareQRCode, action: { [weak self] in
|
items.append(ActionSheetButtonItem(title: strings.SocksProxySetup_ShareQRCode, action: { [weak self] in
|
||||||
self?.dismissAnimated()
|
self?.dismissAnimated()
|
||||||
let _ = (qrCode(string: link, color: .black, backgroundColor: .white, icon: .proxy)
|
let _ = (qrCode(string: link, color: .black, backgroundColor: .white, icon: .proxy)
|
||||||
|> map { generator -> UIImage? in
|
|> map { _, generator -> UIImage? in
|
||||||
let imageSize = CGSize(width: 512.0, height: 512.0)
|
let imageSize = CGSize(width: 768.0, height: 768.0)
|
||||||
let context = generator(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), scale: 1.0))
|
let context = generator(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), scale: 1.0))
|
||||||
return context?.generateImage()
|
return context?.generateImage()
|
||||||
}
|
}
|
||||||
@ -128,7 +128,9 @@ private final class ProxyServerQRCodeItemNode: ActionSheetItemNode {
|
|||||||
self.label.attributedText = NSAttributedString(string: strings.SocksProxySetup_ShareQRCodeInfo, font: ActionSheetTextNode.defaultFont, textColor: self.theme.secondaryTextColor, paragraphAlignment: .center)
|
self.label.attributedText = NSAttributedString(string: strings.SocksProxySetup_ShareQRCodeInfo, font: ActionSheetTextNode.defaultFont, textColor: self.theme.secondaryTextColor, paragraphAlignment: .center)
|
||||||
|
|
||||||
self.imageNode = TransformImageNode()
|
self.imageNode = TransformImageNode()
|
||||||
self.imageNode.setSignal(qrCode(string: link, color: .black, backgroundColor: .white, icon: .proxy), attemptSynchronously: true)
|
self.imageNode.clipsToBounds = true
|
||||||
|
self.imageNode.setSignal(qrCode(string: link, color: .black, backgroundColor: .white, icon: .proxy) |> map { $0.1 }, attemptSynchronously: true)
|
||||||
|
self.imageNode.cornerRadius = 14.0
|
||||||
|
|
||||||
super.init(theme: theme)
|
super.init(theme: theme)
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import SolidRoundedButtonNode
|
|||||||
|
|
||||||
private func shareInvoiceQrCode(context: WalletContext, invoice: String) {
|
private func shareInvoiceQrCode(context: WalletContext, invoice: String) {
|
||||||
let _ = (qrCode(string: invoice, color: .black, backgroundColor: .white, icon: .custom(UIImage(bundleImageName: "Wallet/QrGem")))
|
let _ = (qrCode(string: invoice, color: .black, backgroundColor: .white, icon: .custom(UIImage(bundleImageName: "Wallet/QrGem")))
|
||||||
|> map { generator -> UIImage? in
|
|> map { _, generator -> UIImage? in
|
||||||
let imageSize = CGSize(width: 768.0, height: 768.0)
|
let imageSize = CGSize(width: 768.0, height: 768.0)
|
||||||
let context = generator(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), scale: 1.0))
|
let context = generator(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), scale: 1.0))
|
||||||
return context?.generateImage()
|
return context?.generateImage()
|
||||||
@ -151,12 +151,15 @@ private final class WalletReceiveScreenNode: ViewControllerTracingNode {
|
|||||||
private let qrButtonNode: HighlightTrackingButtonNode
|
private let qrButtonNode: HighlightTrackingButtonNode
|
||||||
private let qrImageNode: TransformImageNode
|
private let qrImageNode: TransformImageNode
|
||||||
private let qrIconNode: AnimatedStickerNode
|
private let qrIconNode: AnimatedStickerNode
|
||||||
|
private var qrCodeSize: Int?
|
||||||
|
|
||||||
private let urlTextNode: ImmediateTextNode
|
private let urlTextNode: ImmediateTextNode
|
||||||
|
|
||||||
private let buttonNode: SolidRoundedButtonNode
|
private let buttonNode: SolidRoundedButtonNode
|
||||||
private let secondaryButtonNode: HighlightableButtonNode
|
private let secondaryButtonNode: HighlightableButtonNode
|
||||||
|
|
||||||
|
private var validLayout: (ContainerViewLayout, CGFloat)?
|
||||||
|
|
||||||
var openCreateInvoice: (() -> Void)?
|
var openCreateInvoice: (() -> Void)?
|
||||||
var displayCopyContextMenu: ((ASDisplayNode, CGRect, String) -> Void)?
|
var displayCopyContextMenu: ((ASDisplayNode, CGRect, String) -> Void)?
|
||||||
|
|
||||||
@ -202,7 +205,15 @@ private final class WalletReceiveScreenNode: ViewControllerTracingNode {
|
|||||||
self.addSubnode(self.buttonNode)
|
self.addSubnode(self.buttonNode)
|
||||||
self.addSubnode(self.secondaryButtonNode)
|
self.addSubnode(self.secondaryButtonNode)
|
||||||
|
|
||||||
self.qrImageNode.setSignal(qrCode(string: urlForMode(mode), color: .black, backgroundColor: .white, icon: .cutout), attemptSynchronously: true)
|
self.qrImageNode.setSignal(qrCode(string: urlForMode(mode), color: .black, backgroundColor: .white, icon: .cutout) |> beforeNext { [weak self] size, _ in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
strongSelf.qrCodeSize = size
|
||||||
|
if let (layout, navigationHeight) = strongSelf.validLayout {
|
||||||
|
strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate)
|
||||||
|
}
|
||||||
|
} |> map { $0.1 }, attemptSynchronously: true)
|
||||||
|
|
||||||
self.qrButtonNode.addTarget(self, action: #selector(self.qrPressed), forControlEvents: .touchUpInside)
|
self.qrButtonNode.addTarget(self, action: #selector(self.qrPressed), forControlEvents: .touchUpInside)
|
||||||
self.qrButtonNode.highligthedChanged = { [weak self] highlighted in
|
self.qrButtonNode.highligthedChanged = { [weak self] highlighted in
|
||||||
@ -275,6 +286,8 @@ private final class WalletReceiveScreenNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
|
self.validLayout = (layout, navigationHeight)
|
||||||
|
|
||||||
var insets = layout.insets(options: [])
|
var insets = layout.insets(options: [])
|
||||||
insets.top += navigationHeight
|
insets.top += navigationHeight
|
||||||
let inset: CGFloat = 22.0
|
let inset: CGFloat = 22.0
|
||||||
@ -295,11 +308,12 @@ private final class WalletReceiveScreenNode: ViewControllerTracingNode {
|
|||||||
transition.updateFrame(node: self.qrImageNode, frame: imageFrame)
|
transition.updateFrame(node: self.qrImageNode, frame: imageFrame)
|
||||||
transition.updateFrame(node: self.qrButtonNode, frame: imageFrame)
|
transition.updateFrame(node: self.qrButtonNode, frame: imageFrame)
|
||||||
|
|
||||||
let iconSide = floor(imageSide * 0.24)
|
if let qrCodeSize = self.qrCodeSize {
|
||||||
let iconSize = CGSize(width: iconSide, height: iconSide)
|
let (_, cutoutFrame, _) = qrCodeCutout(size: qrCodeSize, dimensions: imageSize, scale: nil)
|
||||||
self.qrIconNode.updateLayout(size: iconSize)
|
self.qrIconNode.updateLayout(size: cutoutFrame.size)
|
||||||
transition.updateBounds(node: self.qrIconNode, bounds: CGRect(origin: CGPoint(), size: iconSize))
|
transition.updateBounds(node: self.qrIconNode, bounds: CGRect(origin: CGPoint(), size: cutoutFrame.size))
|
||||||
transition.updatePosition(node: self.qrIconNode, position: imageFrame.center.offsetBy(dx: 0.0, dy: -1.0))
|
transition.updatePosition(node: self.qrIconNode, position: imageFrame.center.offsetBy(dx: 0.0, dy: -1.0))
|
||||||
|
}
|
||||||
|
|
||||||
if self.urlTextNode.attributedText?.string.isEmpty ?? true {
|
if self.urlTextNode.attributedText?.string.isEmpty ?? true {
|
||||||
var url = urlForMode(self.mode)
|
var url = urlForMode(self.mode)
|
||||||
|
@ -418,21 +418,21 @@ private final class WalletTransactionInfoScreenNode: ViewControllerTracingNode,
|
|||||||
|
|
||||||
var feesString: String = ""
|
var feesString: String = ""
|
||||||
if case let .completed(transaction) = walletTransaction {
|
if case let .completed(transaction) = walletTransaction {
|
||||||
if transaction.storageFee != 0 {
|
|
||||||
feesString.append(formatBalanceText(transaction.storageFee, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator) + " storage fee")
|
|
||||||
}
|
|
||||||
if transaction.otherFee != 0 {
|
if transaction.otherFee != 0 {
|
||||||
|
feesString.append(formatBalanceText(transaction.otherFee, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator) + " transaction fee")
|
||||||
|
}
|
||||||
|
if transaction.storageFee != 0 {
|
||||||
if !feesString.isEmpty {
|
if !feesString.isEmpty {
|
||||||
feesString.append("\n")
|
feesString.append("\n")
|
||||||
}
|
}
|
||||||
feesString.append(formatBalanceText(transaction.otherFee, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator) + " transaction fee")
|
feesString.append(formatBalanceText(transaction.storageFee, decimalSeparator: presentationData.dateTimeFormat.decimalSeparator) + " storage fee")
|
||||||
}
|
}
|
||||||
|
|
||||||
self.feesInfoIconNode.isHidden = feesString.isEmpty
|
self.feesInfoIconNode.isHidden = feesString.isEmpty
|
||||||
}
|
}
|
||||||
self.feesNode.attributedText = NSAttributedString(string: feesString, font: subtitleFont, textColor: seccondaryTextColor)
|
self.feesNode.attributedText = NSAttributedString(string: feesString, font: subtitleFont, textColor: seccondaryTextColor)
|
||||||
|
|
||||||
self.feesButtonNode.addTarget(self, action: #selector(feesPressed), forControlEvents: .touchUpInside)
|
self.feesButtonNode.addTarget(self, action: #selector(self.feesPressed), forControlEvents: .touchUpInside)
|
||||||
|
|
||||||
var commentBackgroundColor = presentationData.theme.transaction.descriptionBackgroundColor
|
var commentBackgroundColor = presentationData.theme.transaction.descriptionBackgroundColor
|
||||||
if commentBackgroundColor.distance(to: presentationData.theme.list.plainBackgroundColor) < 100 {
|
if commentBackgroundColor.distance(to: presentationData.theme.list.plainBackgroundColor) < 100 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user