簡體   English   中英

如何在 UIBlurEffect 上繪制一個矩形孔並在 x 軸上移動它(UIKit)

[英]How draw a rectangle hole on UIBlurEffect and move it on x axis (UIKit)

我正在嘗試在視圖上創建模糊效果,而不是添加將在此模糊層上顯示圖像的形狀(自定義視頻編輯功能)

目前我只能從右邊緣拖動蒙版視圖:

在此處輸入圖像描述

但是當我嘗試從左邊緣做時,我得到了這樣的效果:

在此處輸入圖像描述

func configureBlurView() {
        let viewHeight: CGFloat = 60
        let padding: CGFloat = 10
        blurView = UIView()
        blurView.layer.cornerRadius = 10
        blurView.clipsToBounds = true
        blurView.isHidden = true
        blurView.translatesAutoresizingMaskIntoConstraints = false
        addSubview(blurView)
        addConstraints([
            blurView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: padding),
            blurView.bottomAnchor.constraint(equalTo: stackView.topAnchor, constant: -padding),
            blurView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -padding),
            blurView.heightAnchor.constraint(equalToConstant: viewHeight)
        ])
        addBlurEffect(for: blurView)
    }
    
    private func addBlurEffect(for view: UIView) {
        let blurEffect = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
        blurEffect.alpha = 0.5
        blurEffect.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(blurEffect)
        addConstraints([
            blurEffect.topAnchor.constraint(equalTo: view.topAnchor),
            blurEffect.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            blurEffect.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            blurEffect.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
    }
    
    private func makeClearHole(rect: CGRect) {
        let maskLayer = CAShapeLayer()
        maskLayer.fillColor = UIColor.black.cgColor

        let pathToOverlay = CGMutablePath()
        pathToOverlay.addRect(blurView.bounds)
        pathToOverlay.addRect(rect)
        
        maskLayer.path = pathToOverlay
        maskLayer.fillRule = .evenOdd
        maskLayer.cornerRadius = 10
        blurView.layer.mask = maskLayer
    }

我正在使用 touchesMoved 方法來更改橙色視圖尺寸:

 override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard trimmerView.isHidden == false else { return }
        if let touch = touches.first{
            let currentTouchPoint = touch.location(in: self)
            let previousTouchPoint = touch.previousLocation(in: self)
            
            let deltaX = currentTouchPoint.x - previousTouchPoint.x
            
            if trimmerView.bounds.width >= 70 {
                if touchStartEdge.middle {

                    if trimmerViewLeadingConstraint.constant < 10 {
                        trimmerViewLeadingConstraint.constant = 10
                    } else if trimmerViewTrailingConstraint.constant > -10 {
                        trimmerViewTrailingConstraint.constant = -10
                    } else {
                        trimmerViewLeadingConstraint.constant += deltaX
                        trimmerViewTrailingConstraint.constant += deltaX
                    }
                }

                if touchStartEdge.leftEdge {
                    if trimmerViewLeadingConstraint.constant >= 10.0 {
                        trimmerViewLeadingConstraint.constant += deltaX
                    } else if trimmerViewLeadingConstraint.constant < 10.0 {
                        trimmerViewLeadingConstraint.constant = 10
                    }
                }

                if touchStartEdge.rightEdge {
                    if trimmerViewTrailingConstraint.constant <= -10 {
                        trimmerViewTrailingConstraint.constant += deltaX
                    } else if trimmerViewTrailingConstraint.constant > -10 {
                        trimmerViewTrailingConstraint.constant = -10.0
                    }
                }
            }
            
            updateProgressBarConstraints()
            makeClearHole(rect: CGRect(x: 0, y: 0, width: trimmerView.frame.width, height: trimmerView.frame.height))
            UIView.animate(withDuration: 0.10, delay: 0, options: .curveEaseIn) { [weak self] in
                self?.layoutIfNeeded()
            }
        }
    }

我想要實現的是僅在橙色視圖的范圍內消除模糊效果。

有任何想法嗎 ?? :)

感謝幫助!!

幾種方法可以做到這一點 - 這是一個......

將蒙版添加到模糊效果視圖。 當用戶拖動“修剪器”時,更新蒙版。

這是一個簡單的例子......

出色地:

  • 創建一個包含 10 個圖像的堆棧視圖
  • 用蒙版模糊有效視圖覆蓋它
  • 添加“可拖動的修剪器視圖”
  • 當我們拖動修剪器時,我們會更新遮罩

示例視圖控制器

class TrimmerVC: UIViewController {
    
    var blurView: MaskedBlurView!
    let trimmerView = DragView()
    let stackView = UIStackView()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // respect safe area when we setup constraints
        let g = view.safeAreaLayoutGuide
        
        stackView.distribution = .fillEqually
        stackView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(stackView)
        
