ios 如何实现“应用指南教程画面”与特定视图的快速聚焦

sqougxex  于 2022-12-15  发布在  iOS
关注(0)|答案(1)|浏览(123)

我想实现以下重点用户界面。它基本上是指南教程视图为我的用户。

背景是我的主视图控制器,有一堆真实的的功能视图,我在它上面放了一个黑色的50%透明度的视图,以实现聚焦的UI,但它离我试图实现的还很远。任何建议都将不胜感激。
代码:

func createOverlay(frame: CGRect,
                   xOffset: CGFloat,
                   yOffset: CGFloat,
                   radius: CGFloat) -> UIView {
    // Step 1
    let overlayView = UIView(frame: frame)
    overlayView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
    // Step 2
    let path = CGMutablePath()
    path.addArc(center: CGPoint(x: xOffset, y: yOffset),
                radius: radius,
                startAngle: 0.0,
                endAngle: 2.0 * .pi,
                clockwise: false)
    path.addRect(CGRect(origin: .zero, size: overlayView.frame.size))
    // Step 3
    let maskLayer = CAShapeLayer()
    maskLayer.backgroundColor = UIColor.black.cgColor
    maskLayer.path = path
    // For Swift 4.0
    maskLayer.fillRule = CAShapeLayerFillRule.evenOdd
    // For Swift 4.2
    maskLayer.fillRule = .evenOdd
    // Step 4
    overlayView.layer.mask = maskLayer
    overlayView.clipsToBounds = true

    return overlayView
}

电流输出:

a8jjtwal

a8jjtwal1#

我们可以通过在叠加视图的图层上使用一个“反向”阴影路径来实现这一点。这将给予我们一个“羽化边缘”的椭圆。
下面是一个示例视图类:

class FocusView : UIView {
    
    // this will be the frame of the "see-through" oval
    public var ovalRect: CGRect = .zero {
        didSet { setNeedsLayout() }
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()

        // create an oval inside ovalRect
        let clearPath = UIBezierPath(ovalIn: ovalRect)
        
        // create a rectangle path larger than entire view
        //  so we don't see "feathering" on outer edges
        let opaquePath = UIBezierPath(rect: bounds.insetBy(dx: -80.0, dy: -80.0)).reversing()
        
        // append the paths so we get a "see-through" oval
        clearPath.append(opaquePath)
        
        self.layer.shadowPath = clearPath.cgPath
        self.layer.shadowColor = UIColor.black.cgColor
        self.layer.shadowOffset = CGSize.zero
        
        // adjust the opacity as desired
        self.layer.shadowOpacity = 0.5
        
        // adjust shadow radius as desired (controls the "feathered" edge)
        self.layer.shadowRadius = 8
    }
    
}

我们将添加6个彩色矩形作为“背景”,并添加一个UILabel作为“焦点”:

class ViewController: UIViewController {
    
    let focusView = FocusView()

    let label = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .darkGray
        
        // let's add six different color rectangles as the "background"
        let colors: [UIColor] = [
            .systemRed, .systemGreen, .systemBlue,
            .systemBrown, .systemYellow, .systemCyan,
        ]
        let vStack = UIStackView()
        vStack.axis = .vertical
        vStack.distribution = .fillEqually
        var i: Int = 0
        for _ in 0..<3 {
            let hStack = UIStackView()
            hStack.distribution = .fillEqually
            for _ in 0..<2 {
                let v = UIView()
                v.backgroundColor = colors[i % colors.count]
                hStack.addArrangedSubview(v)
                i += 1
            }
            vStack.addArrangedSubview(hStack)
        }

        vStack.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(vStack)
        
        label.font = .systemFont(ofSize: 30.0, weight: .bold)
        label.textColor = .white
        label.textAlignment = .center
        label.text = "Example"
        
        label.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(label)
        
        focusView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(focusView)
        
        // start with it hidden and transparent
        focusView.isHidden = true
        focusView.alpha = 0.0
        
        NSLayoutConstraint.activate([
            
            vStack.topAnchor.constraint(equalTo: view.topAnchor),
            vStack.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            vStack.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            vStack.bottomAnchor.constraint(equalTo: view.bottomAnchor),

            label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            label.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 80.0),
            
            focusView.topAnchor.constraint(equalTo: view.topAnchor),
            focusView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            focusView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            focusView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            
        ])

    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        
        // we may want to make sure the focusView is on top of all other views
        view.bringSubviewToFront(focusView)
        
        // set the focusView's "see-through" oval rect
        // it can be set with a hard-coded rect, or
        //  for this example, we'll use the label frame
        //  expanded by 80-points horizontally, 60-points vertically
        focusView.ovalRect = label.frame.insetBy(dx: -40.0, dy: -30.0)
        
        if focusView.isHidden {
            focusView.isHidden = false
            UIView.animate(withDuration: 0.3, animations: {
                self.focusView.alpha = 1.0
            })
        } else {
            UIView.animate(withDuration: 0.3, animations: {
                self.focusView.alpha = 0.0
            }, completion: { _ in
                self.focusView.isHidden = true
            })
        }
    }
}

在任何地方每次轻按都会淡入或淡出焦点视图:

相关问题