StackView中调整图片保持宽高比,克隆Apple Maps按钮遇图标变形问题
I get exactly what you're dealing with—those SF Symbols are way too big for your small button frames, and the scaling hack you tried ended up warping the icons. Let's break down what's going wrong and fix this properly:
The Root of the Problem
- When you directly assign
UIImage(systemName: systemName)to the UIImageView, it uses the symbol's default size, which is way too large for your 10x10 frames. - The commented-out rendering code relied on
self.frame(which might not be properly sized at init time) and fixed inset values that were way too extreme for your tiny views, leading to squashed, distorted icons.
Here's the Fixed Solution
We'll use Apple's official way to resize SF Symbols (no distortion guaranteed) and tweak the image view's layout to keep icons centered and sharp.
Updated CustomView Code
class CustomView: UIImageView { init(frame: CGRect, corners: CACornerMask, systemName: String) { super.init(frame: frame) createBorders(corners: corners) createImage(systemName: systemName) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func createBorders(corners: CACornerMask) { self.contentMode = .center // Changed from .scaleAspectFill to keep icon centered without stretching self.clipsToBounds = false self.layer.cornerRadius = 10 self.layer.maskedCorners = corners self.layer.masksToBounds = false self.layer.shadowOffset = .zero self.layer.shadowColor = UIColor.gray.cgColor self.layer.shadowRadius = 10 self.layer.shadowOpacity = 0.2 self.backgroundColor = .white // Fix shadow path to use bounds instead of frame (more reliable for view layout) let shadowAmount: CGFloat = 2 let rect = bounds.insetBy(dx: -shadowAmount * 0.1, dy: -shadowAmount * 0.1) self.layer.shadowPath = UIBezierPath(rect: rect).cgPath } func createImage(systemName: String) { // Use SymbolConfiguration to set exact, distortion-free icon size let config = UIImage.SymbolConfiguration(pointSize: 8, weight: .medium) // Adjust size to fit your button guard let image = UIImage(systemName: systemName, withConfiguration: config) else { return } // Optional: Enable tinting to match Apple Maps' style self.image = image.withRenderingMode(.alwaysTemplate) self.tintColor = .darkGray // Set your preferred icon color here } }
Key Improvements:
contentMode = .center: Ensures the icon stays centered without being forced to stretch to fill the entire image view.UIImage.SymbolConfiguration: This is Apple's official method to resize SF Symbols—since they're vector-based, this keeps icons sharp at any size with zero distortion.- Shadow path uses
bounds: Bounds are relative to the view itself, making the shadow path more reliable than usingframe(which depends on the view's position in its parent). - Tinting support: Added
withRenderingMode(.alwaysTemplate)so you can easily adjust the icon color withtintColor, matching Apple Maps' subtle styling.
Quick Adjustment to Your Usage Code
Your 10x10 button size is extremely small—you might want to bump it up a bit to make the icons usable. For example:
let size = CGSize(width: 32, height: 32) // Larger size for better readability let frame = CGRect(origin: .zero, size: size) // StackView handles positioning, so origin can be zero let infoView = CustomView(frame: frame, corners: [.layerMinXMinYCorner, .layerMaxXMinYCorner], systemName: "info.circle") let locationView = CustomView(frame: frame, corners: [], systemName: "location") let plusView = CustomView(frame: frame, corners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner], systemName: "plus.circle") let binocularsView = CustomView(frame: frame, corners: .allCorners, systemName: "binoculars.fill") let stackView = UIStackView(arrangedSubviews: [infoView, locationView, plusView, binocularsView]) stackView.frame = CGRect(origin: CGPoint(x: 360, y: 45), size: CGSize(width: 45, height: 180)) stackView.distribution = .fillEqually stackView.axis = .vertical stackView.setCustomSpacing(3, after: plusView) view.addSubview(stackView)
Why This Works
SF Symbols are designed to be resized via SymbolConfiguration—this method maintains their proportionality and sharpness, unlike manual scaling or drawing hacks. Pairing this with .center content mode ensures the icon never gets stretched or squashed, exactly what you need for that Apple Maps button look.
内容的提问来源于stack exchange,提问作者user12473575

