Генерация QR-кодов на Swift (iOS/macOS): от Core Image до кастомных решений
QR-коды стали неотъемлемой частью современных приложений — от передачи ссылок до авторизации и оплаты. На платформах Apple (iOS, macOS, watchOS, tvOS) существует встроенный мощный инструмент для генерации QR-кодов — фреймворк Core Image с фильтром CIQRCodeGenerator. В этой статье мы подробно разберем, как создать собственный генератор QR-кодов на Swift, включая настройку цвета, размера, добавление логотипа и интеграцию с SwiftUI.
Основы: Core Image и CIQRCodeGenerator
Core Image — это встроенный фреймворк Apple для обработки изображений. Фильтр CIQRCodeGenerator позволяет создать QR-код из любой строки (URL, текст, контактные данные). Он поддерживает коррекцию ошибок (уровни L, M, Q, H) и кодирование до 7089 символов (в зависимости от уровня коррекции).
Базовый пример генерации QR-кода в виде UIImage:
import UIKit
import CoreImage
func generateQRCode(from string: String) -> UIImage? {
let data = string.data(using: .utf8)
guard let filter = CIFilter(name: "CIQRCodeGenerator") else { return nil }
filter.setValue(data, forKey: "inputMessage")
filter.setValue("H", forKey: "inputCorrectionLevel") // Максимальная коррекция ошибок
guard let outputImage = filter.outputImage else { return nil }
// Увеличиваем разрешение (по умолчанию маленькое)
let transform = CGAffineTransform(scaleX: 10, y: 10)
let scaledImage = outputImage.transformed(by: transform)
return UIImage(ciImage: scaledImage)
}
// Использование
if let qrImage = generateQRCode(from: "https://pythonlib.ru") {
// Отображаем qrImage в UIImageView
}
Настройка цвета и фона
По умолчанию QR-код черно-белый. Чтобы изменить цвета, нужно преобразовать CIImage в UIImage, затем выполнить заливку с использованием Core Graphics или CIFilter. Проще всего — использовать CIFalseColor:
func generateColoredQRCode(from string: String, color: UIColor, backgroundColor: UIColor) -> UIImage? {
guard let data = string.data(using: .utf8),
let qrFilter = CIFilter(name: "CIQRCodeGenerator") else { return nil }
qrFilter.setValue(data, forKey: "inputMessage")
qrFilter.setValue("M", forKey: "inputCorrectionLevel")
guard let qrImage = qrFilter.outputImage else { return nil }
// Применяем цветовой фильтр
let colorFilter = CIFilter(name: "CIFalseColor")
colorFilter?.setValue(qrImage, forKey: "inputImage")
colorFilter?.setValue(CIColor(color: color), forKey: "inputColor0") // Цвет модулей
colorFilter?.setValue(CIColor(color: backgroundColor), forKey: "inputColor1") // Цвет фона
guard let coloredImage = colorFilter?.outputImage else { return nil }
let transform = CGAffineTransform(scaleX: 10, y: 10)
let scaledImage = coloredImage.transformed(by: transform)
return UIImage(ciImage: scaledImage)
}
Добавление логотипа в центр QR-кода
Для брендирования часто требуется вставить логотип в центр. Это снижает читаемость, поэтому лучше использовать высокий уровень коррекции ошибок (H). Алгоритм: сначала создаем QR-код, затем накладываем поверх него UIImage с логотипом.
func generateQRWithLogo(from string: String, logo: UIImage, qrColor: UIColor = .black, bgColor: UIColor = .white) -> UIImage? {
guard let qrImage = generateColoredQRCode(from: string, color: qrColor, backgroundColor: bgColor) else { return nil }
let qrSize = qrImage.size
let logoSize = CGSize(width: qrSize.width * 0.25, height: qrSize.height * 0.25) // 25% от QR
let logoOrigin = CGPoint(x: (qrSize.width - logoSize.width) / 2,
y: (qrSize.height - logoSize.height) / 2)
UIGraphicsBeginImageContextWithOptions(qrSize, false, qrImage.scale)
qrImage.draw(at: .zero)
logo.draw(in: CGRect(origin: logoOrigin, size: logoSize))
let finalImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return finalImage
}
Интеграция с SwiftUI
В SwiftUI удобно использовать генератор через @State и Image. Пример компонента:
import SwiftUI
import CoreImage.CIFilterBuiltins
struct QRCodeView: View {
let text: String
let color: UIColor = .blue
let bgColor: UIColor = .white
var body: some View {
if let image = generateColoredQRCode(from: text, color: color, backgroundColor: bgColor) {
Image(uiImage: image