Я считаю, что вы делаете это более сложным, чем нужно ...
Давайте начнем с простого подкласса @IBDesignable UIImageView
.
Начните с нового проекта и добавьте этот код:
@IBDesignable
class MyImageView: UIImageView {
// we'll use this later
var myIntrinsicSize: CGSize = CGSize(width: 100.0, height: 100.0)
override var intrinsicContentSize: CGSize {
return myIntrinsicSize
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setup()
self.image = UIImage()
}
func setup() {
backgroundColor = .green
contentMode = .scaleToFill
}
}
Теперь в Storyboard добавьте UIImageView
к контроллеру представления. Установите его собственный класс на MyImageView
и установите ограничения по горизонтали и вертикали по центру.
Вид изображения должен автоматически иметь размер 100 x 100
, центрированный в виде с зеленым фоном (мы просто устанавливаем фон чтобы мы могли его увидеть):
Run the app, and you should see the same thing.
Now, add it as an @IBOutlet
to a view controller:
class ViewController: UIViewController {
@IBOutlet var testImageView: MyImageView!
override func viewDidLoad() {
super.viewDidLoad()
testImageView.myIntrinsicSize = CGSize(width: 300.0, height: 300.0)
}
}
Run the app, and you will see a centered green image view, but now it will be 300 x 300
points instead of 100 x 100
.
The rest of your task is pretty much adding code to set this custom class's .image
property once you've rendered the QRCode image.
Here's the custom class:
@IBDesignable
class QRCodeView: UIImageView {
// so we can test changing the QRCode content in IB
@IBInspectable
var content:String = "test" {
didSet {
generateCode(content)
}
}
var qrIntrinsicSize: CGSize = CGSize(width: 100.0, height: 100.0)
override var intrinsicContentSize: CGSize {
return qrIntrinsicSize
}
lazy var filter = CIFilter(name: "CIQRCodeGenerator")
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setup()
generateCode(content)
}
func setup() {
contentMode = .scaleToFill
}
override func layoutSubviews() {
super.layoutSubviews()
generateCode(content)
}
func generateCode(_ string: String) {
guard let filter = filter,
let data = string.data(using: .isoLatin1, allowLossyConversion: false) else {
return
}
filter.setValue(data, forKey: "inputMessage")
guard let ciImage = filter.outputImage else {
return
}
let scX = bounds.width / ciImage.extent.size.width
let scY = bounds.height / ciImage.extent.size.height
let transform = CGAffineTransform(scaleX: scX, y: scY)
let scaled = UIImage(ciImage: ciImage.transformed(by: transform))
self.image = scaled
}
}
In Storyboard / IB:
And here's an example view controller:
class ViewController: UIViewController {
@IBOutlet var qrCodeView: QRCodeView!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// calculate your needed size
// I'll assume it ended up being 240 x 240
qrCodeView.qrIntrinsicSize = CGSize(width: 240.0, height: 240.0)
}
}
Edit
Here's a modified QRCodeView
class that will size itself to a (physical) 15x15 mm image.
I used DeviceKit
from https://github.com/devicekit/DeviceKit, чтобы получить ppi
текущего устройства. См. Комментарий, чтобы заменить его своим собственным (при условии, что вы уже используете что-то другое).
При создании экземпляра этого класса он:
- получит ppi текущего устройства
- преобразование ppi в пиксели на миллиметр
- вычисление 15 x пикселей на миллиметр
- преобразование на основе масштаба экрана
- обновление его внутреннего c размера
Для QRCodeView
(подкласс UIImageView
) нужны только ограничения позиции ... поэтому вы можете использовать Top + Leading, Top + Trailing, Center X & Y, Bottom + CenterX и т. Д. c, и т.д. c.
@IBDesignable
class QRCodeView: UIImageView {
@IBInspectable
var content:String = "test" {
didSet {
generateCode(content)
}
}
var qrIntrinsicSize: CGSize = CGSize(width: 100.0, height: 100.0)
override var intrinsicContentSize: CGSize {
return qrIntrinsicSize
}
lazy var filter = CIFilter(name: "CIQRCodeGenerator")
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setup()
generateCode(content)
}
func setup() {
contentMode = .scaleToFill
// using DeviceKit from https://github.com/devicekit/DeviceKit
// replace with your lookup code that gets
// the device's ppi
let device = Device.current
guard let ppi = device.ppi else { return }
// convert to pixels-per-millimeter
let ppmm = CGFloat(ppi) / 25.4
// we want 15mm size
let mm15 = 15.0 * ppmm
// convert based on screen scale
let mmScale = mm15 / UIScreen.main.scale
// update our intrinsic size
self.qrIntrinsicSize = CGSize(width: mmScale, height: mmScale)
}
override func layoutSubviews() {
super.layoutSubviews()
generateCode(content)
}
func generateCode(_ string: String) {
guard let filter = filter,
let data = string.data(using: .isoLatin1, allowLossyConversion: false) else {
return
}
filter.setValue(data, forKey: "inputMessage")
guard let ciImage = filter.outputImage else {
return
}
let scX = bounds.width / ciImage.extent.size.width
let scY = bounds.height / ciImage.extent.size.height
let transform = CGAffineTransform(scaleX: scX, y: scY)
let scaled = UIImage(ciImage: ciImage.transformed(by: transform))
self.image = scaled
}
}