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

StackView内SF-Symbols变形,复刻Apple Map按钮及添加分割线问题

Fixing SF Symbols Distortion and Adding Dividers for Apple Maps-Style Buttons

Hey there! Let's tackle those two issues with your custom Apple Maps-style buttons one by one. I'll walk you through the fixes step by step, with updated code and explanations.


1. Fix SF Symbols Distortion

The main culprits behind the symbol stretching are:

  • Using the initial frame for rendering instead of adapting to dynamic view size changes
  • Manually drawing the image with fixed insets that don't scale with the view
  • Using UIImageView directly as the container without proper layout control

Let's refactor the CustomView to use a container UIView with an internal UIImageView for the symbol. This gives us far better control over layout and scaling:

class CustomView: UIView {
    init(frame: CGRect, corners: CACornerMask, systemName: String) {
        super.init(frame: frame)
        setupView(corners: corners)
        setupSymbolImageView(systemName: systemName)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupView(corners: CACornerMask) {
        backgroundColor = .white
        layer.cornerRadius = 20
        layer.maskedCorners = corners
        layer.shadowOffset = .zero
        layer.shadowColor = UIColor.gray.cgColor
        layer.shadowRadius = 20
        layer.shadowOpacity = 0.2
        // Fix shadow path to use bounds (updates with view size changes)
        layer.shadowPath = UIBezierPath(rect: bounds.insetBy(dx: -1, dy: 2)).cgPath
        layer.masksToBounds = false
    }
    
    private func setupSymbolImageView(systemName: String) {
        let symbolImageView = UIImageView()
        symbolImageView.translatesAutoresizingMaskIntoConstraints = false
        symbolImageView.contentMode = .scaleAspectFit // Ensures symbol scales without stretching
        symbolImageView.tintColor = .black // Customize this to match your desired color
        symbolImageView.image = UIImage(systemName: systemName)?.withRenderingMode(.alwaysTemplate)
        
        addSubview(symbolImageView)
        
        // AutoLayout constraints to center the symbol with consistent padding
        NSLayoutConstraint.activate([
            symbolImageView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor, constant: 30),
            symbolImageView.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor, constant: -30),
            symbolImageView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: 30),
            symbolImageView.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor, constant: -30),
            symbolImageView.centerXAnchor.constraint(equalTo: centerXAnchor),
            symbolImageView.centerYAnchor.constraint(equalTo: centerYAnchor),
            // Optional: Prevent symbol from over-scaling on larger views
            symbolImageView.widthAnchor.constraint(lessThanOrEqualToConstant: 40),
            symbolImageView.heightAnchor.constraint(lessThanOrEqualToConstant: 40)
        ])
    }
    
    // Helper method to add bottom dividers (we'll use this next)
    func addBottomDivider() {
        let divider = UIView()
        divider.translatesAutoresizingMaskIntoConstraints = false
        divider.backgroundColor = UIColor.lightGray.withAlphaComponent(0.3) // Subtle divider color
        addSubview(divider)
        
        NSLayoutConstraint.activate([
            divider.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
            divider.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20),
            divider.bottomAnchor.constraint(equalTo: bottomAnchor),
            divider.heightAnchor.constraint(equalToConstant: 1)
        ])
    }
}

2. Add Subtle Dividers Between StackView Items

To match Apple Maps' divider style, we'll add a thin line to the bottom of each CustomView (except the last one). Here's how to update your StackView setup code:

let size = CGSize(width: 100, height: 100)
let frame = CGRect(origin: CGPoint(x: 100, y: 100), size: size)

let infoView = CustomView(frame: frame, corners: [.layerMinXMinYCorner, .layerMaxXMinYCorner], systemName: "info.circle")
infoView.addBottomDivider() // Add divider below this view

let locationView = CustomView(frame: frame, corners: [], systemName: "location")
locationView.addBottomDivider() // Add divider below this view

let twoDView = CustomView(frame: frame, corners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner], systemName: "view.2d")
twoDView.addBottomDivider() // Add divider below this view

let binocularsView = CustomView(frame: frame, corners: [.layerMinXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner], systemName: "binoculars.fill")
// No divider on the last item

let stackView = UIStackView(arrangedSubviews: [infoView, locationView, twoDView, binocularsView])
stackView.frame = CGRect(origin: CGPoint(x: 100, y: 100), size: CGSize(width: 100, height: 400))
stackView.distribution = .fillEqually
stackView.axis = .vertical
stackView.setCustomSpacing(0, after: twoDView) // Remove extra spacing—dividers are part of the views now
view.addSubview(stackView)

// Optional: Replace fixed frames with AutoLayout for better adaptability
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 100),
    stackView.topAnchor.constraint(equalTo: view.topAnchor, constant: 100),
    stackView.widthAnchor.constraint(equalToConstant: 100),
    stackView.heightAnchor.constraint(equalToConstant: 400)
])

Key Fixes Recap

  • Symbol Distortion: Using an internal UIImageView with scaleAspectFit and AutoLayout ensures the symbol scales proportionally, no matter how the CustomView resizes in the StackView. The optional maximum size constraint prevents the symbol from becoming too large.
  • Dividers: The addBottomDivider method adds a subtle, semi-transparent line at the bottom of each view (except the last one), exactly matching Apple Maps' divider style. We also removed the extra spacing between views since dividers are now integrated into the button views themselves.

内容的提问来源于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:22:09