        NSLayoutConstraint.activate([
            stackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
            stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
            stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
            stackView.heightAnchor.constraint(equalToConstant: 80.0),
        ])

        // let's add 10 imageviews to the stack view
        for i in 1...10 {
            if let img = UIImage(systemName: "\(i).circle.fill") {
                let imgView = UIImageView(image: img)
                imgView.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
                stackView.addArrangedSubview(imgView)
            }
        }
        
        let blurEffect = UIBlurEffect(style: .dark)
        blurView = MaskedBlurView(effect: blurEffect)
        blurView.alpha = 0.5
        blurView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(blurView)
        
        NSLayoutConstraint.activate([
            blurView.topAnchor.constraint(equalTo: stackView.topAnchor, constant: 0.0),
            blurView.leadingAnchor.constraint(equalTo: stackView.leadingAnchor, constant: 0.0),
            blurView.trailingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: 0.0),
            blurView.bottomAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 0.0),
        ])
        
        trimmerView.backgroundColor = .systemOrange
        view.addSubview(trimmerView)
        
        trimmerView.didDrag = { [weak self] newX in
            guard let self = self else { return }
            self.blurView.clearX = newX - self.stackView.frame.origin.x
        }
        
    }
    // we'll use this to update the framing when the stack view width changes
    //  such as on device rotation
    var curStackW: CGFloat = -1
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        if curStackW != stackView.frame.width {
            curStackW = stackView.frame.width
            var r = stackView.frame
            r.origin.y += r.size.height + 20.0
            r.size.width = 160
            r.size.height = 40
            trimmerView.frame = r
            blurView.clearWidth = trimmerView.frame.width
            blurView.clearX = 0
        }
    }
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        // toggle the trimmer view between
        //  below the stack view and
        //  overlaid on the stack view
        if trimmerView.frame.origin.y > stackView.frame.origin.y {
            let r = stackView.frame
            trimmerView.frame.origin.y = r.origin.y - 6.0
            trimmerView.frame.size.height = r.height + 12.0
        } else {
            let r = stackView.frame
            trimmerView.frame.origin.y = r.origin.y + r.height + 12.0
            trimmerView.frame.size.height = 60.0
        }
    }
}

示例可拖動的“修剪器”視圖

class DragView: UIView {
    
    var didDrag: ((CGFloat) -> ())?
    
    let maskLayer = CAShapeLayer()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() {
        maskLayer.fillColor = UIColor.red.cgColor
        layer.mask = maskLayer
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        
        let pathToOverlay = CGMutablePath()
        pathToOverlay.addRect(bounds)
        pathToOverlay.addRect(bounds.insetBy(dx: 20.0, dy: 8.0))
        
        maskLayer.path = pathToOverlay
        maskLayer.fillRule = .evenOdd
        maskLayer.cornerRadius = 10
    }
    
    var touchStartX: CGFloat = 0
    var frameStartX: CGFloat = 0
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        touchStartX = touch.location(in: self.superview!).x
        frameStartX = self.frame.origin.x
    }
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        let loc = touch.location(in: self.superview!)
        self.frame.origin.x = frameStartX + (loc.x - touchStartX)
        didDrag?(self.frame.origin.x)
    }
}

蒙版模糊視圖示例

class MaskedBlurView: UIVisualEffectView {
    
    public var clearWidth: CGFloat = 100 {
        didSet { updateMask() }
    }
    public var clearX: CGFloat = 0 {
        didSet { updateMask() }
    }
    
    private let maskLayer = CAShapeLayer()
    
    override init(effect: UIVisualEffect?) {
        super.init(effect: effect)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() {
        maskLayer.fillColor = UIColor.red.cgColor
        layer.mask = maskLayer
    }
    func updateMask() {
        let leftR = CGRect(x: 0, y: 0, width: clearX, height: bounds.height)
        let rightR = CGRect(x: clearX + clearWidth, y: 0, width: bounds.width, height: bounds.height)
        let bez = UIBezierPath(rect: leftR)
        bez.append(UIBezierPath(rect: rightR))
        maskLayer.path = bez.cgPath
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        maskLayer.frame = bounds
    }

}

運行時(橫向),它將像這樣開始:

在此處輸入圖像描述

我將“修剪器”視圖放在堆棧視圖下方,以使其更清楚發生了什么。

當我們拖動修剪器視圖時,模糊視圖的遮罩將被更新:

在此處輸入圖像描述

點擊屏幕空白部分的任意位置將在“堆棧視圖下”和“疊加在堆棧視圖上”之間切換修剪器視圖:

在此處輸入圖像描述

在此處輸入圖像描述

這只是快速組合在一起 - 您應該可以輕松重組代碼以將所有內容包裝到單個自定義視圖中(或者它最適合您的需求)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM