You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

StackView中调整图片保持宽高比,克隆Apple Maps按钮遇图标变形问题

Fixing SF Symbol Scaling & Distortion in Your Apple Maps-Style Custom Buttons

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

  1. 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.
  2. 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 using frame (which depends on the view's position in its parent).
  • Tinting support: Added withRenderingMode(.alwaysTemplate) so you can easily adjust the icon color with tintColor, 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:06:12