// // UIViewExtension.swift // GMERemittance // // Created by Fm-user on 12/23/17. // Copyright © 2017 Gobal Money Express Co. Ltd. All rights reserved. // import Foundation import UIKit import VisualEffectView import RxSwift import RxCocoa extension UIView { func set(cornerRadius radius: CGFloat) { self.layer.cornerRadius = radius self.layer.masksToBounds = true } func rounded() { self.layer.cornerRadius = self.frame.height / 2 } func addBlur() { let visualEffectView = VisualEffectView(frame: self.frame) visualEffectView.colorTint = .clear visualEffectView.colorTintAlpha = 0.2 visualEffectView.blurRadius = 3 visualEffectView.scale = 1 visualEffectView.isUserInteractionEnabled = false self.addSubview(visualEffectView) } func set(border: UIColor) { self.layer.borderColor = border.cgColor; } func set(borderWidth: CGFloat) { self.layer.borderWidth = borderWidth } func set(borderWidth width: CGFloat, of color: UIColor) { self.set(border: color) self.set(borderWidth: width) } } extension UIView { func fadeIn( duration: TimeInterval = 1.0, delay: TimeInterval = 0.0, completion: @escaping ((Bool) -> Void) = {(finished: Bool) -> Void in}) { UIView.animate( withDuration: duration, delay: delay, options: UIView.AnimationOptions.curveEaseIn, animations: { self.alpha = 1.0 }, completion: completion ) } func fadeOut( duration: TimeInterval = 1.0, delay: TimeInterval = 3.0, completion: @escaping (Bool) -> Void = {(finished: Bool) -> Void in} ) { UIView.animate( withDuration: duration, delay: delay, options: UIView.AnimationOptions.curveEaseIn, animations: { self.alpha = 0.0 }, completion: completion ) } } extension CALayer { func applyCornerRadiusShadow( color: UIColor = UIColor.themeShadow, alpha: Float = 1, x: CGFloat = 0, y: CGFloat = 3, blur: CGFloat = 10, spread: CGFloat = 0, cornerRadiusValue: CGFloat = 0) { cornerRadius = cornerRadiusValue shadowColor = color.cgColor shadowOpacity = alpha shadowOffset = CGSize(width: x, height: y) shadowRadius = blur / 2.0 if spread == 0 { shadowPath = nil } else { let dx = -spread let rect = bounds.insetBy(dx: dx, dy: dx) shadowPath = UIBezierPath(rect: rect).cgPath } } } // MARK: - Animator extension UIView { func bottomToOrigin(duration: Double = 0.3, completion: (() -> Void)? = nil) { let transform = CGAffineTransform(translationX: 0, y: self.bounds.height * 2) self.transform = transform // let velocity = self.bounds.height * CGFloat(duration) UIView.animate( withDuration:duration, delay: duration, usingSpringWithDamping: 0.85, initialSpringVelocity: 2, animations: { let transform = CGAffineTransform(translationX: 0, y: 0) self.transform = transform }, completion: { _ in completion?() } ) } func originToBottom(duration: Double = 0.1, completion: @escaping () -> Void ) { let transform = CGAffineTransform(translationX: 0, y: 0) self.transform = transform let animator = UIViewPropertyAnimator( duration: duration, curve: .linear) { let transform = CGAffineTransform(translationX: 0, y: self.bounds.height * 2) self.transform = transform } animator.addCompletion { _ in completion() } animator.startAnimation() } func popUpBouncy(duration: Double = 0.1) { alpha = 0.0 let transform = CGAffineTransform(scaleX: 0.8, y: 0.8) self.transform = transform UIView.animate( withDuration:duration, delay: duration, usingSpringWithDamping: 0.2, initialSpringVelocity: 10, animations: { self.alpha = 1.0 let transform = CGAffineTransform(scaleX: 1, y: 1) self.transform = transform } ) } func animateHidden(isHidden: Bool, duration: Double = 0.5, completion: (() -> Void)? = nil) { if self.isHidden { self.isHidden = isHidden UIView.animate( withDuration: duration, animations: { self.alpha = isHidden ? 0 : 1 }, completion: { _ in completion?() } ) } else { UIView.animate( withDuration: duration, animations: { self.alpha = isHidden ? 0 : 1 }, completion: { _ in self.isHidden = isHidden completion?() } ) } } func travelSubView(block: (_ view: UIView, _ stop: inout Bool) -> Void) { var stop = false block(self, &stop) if !stop { self.subviews.forEach { $0.travelSubView(block: block) } } } func rotate(duration: Double = 0.1, angle: CGFloat = .pi) { let animator = UIViewPropertyAnimator( duration: duration, curve: .easeInOut ) { self.transform = CGAffineTransform(rotationAngle: .pi) } animator.startAnimation() } } // MARK: - FOR Badge extension UIView { /* * Assign badge with only text. */ /// - parameter text: The badge value, use nil to remove exsiting badge. @objc public func badge(text badgeText: String?) { badge(text: badgeText, appearance: BadgeAppearance()) } /// - parameter text: The badge value, use nil to remove exsiting badge. /// - parameter appearance: The appearance of the badge. public func badge(text badgeText: String?, appearance: BadgeAppearance) { badge(text: badgeText, badgeEdgeInsets: nil, appearance: appearance) } /* * Assign badge with text and edge insets. */ @available(*, deprecated, message: "Use badge(text: String?, appearance:BadgeAppearance)") /// badge /// /// - Parameters: /// - badgeText: badge's Text /// - badgeEdgeInsets: badge's Inset @objc public func badge(text badgeText: String?, badgeEdgeInsets: UIEdgeInsets) { badge(text: badgeText, badgeEdgeInsets: badgeEdgeInsets, appearance: BadgeAppearance()) } /* * Assign badge with text,insets, and appearance. */ public func badge ( text badgeText:String?, badgeEdgeInsets: UIEdgeInsets?, appearance: BadgeAppearance ) { //Create badge label var badgeLabel: BadgeLabel! var doesBadgeExist = false //Find badge in subviews if exists for view in subviews { if view.tag == 1, let label = view as? BadgeLabel { badgeLabel = label } } //If assigned text is nil (request to remove badge) and badge label is not nil: if badgeText == nil && badgeLabel != nil { if appearance.animate { UIView.animate( withDuration: appearance.duration, animations: { badgeLabel.alpha = 0.0 badgeLabel.transform = CGAffineTransform(scaleX: 0.1, y: 0.1) }, completion: { _ in badgeLabel.removeFromSuperview() } ) } else { badgeLabel.removeFromSuperview() } return } else if badgeText == nil && badgeLabel == nil { return } //Badge label is nil (There was no previous badge) if (badgeLabel == nil) { //init badge label variable badgeLabel = BadgeLabel() //assign tag to badge label badgeLabel.tag = 1 } else { doesBadgeExist = true } let oldWidth: CGFloat? = doesBadgeExist ? badgeLabel.frame.width : nil //Set the text on the badge label badgeLabel.text = badgeText //Set font size badgeLabel.font = UIFont.systemFont(ofSize: appearance.textSize) badgeLabel.sizeToFit() //set the allignment badgeLabel.textAlignment = appearance.textAlignment //set background color badgeLabel.layer.backgroundColor = appearance.backgroundColor.cgColor //set text color badgeLabel.textColor = appearance.textColor //get current badge size let badgeSize = badgeLabel.frame.size //calculate width and height with minimum height and width of 20 let height = max(18, Double(badgeSize.height) + 5.0) let width = max(height, Double(badgeSize.width) + 10.0) badgeLabel.frame.size = CGSize(width: width, height: height) //add to subview if doesBadgeExist { //remove view to delete constraints badgeLabel.removeFromSuperview() } addSubview(badgeLabel) //The distance from the center of the view (vertically) let centerY = appearance.distanceFromCenterY == 0 ? -(bounds.size.height / 2) : appearance.distanceFromCenterY //The distance from the center of the view (horizontally) let centerX = appearance.distanceFromCenterX == 0 ? (bounds.size.width / 2) : appearance.distanceFromCenterX //disable auto resizing mask badgeLabel.translatesAutoresizingMaskIntoConstraints = false //add height constraint addConstraint( NSLayoutConstraint( item: badgeLabel!, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: CGFloat(height) ) ) //add width constraint addConstraint( NSLayoutConstraint( item: badgeLabel!, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: CGFloat(width) ) ) //add vertical constraint addConstraint( NSLayoutConstraint( item: badgeLabel!, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1.0, constant: centerX ) ) //add horizontal constraint addConstraint( NSLayoutConstraint( item: badgeLabel!, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1.0, constant: centerY ) ) badgeLabel.layer.borderColor = appearance.borderColor.cgColor badgeLabel.layer.borderWidth = appearance.borderWidth //corner radius badgeLabel.layer.cornerRadius = badgeLabel.frame.size.height / 2 //setup shadow if appearance.allowShadow { badgeLabel.layer.shadowOffset = CGSize(width: 1, height: 1) badgeLabel.layer.shadowRadius = 1 badgeLabel.layer.shadowOpacity = 0.5 badgeLabel.layer.shadowColor = UIColor.black.cgColor } //badge does not exist, meaning we are adding a new one if !doesBadgeExist { //should it animate? if appearance.animate { badgeLabel.transform = CGAffineTransform(scaleX: 0, y: 0) UIView.animate( withDuration: appearance.duration, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: [], animations: { badgeLabel.transform = .identity }, completion: nil ) } } else { if appearance.animate, let oldWidth = oldWidth { let currentWidth = badgeLabel.frame.width badgeLabel.frame.size.width = oldWidth UIView.animate(withDuration: appearance.duration) { badgeLabel.frame.size.width = currentWidth } } } } } extension Reactive where Base: UIView { /// Bindable sink for `hidden` property. public var isAnimateHidden: Binder { return Binder(self.base) { view, hidden in view.animateHidden(isHidden: hidden) } } } extension UIView{ func addArc(path: UIBezierPath, offset: CGFloat, radius: CGFloat){ var offsets: CGFloat = offset path.addArc(withCenter: CGPoint(x: (offset + radius), y: self.frame.height), radius: radius, startAngle: CGFloat(180.0).toRadians(), endAngle: CGFloat(0.0).toRadians(), clockwise: true) offsets += (15*2 + radius) if offset < self.frame.width{ addArc(path: path, offset: offsets, radius: radius) } } func bottomArc(){ let path = UIBezierPath() path.move(to: CGPoint(x: 0.0, y: 0.0)) path.addLine(to: CGPoint(x: 0, y: self.frame.height)) path.addLine(to: CGPoint(x: 15, y: self.frame.height)) addArc(path: path, offset: 15.0, radius: 15) path.addLine(to: CGPoint(x: self.frame.size.width, y: self.frame.size.height)) path.addLine(to: CGPoint(x: self.frame.size.width, y: 0)) path.close() let shapeLayer = CAShapeLayer() shapeLayer.path = path.cgPath self.layer.mask = shapeLayer } } extension CGFloat { func toRadians() -> CGFloat { return self * CGFloat(Double.pi) / 180.0 } } extension UIView { func applyGradient(isVertical: Bool, colorArray: [UIColor]) { layer.sublayers?.filter({ $0 is CAGradientLayer }).forEach({ $0.removeFromSuperlayer() }) let gradientLayer = CAGradientLayer() gradientLayer.colors = colorArray.map({ $0.cgColor }) if isVertical { //top to bottom gradientLayer.locations = [0.0, 1.0] } else { //left to right gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0) gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.0) } // backgroundColor = .clear gradientLayer.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height + 10) layer.insertSublayer(gradientLayer, at: 0) } } extension UIView { func setupCornerRadius(_ cornerRadius: CGFloat = 0, maskedCorners: CACornerMask? = nil) { layer.cornerRadius = cornerRadius if let corners = maskedCorners { layer.maskedCorners = corners } } func animateClick(completion: @escaping () -> Void) { UIView.animate(withDuration: 0.15) { self.transform = CGAffineTransform(scaleX: 0.9, y: 0.9) } completion: { _ in UIView.animate(withDuration: 0.15) { self.transform = CGAffineTransform.identity } completion: { _ in completion() } } } func addSubviews(_ views: UIView...) { views.forEach { addSubview($0) } } func addShadow() { layer.shadowColor = UIColor.black.cgColor layer.shadowOffset = .zero layer.shadowOpacity = 0.4 layer.shadowRadius = 7 } } extension UIView { func anchor(top : NSLayoutYAxisAnchor? = nil, paddingTop : CGFloat = 0 , bottom : NSLayoutYAxisAnchor? = nil, paddingBottom : CGFloat = 0 , left: NSLayoutXAxisAnchor? = nil, paddingLeft: CGFloat = 0, right: NSLayoutXAxisAnchor? = nil, paddingRight: CGFloat = 0, width: CGFloat? = nil, height: CGFloat? = nil ){ if let top = top { topAnchor.constraint(equalTo: top, constant: paddingTop).isActive = true } if let bottom = bottom { bottomAnchor.constraint(equalTo: bottom, constant: paddingBottom).isActive = true } if let left = left { leadingAnchor.constraint(equalTo: left, constant: paddingLeft).isActive = true } if let right = right { trailingAnchor.constraint(equalTo: right, constant: paddingRight).isActive = true } if let width = width ,width != 0 { widthAnchor.constraint(equalToConstant: width).isActive = true } if let height = height, height != 0 { heightAnchor.constraint(equalToConstant: height).isActive = true } } func anchorGreaterOreEqualTo( top : NSLayoutYAxisAnchor? = nil, paddingTop : CGFloat = 0 , bottom : NSLayoutYAxisAnchor? = nil, paddingBottom : CGFloat = 0 , left: NSLayoutXAxisAnchor? = nil, paddingLeft: CGFloat = 0, right: NSLayoutXAxisAnchor? = nil, paddingRight: CGFloat = 0 ) { if let top = top { topAnchor.constraint(greaterThanOrEqualTo: top, constant: paddingTop).isActive = true } if let bottom = bottom { bottomAnchor.constraint(greaterThanOrEqualTo: bottom, constant: paddingBottom).isActive = true } if let left = left { leadingAnchor.constraint(greaterThanOrEqualTo: left, constant: paddingLeft).isActive = true } if let right = right { trailingAnchor.constraint(greaterThanOrEqualTo: right, constant: paddingRight).isActive = true } } func anchorLessThanEqualTo( top : NSLayoutYAxisAnchor? = nil, paddingTop : CGFloat = 0 , bottom : NSLayoutYAxisAnchor? = nil, paddingBottom : CGFloat = 0 , left: NSLayoutXAxisAnchor? = nil, paddingLeft: CGFloat = 0, right: NSLayoutXAxisAnchor? = nil, paddingRight: CGFloat = 0 ) { if let top = top { topAnchor.constraint(lessThanOrEqualTo: top, constant: paddingTop).isActive = true } if let bottom = bottom { bottomAnchor.constraint(lessThanOrEqualTo: bottom, constant: paddingBottom).isActive = true } if let left = left { leadingAnchor.constraint(lessThanOrEqualTo: left, constant: paddingLeft).isActive = true } if let right = right { trailingAnchor.constraint(lessThanOrEqualTo: right, constant: paddingRight).isActive = true } } func center(centerX : NSLayoutXAxisAnchor? , paddingX : CGFloat , centerY : NSLayoutYAxisAnchor? , paddingY : CGFloat) { translatesAutoresizingMaskIntoConstraints = false if let centerX = centerX { centerXAnchor.constraint(equalTo: centerX , constant: paddingX).isActive = true } if let centerY = centerY { centerYAnchor.constraint(equalTo: centerY , constant: paddingY).isActive = true } } } extension UIStackView { func addArrangedSubviews(_ subviews: [UIView]) { subviews.forEach { addArrangedSubview($0) } } }