首页 > 解决方案 > stackView 内容(SF-Symbols)扭曲

问题描述

我想复制 Apple Map App Buttons,如下所示: 截屏

这是到目前为止的代码:

class CustomView: UIImageView {
init(frame: CGRect, corners: CACornerMask, systemName: String) {
    super.init(frame: frame)
    self.createBorders(corners: corners)
    self.createImage(systemName: systemName)
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

func createBorders(corners: CACornerMask) {
    self.contentMode = .scaleAspectFill
    self.clipsToBounds = false
    self.layer.cornerRadius = 20
    self.layer.maskedCorners = corners
    self.layer.masksToBounds = false
    self.layer.shadowOffset = .zero
    self.layer.shadowColor = UIColor.gray.cgColor
    self.layer.shadowRadius = 20
    self.layer.shadowOpacity = 0.2
    self.backgroundColor = .white
    let shadowAmount: CGFloat = 2
    let rect = CGRect(x: 0, y: 2, width: self.bounds.width + shadowAmount * 0.1, height: self.bounds.height + shadowAmount * 0.1)
    self.layer.shadowPath = UIBezierPath(rect: rect).cgPath

}

func createImage(systemName: String) {
    let image = UIImage(systemName: systemName)!
    let renderer = UIGraphicsImageRenderer(bounds: self.frame)
    let renderedImage = renderer.image { (_) in
        image.draw(in: frame.insetBy(dx: 30, dy: 30))
    }

并像这样使用它:

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")
let locationView = CustomView(frame: frame, corners: [], systemName: "location")
let twoDView = CustomView(frame: frame, corners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner], systemName: "view.2d")
let binocularsView = CustomView(frame: frame, corners: [.layerMinXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner], systemName: "binoculars.fill")

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(20, after: twoDView)
view.addSubview(stackView)

这使它看起来像这样:

我的截图

并且:stackView 的每个图标之间没有细线:/

谢谢你的帮助!

标签: swiftxcodeuibuttonstackview

解决方案


不知道发生了什么,因为当我按原样运行代码时,我没有得到你显示的拉伸图像。

要获得“每个图标之间的细线”,您可以修改自定义图像视图以将线条添加到中间图标图像,或者更容易的是UIView在每个图标之间使用 1 点高度。

此外,您最好使用自动布局。

看看这是否给你想要的结果:

class CustomView: UIImageView {
    init(frame: CGRect, corners: CACornerMask, systemName: String) {
        super.init(frame: frame)
        self.createBorders(corners: corners)
        self.createImage(systemName: systemName)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func createBorders(corners: CACornerMask) {
        self.contentMode = .scaleAspectFill
        self.clipsToBounds = false
        self.layer.cornerRadius = 20
        self.layer.maskedCorners = corners
        self.layer.masksToBounds = false
        self.layer.shadowOffset = .zero
        self.layer.shadowColor = UIColor.gray.cgColor
        self.layer.shadowRadius = 20
        self.layer.shadowOpacity = 0.2
        self.backgroundColor = .white
        let shadowAmount: CGFloat = 2
        let rect = CGRect(x: 0, y: 2, width: self.bounds.width + shadowAmount * 0.1, height: self.bounds.height + shadowAmount * 0.1)
        self.layer.shadowPath = UIBezierPath(rect: rect).cgPath
    }
    
    func createImage(systemName: String) {
        guard let image = UIImage(systemName: systemName)?.withTintColor(.blue, renderingMode: .alwaysOriginal) else { return }
        let renderer = UIGraphicsImageRenderer(bounds: self.frame)
        let renderedImage = renderer.image { (_) in
            image.draw(in: frame.insetBy(dx: 30, dy: 30))
        }
        self.image = renderedImage
    }
}

class TestCustomViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .systemYellow
        
        let size = CGSize(width: 100, height: 100)
        let frame = CGRect(origin: .zero, size: size)
        
        let infoView       = CustomView(frame: frame, corners: [.layerMinXMinYCorner, .layerMaxXMinYCorner], systemName: "info.circle")
        let locationView   = CustomView(frame: frame, corners: [], systemName: "location")
        let twoDView       = CustomView(frame: frame, corners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner], systemName: "view.2d")
        let binocularsView = CustomView(frame: frame, corners: [.layerMinXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner], systemName: "binoculars.fill")

        // create 2 separator views
        let sep1 = UIView()
        sep1.backgroundColor = .blue
        let sep2 = UIView()
        sep2.backgroundColor = .blue
        
        let stackView = UIStackView(arrangedSubviews: [infoView, sep1, locationView, sep2, twoDView, binocularsView])

        // use .fill, not .fillEqually
        stackView.distribution = .fill
        
        stackView.axis = .vertical
        stackView.setCustomSpacing(20, after: twoDView)

        view.addSubview(stackView)
        
        // let's use auto-layout
        stackView.translatesAutoresizingMaskIntoConstraints = false
        
        // respect safe area
        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            
            // only need top and leading constraints for the stack view
            stackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 100.0),
            stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 100.0),
        
            // both separator views need height constraint of 1.0
            sep1.heightAnchor.constraint(equalToConstant: 1.0),
            sep2.heightAnchor.constraint(equalToConstant: 1.0),
        ])
        
        // set all images to the same 1:1 ratio size
        [infoView, locationView, twoDView, binocularsView].forEach { v in
            v.widthAnchor.constraint(equalToConstant: size.width).isActive = true
            v.heightAnchor.constraint(equalTo: v.widthAnchor).isActive = true
        }
        
    }
}

这就是我从该代码示例中得到的:

在此处输入图像描述


推荐阅读