1. 현재 뷰의 전체 스크린샷 추출

extension UIView {
  
    /// 현재 뷰의 전체 스크린샷 추출
    func screenCapture() -> UIImage {
        let snapview = UIScreen.main.snapshotView(afterScreenUpdates: true)
        let renderer = UIGraphicsImageRenderer(size: snapview.bounds.size)
        let image = renderer.image { _ in
            self.addSubview(snapview)
            self.drawHierarchy(in: snapview.bounds, afterScreenUpdates: true)
            snapview.removeFromSuperview()
        }
        return image
    }
}

2. 일반적인 기능들

extension UIView {
    static let kLayerNameEdgeBorder = "EdgeBorderLayer"
    static let kLayerNameShadowPath = "ShadowPathLayer"
    static let kLayerNameGradientBorder = "GradientBorderLayer"
    static let kLayerNameGradientBackground = "GradientBackgroundLayer"
    
    ///현재 뷰의 parent ViewController 찾기
    var parentViewController: UIViewController? {
        var parentResponder: UIResponder? = self
        while parentResponder != nil {
            parentResponder = parentResponder!.next
            if let viewController = parentResponder as? UIViewController {
                return viewController
            }
        }
        return nil
    }
    
    /// 테그번호로 뷰 가져오기
    func getView(viewTag: Int) -> Any {
        return self.viewWithTag(viewTag) as Any
    }
    
    /// 현재 뷰에 Blur 효과 적용
    func addBlurToView(style: UIBlurEffect.Style, alpha: CGFloat){
        let blurEffect:UIBlurEffect = UIBlurEffect(style: style)
        let blurredEffectView = UIVisualEffectView(effect: blurEffect)
        blurredEffectView.frame = self.bounds
        blurredEffectView.alpha = alpha
        blurredEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        self.addSubview(blurredEffectView)
        self.sendSubviewToBack(blurredEffectView)
    }
    
    /// 적용된 Blur 효과 제거 
    func removeBlurFromView(){
        for subView in self.subviews{
            if let subView = subView as? UIVisualEffectView{
                subView.removeFromSuperview()
            }
        }
    }
    
    /// 뷰의 모서리 둥근 모양 및 그림자 효과
    func setCornerRadiusWithShadow(cornerRadius: CGFloat, masksToBounds :Bool = false) {
        
        if layer.shadowPath != nil {
            return
        }
        
        layer.shadowColor = UIColor.darkGray.cgColor
        layer.shadowOffset = CGSize(width: 0.0, height: 2.0)
        layer.shadowRadius = cornerRadius
        layer.shadowOpacity = 0.3//1.0
        layer.masksToBounds = masksToBounds
        layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: 8).cgPath

        layer.cornerRadius = cornerRadius
    }
    
    /// 전체 둥근 모양 설정
    func harfCornerRadius() {
        self.layer.cornerRadius = self.frame.height/2
    }
    
    
    /// 부분적 둥근 모서리 설정  (self.view.edgeCornerRadius(corners: [.topLeft, .topRight], radius: 10.0))
    func edgeCornerRadius(corners: UIRectCorner, radius: CGFloat, clipsToBounds: Bool = true, width: CGFloat = 0.0, borderColor: UIColor = .white) {

        // Corners Radius
        let mask = CAShapeLayer()
        mask.frame = bounds
        mask.path = UIBezierPath(roundedRect: bounds,
                                 byRoundingCorners: corners,
                                 cornerRadii: CGSize(width: radius, height: radius)).cgPath

        // 레이어 경계선 위로 하위뷰가 벗어나있을 경우 잘림. (10 이하 버전용)
        //layer.mask = mask
        //clipsToBounds = false
        
        /* 11 이상 버전 사용
         - clipsToBounds true일 경우 SubView가 외곽으로 벗어나는 UI의 경우 짤리기 때문에 false로 설정
         - 그 외 일반적인 상황에서는 default true 값을 사용해야 CornerRaidus적용됨.
         */
        self.clipsToBounds = clipsToBounds
        layer.cornerRadius = radius
        layer.maskedCorners = CACornerMask(rawValue: corners.rawValue)
        
        // Add Border
        if width > 0 {
            removeLayer(layername: UIView.kLayerNameEdgeBorder)
            let borderLayer = CAShapeLayer()
            borderLayer.path = mask.path // Reuse the Bezier path
            borderLayer.fillColor = UIColor.clear.cgColor
            borderLayer.strokeColor = borderColor.cgColor
            borderLayer.lineWidth = width
            borderLayer.frame = bounds
            borderLayer.name = UIView.kLayerNameEdgeBorder
            
            layer.addSublayer(borderLayer)
        }
    }
    
    /// 뷰의 테두리 컬러 적용
    func borderColor(width: CGFloat, borderColor color: UIColor) {
        self.layer.borderWidth = width
        self.layer.borderColor = color.cgColor
    }
    
    /// 뷰의 백그라운드 그라디언트 컬러 적용
    func addBackgroundGradient(colors: [UIColor],
                       cornerRadius: CGFloat = 5.0,
                       opacity: CGFloat = 1.0,
                       shadow: Bool = false,
                       startPoint: CGPoint = CGPoint(x: 0.0, y: 0.0),
                       endPoint: CGPoint = CGPoint(x: 1, y: 0)) {
        
        let gradientLayer = CAGradientLayer()
        gradientLayer.colors = colors.map { return $0.cgColor }
        gradientLayer.startPoint = startPoint
        gradientLayer.endPoint = endPoint
//        gradientLayer.locations = [0.0, 1.0]
        gradientLayer.opacity = Float(opacity)
        gradientLayer.frame = self.bounds
        
//        if cornerRadius == true {
        gradientLayer.cornerRadius = cornerRadius //self.frame.height/2
//        }
        
        if shadow {
            gradientLayer.shadowColor = UIColor.darkGray.cgColor
            gradientLayer.shadowOffset = CGSize(width: 2.5, height: 2.5)
            gradientLayer.shadowRadius = 5.0
            gradientLayer.shadowOpacity = 0.3
        }
        
        gradientLayer.name = UIView.kLayerNameGradientBackground
        
        gradientLayer.masksToBounds = false
        self.layer.insertSublayer(gradientLayer, at: 0)
        
        
    }
    
    /// 뷰의 테두리에 그라디언트 컬러 효과 적용
    public func addBorderGradient(
        width: CGFloat,
        colors: [UIColor],
        cornerRadius: CGFloat = 5.0,
        startPoint: CGPoint = CGPoint(x: 0.0, y: 0.0),
        endPoint: CGPoint = CGPoint(x: 1, y: 0)) {
        
//        removeLayer(layername: UIView.kLayerNameGradientBorder)
        
        let existedBorder = gradientBorderLayer()
        let border = existedBorder ?? CAGradientLayer()
        border.frame = self.bounds
        border.colors = colors.map { return $0.cgColor }
//        border.opacity = 1.0
        border.startPoint = startPoint
        border.endPoint = endPoint
        border.name = UIView.kLayerNameGradientBorder
        border.cornerRadius = cornerRadius
        
        let mask = CAShapeLayer()
//        mask.path = UIBezierPath(roundedRect: bounds, cornerRadius: 8).cgPath
        mask.path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius).cgPath //0).cgPath
        mask.fillColor = UIColor.clear.cgColor
        mask.strokeColor = UIColor.white.cgColor
        mask.lineWidth = width
        
        border.mask = mask
        
        
        let exists = existedBorder != nil
        if !exists {
            layer.addSublayer(border)
//            layer.layoutIfNeeded()
            //            self.layer.insertSublayer(border, at: 0)
        } else {
            layer.layoutIfNeeded()
        }
    }
    
    
    /*
     상단 뷰 등근 곡선
    */
    func addTopRoundedEdge(desiredCurve:CGFloat?)
    {
        let offset:CGFloat = self.frame.width/desiredCurve!
        let bounds: CGRect = self.bounds
        
        let rectBounds: CGRect = CGRect(x: bounds.origin.x, y: bounds.origin.y+bounds.size.height / 2, width: bounds.size.width, height: bounds.size.height / 2)

        
        let rectPath: UIBezierPath = UIBezierPath(rect: rectBounds)
        
        let ovalBounds: CGRect = CGRect(x: bounds.origin.x - offset / 2, y: bounds.origin.y, width: bounds.size.width + offset, height: bounds.size.height)
  
        let ovalPath: UIBezierPath = UIBezierPath(ovalIn: ovalBounds)
        rectPath.append(ovalPath)
        
        // Create the shape layer and set its path
        let maskLayer: CAShapeLayer = CAShapeLayer()
        maskLayer.frame = bounds
        maskLayer.path = rectPath.cgPath
        
        // Set the newly created shape layer as the mask for the view's layer
        self.layer.mask = maskLayer
    }
    
    /* 하단 뷰 움푹파인 등근 곡선
     * bgView.addBottomRoundedEdge(desiredCurve: 1.5)
     */
    func addBottomRoundedEdge(desiredCurve: CGFloat?) {
        let offset: CGFloat = self.frame.width / desiredCurve!
        let bounds: CGRect = self.bounds
        
        let rectBounds: CGRect = CGRect(x: bounds.origin.x, y: bounds.origin.y, width: bounds.size.width, height: bounds.size.height / 2)
        
        let rectPath: UIBezierPath = UIBezierPath(rect: rectBounds)
        
        let ovalBounds: CGRect = CGRect(x: bounds.origin.x - offset / 2, y: bounds.origin.y, width: bounds.size.width + offset, height: bounds.size.height)
        
        let ovalPath: UIBezierPath = UIBezierPath(ovalIn: ovalBounds)
        rectPath.append(ovalPath)
        
        // Create the shape layer and set its path
        let maskLayer: CAShapeLayer = CAShapeLayer()
        maskLayer.frame = bounds
        maskLayer.path = rectPath.cgPath
        
        // Set the newly created shape layer as the mask for the view's layer
        self.layer.mask = maskLayer
    }
    
    private func gradientBorderLayer() -> CAGradientLayer? {
        let borderLayers = layer.sublayers?.filter {
            return $0.name == UIView.kLayerNameGradientBorder }
        
        if borderLayers?.count ?? 0 > 1 {
            fatalError()
        }
        return borderLayers?.first as? CAGradientLayer
    }
    
    func removeLayer(layername: String) {
        if let sublayers = self.layer.sublayers {
               for layer: CALayer in sublayers {
                   if (layer.name == layername) {
//                    log(direction: .UI, ofType: self, datas: "=> UIView removeLayer \(String(describing: layername))")
                       layer.removeFromSuperlayer()
                       break
                   }
               }
        }
        /*
        guard let sublayers = self.layer.sublayers else {
            return
        }
        
        if sublayers.count > 0 {
            sublayers.forEach {
                if $0.name == layername {
                    $0.removeFromSuperlayer()
                }
            }
        }*/
    }
    
    public func removeGradientBorder() {
        self.gradientBorderLayer()?.removeFromSuperlayer()
    }
    
}

3. TapGestureRecognizer (탭 제스쳐) 

  ** WkWebView 에서는 동작 안함. (WKWebView는 별도로 탭 제스쳐 처리해줘야됨)

// MARK: TagGesture
extension UIView {
    
    // In order to create computed properties for extensions, we need a key to
    // store and access the stored property
    fileprivate struct AssociatedObjectKeys {
        static var tapGestureRecognizer = "MediaViewerAssociatedObjectKey_mediaViewer"
    }
    
    fileprivate typealias Action = (() -> Void)?
    
    // Set our computed property type to a closure
    fileprivate var tapGestureRecognizerAction: Action? {
        set {
            if let newValue = newValue {
                // Computed properties get stored as associated objects
                objc_setAssociatedObject(self, &AssociatedObjectKeys.tapGestureRecognizer, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
            }
        }
        get {
            let tapGestureRecognizerActionInstance = objc_getAssociatedObject(self, &AssociatedObjectKeys.tapGestureRecognizer) as? Action
            return tapGestureRecognizerActionInstance
        }
    }
    
    // This is the meat of the sauce, here we create the tap gesture recognizer and
    // store the closure the user passed to us in the associated object we declared above
    public func addTapGestureRecognizer(action: (() -> Void)?) {
        self.isUserInteractionEnabled = true
        
        self.tapGestureRecognizerAction = action
        
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture))
        
        self.isUserInteractionEnabled = true
        self.addGestureRecognizer(tapGestureRecognizer)
    }
    
    // Every time the user taps on the UIImageView, this function gets called,
    // which triggers the closure we stored
    @objc fileprivate func handleTapGesture(sender: UITapGestureRecognizer) {
        if let action = self.tapGestureRecognizerAction {
            action?()
        } else {
            print("no action")
        }
    }
    
}

+ Recent posts