当我们创建一个UIImage
使用 SF 符号,它会获得矢量支持,并且图像的行为更像是字体字符而不是图像。
如果我们跳过的话会更容易理解cornerRadius
目前。
例如,如果我将标签的文本设置为O
并给它一个黄色背景,它看起来像这样:
字符未到达边界框的边缘。
所以,当我们使用:let thisImage = UIImage(systemName: "person.crop.circle.fill")
并设置图像视图的图像,我们得到:
正如我们所看到的,所有 4 个边都有“填充”。
要“删除”填充,我们可以将CGImage
支持一个UIImage
...但我们需要记住一些事情。
因此,有 6 个示例 - 每个示例都是此“基本”控制器的子类:
class MyBaseVC: UIViewController {
let imgViewA = UIImageView()
let imgViewB = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
[imgViewA, imgViewB].forEach { v in
// use AspectFill
v.contentMode = .scaleAspectFill
// background color so we can see the framing
v.backgroundColor = .systemYellow
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
}
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
imgViewA.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
imgViewA.centerXAnchor.constraint(equalTo: g.centerXAnchor),
imgViewA.widthAnchor.constraint(equalToConstant: 160.0),
imgViewA.heightAnchor.constraint(equalTo: imgViewA.widthAnchor),
imgViewB.topAnchor.constraint(equalTo: imgViewA.bottomAnchor, constant: 20.0),
imgViewB.centerXAnchor.constraint(equalTo: g.centerXAnchor),
imgViewB.widthAnchor.constraint(equalToConstant: 160.0),
imgViewB.heightAnchor.constraint(equalTo: imgViewB.widthAnchor),
])
}
}
第一个示例仅显示带有空黄色背景图像视图的布局:
class Example1VC: MyBaseVC {
override func viewDidLoad() {
super.viewDidLoad()
}
}
它看起来像这样:
第二个示例从“person.crop.circle.fill”创建图像并设置第一个图像视图:
class Example2VC: MyBaseVC {
override func viewDidLoad() {
super.viewDidLoad()
let nm = "person.crop.circle.fill"
// create UIImage from SF Symbol at "default" size
guard let imgA = UIImage(systemName: nm)?.withTintColor(.lightGray, renderingMode: .alwaysOriginal) else {
fatalError("Could not load SF Symbol: \(nm)!")
}
print("imgA size:", imgA.size)
imgViewA.image = imgA
}
}
Output:
到目前为止,没有什么是你没见过的。
该代码还将生成的图像大小输出到调试控制台......在这种情况下,imgA size: (20.0, 19.0)
因此,我们将该图像保存为 20x19 png 并将其加载到第二个图像视图中:
class Example3VC: MyBaseVC {
override func viewDidLoad() {
super.viewDidLoad()
let nm = "person.crop.circle.fill"
// create UIImage from SF Symbol at "default" size
guard let imgA = UIImage(systemName: nm)?.withTintColor(.lightGray, renderingMode: .alwaysOriginal) else {
fatalError("Could not load SF Symbol: \(nm)!")
}
guard let imgB = UIImage(named: "imgA20x19") else {
fatalError("Could not load imgA20x19")
}
print("imgA:", imgA.size, "imgB:", imgB.size)
imgViewA.image = imgA
imgViewB.image = imgB
}
}
正如预期的那样,因为它现在是位图而不是矢量数据,所以它变得难以接受的模糊......而且,我们仍然没有到达边缘:
因此,对于第四个示例,我们将使用.cgImage
支持生成的 SF 符号图像中的数据,以有效地“即时”创建位图版本:
class Example4VC: MyBaseVC {
override func viewDidLoad() {
super.viewDidLoad()
let nm = "person.crop.circle.fill"
// create UIImage from SF Symbol at "default" size
guard let imgA = UIImage(systemName: nm)?.withTintColor(.lightGray, renderingMode: .alwaysOriginal) else {
fatalError("Could not load SF Symbol: \(nm)!")
}
// get a cgRef from imgA
guard let cgRef = imgA.cgImage else {
fatalError("Could not get cgImage!")
}
// create imgB from the cgRef
let imgB = UIImage(cgImage: cgRef, scale: imgA.scale, orientation: imgA.imageOrientation)
.withTintColor(.lightGray, renderingMode: .alwaysOriginal)
print("imgA:", imgA.size, "imgB:", imgB.size)
imgViewA.image = imgA
imgViewB.image = imgB
}
}
越来越近......图像现在到达边缘,但仍然模糊。
为了解决这个问题,我们在生成初始 SF 符号图像时将使用“点”配置:
class Example5VC: MyBaseVC {
override func viewDidLoad() {
super.viewDidLoad()
let nm = "person.crop.circle.fill"
// create UIImage from SF Symbol at "160-pts" size
let cfg = UIImage.SymbolConfiguration(pointSize: 160.0)
guard let imgA = UIImage(systemName: nm, withConfiguration: cfg)?.withTintColor(.lightGray, renderingMode: .alwaysOriginal) else {
fatalError("Could not load SF Symbol: \(nm)!")
}
// get a cgRef from imgA
guard let cgRef = imgA.cgImage else {
fatalError("Could not get cgImage!")
}
// create imgB from the cgRef
let imgB = UIImage(cgImage: cgRef, scale: imgA.scale, orientation: imgA.imageOrientation)
.withTintColor(.lightGray, renderingMode: .alwaysOriginal)
print("imgA:", imgA.size, "imgB:", imgB.size)
imgViewA.image = imgA
imgViewB.image = imgB
}
}
调试大小输出显示imgB: (159.5, 159.5)
(所以,非常接近图像视图的 160x160 尺寸),它看起来像这样:
我们现在有了清晰的渲染,没有“填充”......我们可以添加角半径和边框:
class Example6VC: MyBaseVC {
override func viewDidLoad() {
super.viewDidLoad()
let nm = "person.crop.circle.fill"
// create UIImage from SF Symbol at "160-pts" size
let cfg = UIImage.SymbolConfiguration(pointSize: 160.0)
guard let imgA = UIImage(systemName: nm, withConfiguration: cfg)?.withTintColor(.lightGray, renderingMode: .alwaysOriginal) else {
fatalError("Could not load SF Symbol: \(nm)!")
}
// get a cgRef from imgA
guard let cgRef = imgA.cgImage else {
fatalError("Could not get cgImage!")
}
// create imgB from the cgRef
let imgB = UIImage(cgImage: cgRef, scale: imgA.scale, orientation: imgA.imageOrientation)
.withTintColor(.lightGray, renderingMode: .alwaysOriginal)
print("imgA:", imgA.size, "imgB:", imgB.size)
imgViewA.image = imgA
imgViewB.image = imgB
[imgViewA, imgViewB].forEach { v in
v.layer.cornerRadius = 80
v.layer.borderColor = UIColor.red.cgColor
v.layer.borderWidth = 1
}
}
}
实施例6 结果:
正如您在帖子中提到的,您会注意到顶部图像视图并不完全是圆形的——也就是说,它以某种方式失去了 1:1 的比例。
这是使用 SF 符号作为图像的一个奇怪的副作用,我早已放弃尝试理解它。无论约束如何,不同的符号都会导致图像视图改变大小。
您可以在这里看到一些讨论:https://stackoverflow.com/a/66293917/6257435