从 UIImageView 中删除多余的 space
Remove extra space from UIImageView
在我的 UIView 中,我有一种方法可以从 base64(来自 API)加载图像并在 UIImageView 中配置它。可以想象,API 返回了不同大小的图像。我正在使用具有本机约束(以编程方式)和 UIImageView contentMode = .scaleAspectFit 的自动布局。某些图像在 UIImageView 中有额外的 space。这里有一些截图(背景是青色的,所以我可以看到我想删除的额外 spaces)。
我的约束:
assetImageView.topAnchor.constraint(equalTo: typeOrderLabel.bottomAnchor, constant: 14.7),
assetImageView.leadingAnchor.constraint(equalTo: typeOrderLabel.leadingAnchor),
lazy var assetImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
imageView.backgroundColor = .cyan
return imageView
}()
如果您希望图像视图的最大尺寸为 100 x 30
,您需要在单元格中创建宽度和高度约束 class:
var cWidth: NSLayoutConstraint!
var cHeight: NSLayoutConstraint!
然后在您单元格的 init 中初始化它们:
// initialize image view Width and Height constraints
cWidth = assetImageView.widthAnchor.constraint(equalToConstant: 0)
cHeight = assetImageView.heightAnchor.constraint(equalToConstant: 0)
NSLayoutConstraint.activate([
// label constraints...
assetImageView.topAnchor.constraint(equalTo: typeOrderLabel.bottomAnchor, constant: 15.0),
assetImageView.leadingAnchor.constraint(equalTo: typeOrderLabel.leadingAnchor),
// activate image view Width and Height constraints
cWidth,
cHeight,
])
然后,当您在 cellForRowAt
中设置图像时,根据图像的大小计算大小并更新 Width 和 Height 约束常量:
assetImageView.image = img
if img.size.height <= maxHeight && img.size.width <= maxWidth {
// image height and width are smaller than max Height and Width
// so use actual size
cWidth.constant = img.size.width
cHeight.constant = img.size.height
} else {
// use standard Aspect Fit calculation
var f = maxWidth / img.size.width
var w = img.size.width * f
var h = img.size.height * f
if h > maxHeight {
f = maxHeight / img.size.height
h = img.size.height * f
w = img.size.width * f
}
// update constraints
cWidth.constant = w
cHeight.constant = h
}
这是一个完整的例子:
struct EduardoStruct {
var typeOrder: String = ""
var other: String = ""
var asset: String = ""
}
class EduardoCell: UITableViewCell {
let typeOrderLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.font = .systemFont(ofSize: 14, weight: .bold)
v.textColor = .systemGreen
// if we want to see the label frame
//v.backgroundColor = .yellow
return v
}()
let assetImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleToFill
imageView.backgroundColor = .cyan
return imageView
}()
let otherLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.font = .systemFont(ofSize: 13, weight: .thin)
v.textColor = .darkGray
// if we want to see the label frame
//v.backgroundColor = .yellow
return v
}()
var cWidth: NSLayoutConstraint!
var cHeight: NSLayoutConstraint!
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
// add the subviews
contentView.addSubview(typeOrderLabel)
contentView.addSubview(assetImageView)
contentView.addSubview(otherLabel)
// initialize image view Width and Height constraints
cWidth = assetImageView.widthAnchor.constraint(equalToConstant: 0)
cHeight = assetImageView.heightAnchor.constraint(equalToConstant: 0)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
typeOrderLabel.topAnchor.constraint(equalTo: g.topAnchor),
typeOrderLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor),
typeOrderLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor),
assetImageView.topAnchor.constraint(equalTo: typeOrderLabel.bottomAnchor, constant: 15.0),
assetImageView.leadingAnchor.constraint(equalTo: typeOrderLabel.leadingAnchor),
otherLabel.topAnchor.constraint(equalTo: assetImageView.bottomAnchor, constant: 10.0),
otherLabel.leadingAnchor.constraint(equalTo: typeOrderLabel.leadingAnchor),
otherLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor),
otherLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor),
// activate image view Width and Height constraints
cWidth,
cHeight,
])
}
func fillData(_ st: EduardoStruct) {
typeOrderLabel.text = st.typeOrder
otherLabel.text = st.other
// image max Width and Height
let maxWidth: CGFloat = 100
let maxHeight: CGFloat = 30
guard let img = UIImage(named: st.asset) else {
// if we can't load the image, set the image view
// to maxWidth x maxHeight
cWidth.constant = maxWidth
cHeight.constant = maxHeight
assetImageView.image = nil
return
}
assetImageView.image = img
if img.size.height <= maxHeight && img.size.width <= maxWidth {
// image height and width are smaller than max Height and Width
// so use actual size
cWidth.constant = img.size.width
cHeight.constant = img.size.height
} else {
// use standard Aspect Fit calculation
var f = maxWidth / img.size.width
var w = img.size.width * f
var h = img.size.height * f
if h > maxHeight {
f = maxHeight / img.size.height
h = img.size.height * f
w = img.size.width * f
}
// update constraints
cWidth.constant = w
cHeight.constant = h
}
}
}
class EduardoExampleTableViewController: UITableViewController {
var myData: [EduardoStruct] = []
override func viewDidLoad() {
super.viewDidLoad()
let imageNames: [String] = [
"img100x30", "img80x20", "img120x50", "img80x80", "img150x30", "img120x120",
]
for (i, str) in imageNames.enumerated() {
var st: EduardoStruct = EduardoStruct()
st.typeOrder = "TYPE \(i)"
st.other = "OTHER \(i)"
st.asset = str
myData.append(st)
}
tableView.register(EduardoCell.self, forCellReuseIdentifier: "cell")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! EduardoCell
cell.fillData(myData[indexPath.row])
return cell
}
}
使用该代码,使用这些“资产图像”:
我们得到这个结果:
如您所见,所有图像都是“纵横比”缩放到最大尺寸 100 x 30
,没有“额外 space”。“=30=]
编辑
在 OP 澄清后,图像将是 160 x 55
像素,目标是:
- 仅提取图像的“有用”部分(non-alpha 部分)
- 以
100 x 30
的最大尺寸显示该部分,同时保持宽高比。
因此,有了一组新图像,每张图像都 160 x 55
具有透明“背景”(下载这些图像以查看):
我们可以使用这个 UIImage
扩展来“剪掉” non-transparent 部分:
extension UIImage {
func clipAlpha(_ tolerancePercent: Double) -> UIImage {
guard let imageRef = self.cgImage else {
return self
}
let columns = imageRef.width
let rows = imageRef.height
let bytesPerPixel = 4
let bytesPerRow = bytesPerPixel * columns
let bitmapByteCount = bytesPerRow * rows
// allocate memory
let rawData = UnsafeMutablePointer<UInt8>.allocate(capacity: bitmapByteCount)
// initialize buffer to Zeroes
rawData.initialize(repeating: 0, count: bitmapByteCount)
defer {
rawData.deallocate()
}
guard let colorSpace = CGColorSpace(name: CGColorSpace.genericRGBLinear) else {
return self
}
guard let context = CGContext(
data: rawData,
width: columns,
height: rows,
bitsPerComponent: 8,
bytesPerRow: bytesPerRow,
space: colorSpace,
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
| CGBitmapInfo.byteOrder32Big.rawValue
) else {
return self
}
var l: Int = -1
var r: Int = -1
var t: Int = -1
var b: Int = -1
// for debugging...
// used to count the number of iterations needed
// to find the non-alpha bounding box
//var c: Int = 0
var colOffset: Int = 0
var rowOffset: Int = 0
// Draw source image on created context.
let rc = CGRect(x: 0, y: 0, width: columns, height: rows)
context.draw(imageRef, in: rc)
let tolerance: Int = Int(255.0 * tolerancePercent)
// find the left-most non-alpha pixel
for col in 0..<columns {
colOffset = col * bytesPerPixel
for row in 0..<rows {
// debugging
//c += 1
rowOffset = row * bytesPerRow
// Get alpha of current pixel
let alpha = CGFloat(rawData[colOffset + rowOffset + 3])
if alpha > CGFloat(tolerance) {
l = col
break
}
}
if l > -1 {
break
}
}
// find the right-most non-alpha pixel
for col in stride(from: columns - 1, to: l, by: -1) {
colOffset = col * bytesPerPixel
for row in 0..<rows {
// debugging
//c += 1
rowOffset = row * bytesPerRow
// Get alpha of current pixel
let alpha = CGFloat(rawData[colOffset + rowOffset + 3])
if alpha > CGFloat(tolerance) {
r = col
break
}
}
if r > -1 {
break
}
}
// find the top-most non-alpha pixel
for row in 0..<rows {
rowOffset = row * bytesPerRow
for col in l..<r {
// debugging
//c += 1
colOffset = col * bytesPerPixel
// Get alpha of current pixel
let alpha = CGFloat(rawData[colOffset + rowOffset + 3])
if alpha > CGFloat(tolerance) {
t = row
break
}
}
if t > -1 {
break
}
}
// find the bottom-most non-alpha pixel
for row in stride(from: rows - 1, to: t, by: -1) {
rowOffset = row * bytesPerRow
for col in l..<r {
// debugging
//c += 1
colOffset = col * bytesPerPixel
// Get alpha of current pixel
let alpha = CGFloat(rawData[colOffset + rowOffset + 3])
if alpha > CGFloat(tolerance) {
b = row
break
}
}
if b > -1 {
break
}
}
// debugging
//print(c, l, t, r, b)
// define a rectangle for the non-alpha pixels
let targetRect = CGRect(x: l, y: t, width: r - l + 1, height: b - t + 1)
let size = targetRect.size
let renderer = UIGraphicsImageRenderer(size: size)
let renderedImage = renderer.image { _ in
// render the non-alpha portion
self.draw(at: CGPoint(x: -targetRect.origin.x, y: -targetRect.origin.y))
}
return renderedImage
}
}
然后,我们将首先“剪辑”它,然后应用前面的 aspect-sizing 代码,而不是直接使用图像。
这是完整的示例(仅对原始帖子稍作修改):
struct EduardoStruct {
var typeOrder: String = ""
var other: String = ""
var asset: String = ""
}
class EduardoCell: UITableViewCell {
let typeOrderLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.font = .systemFont(ofSize: 14, weight: .bold)
v.textColor = .systemGreen
// if we want to see the label frame
//v.backgroundColor = .yellow
return v
}()
let assetImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleToFill
imageView.backgroundColor = .cyan
return imageView
}()
let otherLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.font = .systemFont(ofSize: 13, weight: .thin)
v.textColor = .darkGray
// if we want to see the label frame
//v.backgroundColor = .yellow
return v
}()
var cWidth: NSLayoutConstraint!
var cHeight: NSLayoutConstraint!
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
// add the subviews
contentView.addSubview(typeOrderLabel)
contentView.addSubview(assetImageView)
contentView.addSubview(otherLabel)
// initialize image view Width and Height constraints
cWidth = assetImageView.widthAnchor.constraint(equalToConstant: 0)
cHeight = assetImageView.heightAnchor.constraint(equalToConstant: 0)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
typeOrderLabel.topAnchor.constraint(equalTo: g.topAnchor),
typeOrderLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor),
typeOrderLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor),
assetImageView.topAnchor.constraint(equalTo: typeOrderLabel.bottomAnchor, constant: 15.0),
assetImageView.leadingAnchor.constraint(equalTo: typeOrderLabel.leadingAnchor),
otherLabel.topAnchor.constraint(equalTo: assetImageView.bottomAnchor, constant: 10.0),
otherLabel.leadingAnchor.constraint(equalTo: typeOrderLabel.leadingAnchor),
otherLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor),
otherLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor),
// activate image view Width and Height constraints
cWidth,
cHeight,
])
}
func fillData(_ st: EduardoStruct, showBKG: Bool) {
typeOrderLabel.text = st.typeOrder
otherLabel.text = st.other
// image max Width and Height
let maxWidth: CGFloat = 100
let maxHeight: CGFloat = 30
assetImageView.backgroundColor = showBKG ? .cyan : .clear
guard let origImg = UIImage(named: st.asset) else {
// if we can't load the image, set the image view
// to maxWidth x maxHeight
cWidth.constant = maxWidth
cHeight.constant = maxHeight
assetImageView.image = nil
return
}
let img = origImg.clipAlpha(0.0)
assetImageView.image = img
if img.size.height <= maxHeight && img.size.width <= maxWidth {
// image height and width are smaller than max Height and Width
// so use actual size
cWidth.constant = img.size.width
cHeight.constant = img.size.height
} else {
// use standard Aspect Fit calculation
var f = maxWidth / img.size.width
var w = img.size.width * f
var h = img.size.height * f
if h > maxHeight {
f = maxHeight / img.size.height
h = img.size.height * f
w = img.size.width * f
}
// update constraints
cWidth.constant = w
cHeight.constant = h
}
}
}
class TestSizingCellTableViewController: UITableViewController {
var myData: [EduardoStruct] = []
override func viewDidLoad() {
super.viewDidLoad()
let imageNames: [String] = [
"img1", "img2", "img3", "img4", "img5",
]
for (i, str) in imageNames.enumerated() {
var st: EduardoStruct = EduardoStruct()
st.typeOrder = "TYPE \(i)"
st.other = "OTHER \(i)"
st.asset = str
myData.append(st)
}
tableView.register(EduardoCell.self, forCellReuseIdentifier: "cell")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! EduardoCell
// set cell's imageView background to cyan or clear
let showBKG = false
cell.fillData(myData[indexPath.row], showBKG: showBKG)
return cell
}
}
以及输出——首先将 imageView 背景设置为 .cyan
(以便我们可以看到实际帧):
并将 imageView 背景设置为 .clear
:
在我的 UIView 中,我有一种方法可以从 base64(来自 API)加载图像并在 UIImageView 中配置它。可以想象,API 返回了不同大小的图像。我正在使用具有本机约束(以编程方式)和 UIImageView contentMode = .scaleAspectFit 的自动布局。某些图像在 UIImageView 中有额外的 space。这里有一些截图(背景是青色的,所以我可以看到我想删除的额外 spaces)。
我的约束:
assetImageView.topAnchor.constraint(equalTo: typeOrderLabel.bottomAnchor, constant: 14.7),
assetImageView.leadingAnchor.constraint(equalTo: typeOrderLabel.leadingAnchor),
lazy var assetImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
imageView.backgroundColor = .cyan
return imageView
}()
如果您希望图像视图的最大尺寸为 100 x 30
,您需要在单元格中创建宽度和高度约束 class:
var cWidth: NSLayoutConstraint!
var cHeight: NSLayoutConstraint!
然后在您单元格的 init 中初始化它们:
// initialize image view Width and Height constraints
cWidth = assetImageView.widthAnchor.constraint(equalToConstant: 0)
cHeight = assetImageView.heightAnchor.constraint(equalToConstant: 0)
NSLayoutConstraint.activate([
// label constraints...
assetImageView.topAnchor.constraint(equalTo: typeOrderLabel.bottomAnchor, constant: 15.0),
assetImageView.leadingAnchor.constraint(equalTo: typeOrderLabel.leadingAnchor),
// activate image view Width and Height constraints
cWidth,
cHeight,
])
然后,当您在 cellForRowAt
中设置图像时,根据图像的大小计算大小并更新 Width 和 Height 约束常量:
assetImageView.image = img
if img.size.height <= maxHeight && img.size.width <= maxWidth {
// image height and width are smaller than max Height and Width
// so use actual size
cWidth.constant = img.size.width
cHeight.constant = img.size.height
} else {
// use standard Aspect Fit calculation
var f = maxWidth / img.size.width
var w = img.size.width * f
var h = img.size.height * f
if h > maxHeight {
f = maxHeight / img.size.height
h = img.size.height * f
w = img.size.width * f
}
// update constraints
cWidth.constant = w
cHeight.constant = h
}
这是一个完整的例子:
struct EduardoStruct {
var typeOrder: String = ""
var other: String = ""
var asset: String = ""
}
class EduardoCell: UITableViewCell {
let typeOrderLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.font = .systemFont(ofSize: 14, weight: .bold)
v.textColor = .systemGreen
// if we want to see the label frame
//v.backgroundColor = .yellow
return v
}()
let assetImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleToFill
imageView.backgroundColor = .cyan
return imageView
}()
let otherLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.font = .systemFont(ofSize: 13, weight: .thin)
v.textColor = .darkGray
// if we want to see the label frame
//v.backgroundColor = .yellow
return v
}()
var cWidth: NSLayoutConstraint!
var cHeight: NSLayoutConstraint!
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
// add the subviews
contentView.addSubview(typeOrderLabel)
contentView.addSubview(assetImageView)
contentView.addSubview(otherLabel)
// initialize image view Width and Height constraints
cWidth = assetImageView.widthAnchor.constraint(equalToConstant: 0)
cHeight = assetImageView.heightAnchor.constraint(equalToConstant: 0)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
typeOrderLabel.topAnchor.constraint(equalTo: g.topAnchor),
typeOrderLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor),
typeOrderLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor),
assetImageView.topAnchor.constraint(equalTo: typeOrderLabel.bottomAnchor, constant: 15.0),
assetImageView.leadingAnchor.constraint(equalTo: typeOrderLabel.leadingAnchor),
otherLabel.topAnchor.constraint(equalTo: assetImageView.bottomAnchor, constant: 10.0),
otherLabel.leadingAnchor.constraint(equalTo: typeOrderLabel.leadingAnchor),
otherLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor),
otherLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor),
// activate image view Width and Height constraints
cWidth,
cHeight,
])
}
func fillData(_ st: EduardoStruct) {
typeOrderLabel.text = st.typeOrder
otherLabel.text = st.other
// image max Width and Height
let maxWidth: CGFloat = 100
let maxHeight: CGFloat = 30
guard let img = UIImage(named: st.asset) else {
// if we can't load the image, set the image view
// to maxWidth x maxHeight
cWidth.constant = maxWidth
cHeight.constant = maxHeight
assetImageView.image = nil
return
}
assetImageView.image = img
if img.size.height <= maxHeight && img.size.width <= maxWidth {
// image height and width are smaller than max Height and Width
// so use actual size
cWidth.constant = img.size.width
cHeight.constant = img.size.height
} else {
// use standard Aspect Fit calculation
var f = maxWidth / img.size.width
var w = img.size.width * f
var h = img.size.height * f
if h > maxHeight {
f = maxHeight / img.size.height
h = img.size.height * f
w = img.size.width * f
}
// update constraints
cWidth.constant = w
cHeight.constant = h
}
}
}
class EduardoExampleTableViewController: UITableViewController {
var myData: [EduardoStruct] = []
override func viewDidLoad() {
super.viewDidLoad()
let imageNames: [String] = [
"img100x30", "img80x20", "img120x50", "img80x80", "img150x30", "img120x120",
]
for (i, str) in imageNames.enumerated() {
var st: EduardoStruct = EduardoStruct()
st.typeOrder = "TYPE \(i)"
st.other = "OTHER \(i)"
st.asset = str
myData.append(st)
}
tableView.register(EduardoCell.self, forCellReuseIdentifier: "cell")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! EduardoCell
cell.fillData(myData[indexPath.row])
return cell
}
}
使用该代码,使用这些“资产图像”:
我们得到这个结果:
如您所见,所有图像都是“纵横比”缩放到最大尺寸 100 x 30
,没有“额外 space”。“=30=]
编辑
在 OP 澄清后,图像将是 160 x 55
像素,目标是:
- 仅提取图像的“有用”部分(non-alpha 部分)
- 以
100 x 30
的最大尺寸显示该部分,同时保持宽高比。
因此,有了一组新图像,每张图像都 160 x 55
具有透明“背景”(下载这些图像以查看):
我们可以使用这个 UIImage
扩展来“剪掉” non-transparent 部分:
extension UIImage {
func clipAlpha(_ tolerancePercent: Double) -> UIImage {
guard let imageRef = self.cgImage else {
return self
}
let columns = imageRef.width
let rows = imageRef.height
let bytesPerPixel = 4
let bytesPerRow = bytesPerPixel * columns
let bitmapByteCount = bytesPerRow * rows
// allocate memory
let rawData = UnsafeMutablePointer<UInt8>.allocate(capacity: bitmapByteCount)
// initialize buffer to Zeroes
rawData.initialize(repeating: 0, count: bitmapByteCount)
defer {
rawData.deallocate()
}
guard let colorSpace = CGColorSpace(name: CGColorSpace.genericRGBLinear) else {
return self
}
guard let context = CGContext(
data: rawData,
width: columns,
height: rows,
bitsPerComponent: 8,
bytesPerRow: bytesPerRow,
space: colorSpace,
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
| CGBitmapInfo.byteOrder32Big.rawValue
) else {
return self
}
var l: Int = -1
var r: Int = -1
var t: Int = -1
var b: Int = -1
// for debugging...
// used to count the number of iterations needed
// to find the non-alpha bounding box
//var c: Int = 0
var colOffset: Int = 0
var rowOffset: Int = 0
// Draw source image on created context.
let rc = CGRect(x: 0, y: 0, width: columns, height: rows)
context.draw(imageRef, in: rc)
let tolerance: Int = Int(255.0 * tolerancePercent)
// find the left-most non-alpha pixel
for col in 0..<columns {
colOffset = col * bytesPerPixel
for row in 0..<rows {
// debugging
//c += 1
rowOffset = row * bytesPerRow
// Get alpha of current pixel
let alpha = CGFloat(rawData[colOffset + rowOffset + 3])
if alpha > CGFloat(tolerance) {
l = col
break
}
}
if l > -1 {
break
}
}
// find the right-most non-alpha pixel
for col in stride(from: columns - 1, to: l, by: -1) {
colOffset = col * bytesPerPixel
for row in 0..<rows {
// debugging
//c += 1
rowOffset = row * bytesPerRow
// Get alpha of current pixel
let alpha = CGFloat(rawData[colOffset + rowOffset + 3])
if alpha > CGFloat(tolerance) {
r = col
break
}
}
if r > -1 {
break
}
}
// find the top-most non-alpha pixel
for row in 0..<rows {
rowOffset = row * bytesPerRow
for col in l..<r {
// debugging
//c += 1
colOffset = col * bytesPerPixel
// Get alpha of current pixel
let alpha = CGFloat(rawData[colOffset + rowOffset + 3])
if alpha > CGFloat(tolerance) {
t = row
break
}
}
if t > -1 {
break
}
}
// find the bottom-most non-alpha pixel
for row in stride(from: rows - 1, to: t, by: -1) {
rowOffset = row * bytesPerRow
for col in l..<r {
// debugging
//c += 1
colOffset = col * bytesPerPixel
// Get alpha of current pixel
let alpha = CGFloat(rawData[colOffset + rowOffset + 3])
if alpha > CGFloat(tolerance) {
b = row
break
}
}
if b > -1 {
break
}
}
// debugging
//print(c, l, t, r, b)
// define a rectangle for the non-alpha pixels
let targetRect = CGRect(x: l, y: t, width: r - l + 1, height: b - t + 1)
let size = targetRect.size
let renderer = UIGraphicsImageRenderer(size: size)
let renderedImage = renderer.image { _ in
// render the non-alpha portion
self.draw(at: CGPoint(x: -targetRect.origin.x, y: -targetRect.origin.y))
}
return renderedImage
}
}
然后,我们将首先“剪辑”它,然后应用前面的 aspect-sizing 代码,而不是直接使用图像。
这是完整的示例(仅对原始帖子稍作修改):
struct EduardoStruct {
var typeOrder: String = ""
var other: String = ""
var asset: String = ""
}
class EduardoCell: UITableViewCell {
let typeOrderLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.font = .systemFont(ofSize: 14, weight: .bold)
v.textColor = .systemGreen
// if we want to see the label frame
//v.backgroundColor = .yellow
return v
}()
let assetImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleToFill
imageView.backgroundColor = .cyan
return imageView
}()
let otherLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
v.font = .systemFont(ofSize: 13, weight: .thin)
v.textColor = .darkGray
// if we want to see the label frame
//v.backgroundColor = .yellow
return v
}()
var cWidth: NSLayoutConstraint!
var cHeight: NSLayoutConstraint!
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
// add the subviews
contentView.addSubview(typeOrderLabel)
contentView.addSubview(assetImageView)
contentView.addSubview(otherLabel)
// initialize image view Width and Height constraints
cWidth = assetImageView.widthAnchor.constraint(equalToConstant: 0)
cHeight = assetImageView.heightAnchor.constraint(equalToConstant: 0)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
typeOrderLabel.topAnchor.constraint(equalTo: g.topAnchor),
typeOrderLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor),
typeOrderLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor),
assetImageView.topAnchor.constraint(equalTo: typeOrderLabel.bottomAnchor, constant: 15.0),
assetImageView.leadingAnchor.constraint(equalTo: typeOrderLabel.leadingAnchor),
otherLabel.topAnchor.constraint(equalTo: assetImageView.bottomAnchor, constant: 10.0),
otherLabel.leadingAnchor.constraint(equalTo: typeOrderLabel.leadingAnchor),
otherLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor),
otherLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor),
// activate image view Width and Height constraints
cWidth,
cHeight,
])
}
func fillData(_ st: EduardoStruct, showBKG: Bool) {
typeOrderLabel.text = st.typeOrder
otherLabel.text = st.other
// image max Width and Height
let maxWidth: CGFloat = 100
let maxHeight: CGFloat = 30
assetImageView.backgroundColor = showBKG ? .cyan : .clear
guard let origImg = UIImage(named: st.asset) else {
// if we can't load the image, set the image view
// to maxWidth x maxHeight
cWidth.constant = maxWidth
cHeight.constant = maxHeight
assetImageView.image = nil
return
}
let img = origImg.clipAlpha(0.0)
assetImageView.image = img
if img.size.height <= maxHeight && img.size.width <= maxWidth {
// image height and width are smaller than max Height and Width
// so use actual size
cWidth.constant = img.size.width
cHeight.constant = img.size.height
} else {
// use standard Aspect Fit calculation
var f = maxWidth / img.size.width
var w = img.size.width * f
var h = img.size.height * f
if h > maxHeight {
f = maxHeight / img.size.height
h = img.size.height * f
w = img.size.width * f
}
// update constraints
cWidth.constant = w
cHeight.constant = h
}
}
}
class TestSizingCellTableViewController: UITableViewController {
var myData: [EduardoStruct] = []
override func viewDidLoad() {
super.viewDidLoad()
let imageNames: [String] = [
"img1", "img2", "img3", "img4", "img5",
]
for (i, str) in imageNames.enumerated() {
var st: EduardoStruct = EduardoStruct()
st.typeOrder = "TYPE \(i)"
st.other = "OTHER \(i)"
st.asset = str
myData.append(st)
}
tableView.register(EduardoCell.self, forCellReuseIdentifier: "cell")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! EduardoCell
// set cell's imageView background to cyan or clear
let showBKG = false
cell.fillData(myData[indexPath.row], showBKG: showBKG)
return cell
}
}
以及输出——首先将 imageView 背景设置为 .cyan
(以便我们可以看到实际帧):
并将 imageView 背景设置为 .clear
: