如何制作一个自适应大小的 UiImageView?
How to make a Self-sizing UiImageView?
我需要一个可以重复使用的简单 QR 码 class。我已经创建了 class 并且它可以工作,但是不需要手动设置大小限制,因为它需要根据设备的 DPI 调整其大小。在这个最小的示例中,我只使用 100,因为大小计算代码不相关(在 IB 中设置为 50)。此外,我将在不同位置放置多个二维码,我将通过 IB 管理它们的定位。但至少我希望能够在代码中设置宽高限制。
下面的代码显示了一个 QR 码,大小正确(在运行时设置),但是当约束设置为水平和垂直居中时,它不会。同样,我不想要 IB 中的大小限制,但我确实想要 IB 中的位置限制
import Foundation
import UIKit
@IBDesignable class QrCodeView: UIImageView {
var content:String = "test" {
didSet {
generateCode(content)
}
}
lazy var filter = CIFilter(name: "CIQRCodeGenerator")
lazy var imageView = UIImageView()
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override func layoutSubviews() {
super.layoutSubviews()
imageView.frame = CGRect(x:0, y:0, width:100, height:100)
frame = CGRect(x:frame.origin.x, y:frame.origin.y, width:100, height:100)
}
func setup() {
//translatesAutoresizingMaskIntoConstraints = false
generateCode(content)
addSubview(imageView)
layoutIfNeeded()
}
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 transform = CGAffineTransform(scaleX: 10, y: 10)
let scaled = UIImage(ciImage: ciImage.transformed(by: transform))
imageView.image = scaled
}
}
我相信你让事情变得比需要的更复杂...
让我们从一个简单的 @IBDesignable UIImageView
subclass.
开始
从一个新项目开始并添加此代码:
@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
添加到视图控制器。将其自定义 class 设置为 MyImageView
并设置水平和垂直中心约束。
图像视图应自动将自身大小调整为 100 x 100
,以绿色背景的视图为中心(我们只是设置背景以便我们可以看到它):
运行 应用程序,您应该会看到相同的内容。
现在,将其作为 @IBOutlet
添加到视图控制器:
class ViewController: UIViewController {
@IBOutlet var testImageView: MyImageView!
override func viewDidLoad() {
super.viewDidLoad()
testImageView.myIntrinsicSize = CGSize(width: 300.0, height: 300.0)
}
}
运行 应用程序,您会看到居中的绿色图像视图,但现在它将是 300 x 300
点而不是 100 x 100
。
您的其余任务几乎就是在渲染 QRCode 图像后添加代码来设置此自定义 class 的 .image
属性。
这是习俗 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
}
}
在故事板/IB 中:
这是一个示例视图控制器:
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)
}
}
编辑
这是修改后的 QRCodeView
class,它将自身调整为(物理)15x15 毫米图像。
我使用 https://github.com/devicekit/DeviceKit 中的 DeviceKit
来获取当前设备的 ppi
。请参阅评论以将其替换为您自己的(假设您已经在使用其他东西)。
实例化此 class 时,它将:
- 获取当前设备的ppi
- 将 ppi 转换为每毫米像素
- 计算 15 x 每毫米像素
- 根据屏幕比例转换
- 更新其固有大小
QRCodeView
(UIImageView
的子class)只需要位置限制...因此您可以使用顶部 + 前导、顶部 + 尾随、中心 X 和 Y、底部+ CenterX 等等等等
@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
}
}
我需要一个可以重复使用的简单 QR 码 class。我已经创建了 class 并且它可以工作,但是不需要手动设置大小限制,因为它需要根据设备的 DPI 调整其大小。在这个最小的示例中,我只使用 100,因为大小计算代码不相关(在 IB 中设置为 50)。此外,我将在不同位置放置多个二维码,我将通过 IB 管理它们的定位。但至少我希望能够在代码中设置宽高限制。
下面的代码显示了一个 QR 码,大小正确(在运行时设置),但是当约束设置为水平和垂直居中时,它不会。同样,我不想要 IB 中的大小限制,但我确实想要 IB 中的位置限制
import Foundation
import UIKit
@IBDesignable class QrCodeView: UIImageView {
var content:String = "test" {
didSet {
generateCode(content)
}
}
lazy var filter = CIFilter(name: "CIQRCodeGenerator")
lazy var imageView = UIImageView()
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override func layoutSubviews() {
super.layoutSubviews()
imageView.frame = CGRect(x:0, y:0, width:100, height:100)
frame = CGRect(x:frame.origin.x, y:frame.origin.y, width:100, height:100)
}
func setup() {
//translatesAutoresizingMaskIntoConstraints = false
generateCode(content)
addSubview(imageView)
layoutIfNeeded()
}
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 transform = CGAffineTransform(scaleX: 10, y: 10)
let scaled = UIImage(ciImage: ciImage.transformed(by: transform))
imageView.image = scaled
}
}
我相信你让事情变得比需要的更复杂...
让我们从一个简单的 @IBDesignable UIImageView
subclass.
从一个新项目开始并添加此代码:
@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
添加到视图控制器。将其自定义 class 设置为 MyImageView
并设置水平和垂直中心约束。
图像视图应自动将自身大小调整为 100 x 100
,以绿色背景的视图为中心(我们只是设置背景以便我们可以看到它):
运行 应用程序,您应该会看到相同的内容。
现在,将其作为 @IBOutlet
添加到视图控制器:
class ViewController: UIViewController {
@IBOutlet var testImageView: MyImageView!
override func viewDidLoad() {
super.viewDidLoad()
testImageView.myIntrinsicSize = CGSize(width: 300.0, height: 300.0)
}
}
运行 应用程序,您会看到居中的绿色图像视图,但现在它将是 300 x 300
点而不是 100 x 100
。
您的其余任务几乎就是在渲染 QRCode 图像后添加代码来设置此自定义 class 的 .image
属性。
这是习俗 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
}
}
在故事板/IB 中:
这是一个示例视图控制器:
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)
}
}
编辑
这是修改后的 QRCodeView
class,它将自身调整为(物理)15x15 毫米图像。
我使用 https://github.com/devicekit/DeviceKit 中的 DeviceKit
来获取当前设备的 ppi
。请参阅评论以将其替换为您自己的(假设您已经在使用其他东西)。
实例化此 class 时,它将:
- 获取当前设备的ppi
- 将 ppi 转换为每毫米像素
- 计算 15 x 每毫米像素
- 根据屏幕比例转换
- 更新其固有大小
QRCodeView
(UIImageView
的子class)只需要位置限制...因此您可以使用顶部 + 前导、顶部 + 尾随、中心 X 和 Y、底部+ CenterX 等等等等
@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
}
}