You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

660 lines
20 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
4 years ago
4 years ago
  1. //
  2. // UIViewExtension.swift
  3. // GMERemittance
  4. //
  5. // Created by Fm-user on 12/23/17.
  6. // Copyright © 2017 Gobal Money Express Co. Ltd. All rights reserved.
  7. //
  8. import Foundation
  9. import UIKit
  10. import VisualEffectView
  11. import RxSwift
  12. import RxCocoa
  13. extension UIView {
  14. func set(cornerRadius radius: CGFloat) {
  15. self.layer.cornerRadius = radius
  16. self.layer.masksToBounds = true
  17. }
  18. func rounded() {
  19. self.layer.cornerRadius = self.frame.height / 2
  20. }
  21. func addBlur() {
  22. let visualEffectView = VisualEffectView(frame: self.frame)
  23. visualEffectView.colorTint = .clear
  24. visualEffectView.colorTintAlpha = 0.2
  25. visualEffectView.blurRadius = 3
  26. visualEffectView.scale = 1
  27. visualEffectView.isUserInteractionEnabled = false
  28. self.addSubview(visualEffectView)
  29. }
  30. func set(border: UIColor) {
  31. self.layer.borderColor = border.cgColor;
  32. }
  33. func set(borderWidth: CGFloat) {
  34. self.layer.borderWidth = borderWidth
  35. }
  36. func set(borderWidth width: CGFloat, of color: UIColor) {
  37. self.set(border: color)
  38. self.set(borderWidth: width)
  39. }
  40. }
  41. extension UIView {
  42. func fadeIn(
  43. duration: TimeInterval = 1.0,
  44. delay: TimeInterval = 0.0,
  45. completion: @escaping ((Bool) -> Void) = {(finished: Bool) -> Void in}) {
  46. UIView.animate(
  47. withDuration: duration,
  48. delay: delay,
  49. options: UIView.AnimationOptions.curveEaseIn,
  50. animations: {
  51. self.alpha = 1.0
  52. },
  53. completion: completion
  54. )
  55. }
  56. func fadeOut(
  57. duration: TimeInterval = 1.0,
  58. delay: TimeInterval = 3.0,
  59. completion: @escaping (Bool) -> Void = {(finished: Bool) -> Void in}
  60. ) {
  61. UIView.animate(
  62. withDuration: duration,
  63. delay: delay,
  64. options: UIView.AnimationOptions.curveEaseIn,
  65. animations: {
  66. self.alpha = 0.0
  67. },
  68. completion: completion
  69. )
  70. }
  71. }
  72. extension CALayer {
  73. func applyCornerRadiusShadow(
  74. color: UIColor = UIColor.themeShadow,
  75. alpha: Float = 1,
  76. x: CGFloat = 0,
  77. y: CGFloat = 3,
  78. blur: CGFloat = 10,
  79. spread: CGFloat = 0,
  80. cornerRadiusValue: CGFloat = 0)
  81. {
  82. cornerRadius = cornerRadiusValue
  83. shadowColor = color.cgColor
  84. shadowOpacity = alpha
  85. shadowOffset = CGSize(width: x, height: y)
  86. shadowRadius = blur / 2.0
  87. if spread == 0 {
  88. shadowPath = nil
  89. } else {
  90. let dx = -spread
  91. let rect = bounds.insetBy(dx: dx, dy: dx)
  92. shadowPath = UIBezierPath(rect: rect).cgPath
  93. }
  94. }
  95. }
  96. // MARK: - Animator
  97. extension UIView {
  98. func bottomToOrigin(duration: Double = 0.3, completion: (() -> Void)? = nil) {
  99. let transform = CGAffineTransform(translationX: 0, y: self.bounds.height * 2)
  100. self.transform = transform
  101. // let velocity = self.bounds.height * CGFloat(duration)
  102. UIView.animate(
  103. withDuration:duration,
  104. delay: duration,
  105. usingSpringWithDamping: 0.85,
  106. initialSpringVelocity: 2,
  107. animations: {
  108. let transform = CGAffineTransform(translationX: 0, y: 0)
  109. self.transform = transform
  110. },
  111. completion: { _ in
  112. completion?()
  113. }
  114. )
  115. }
  116. func originToBottom(duration: Double = 0.1, completion: @escaping () -> Void ) {
  117. let transform = CGAffineTransform(translationX: 0, y: 0)
  118. self.transform = transform
  119. let animator = UIViewPropertyAnimator(
  120. duration: duration,
  121. curve: .linear) {
  122. let transform = CGAffineTransform(translationX: 0, y: self.bounds.height * 2)
  123. self.transform = transform
  124. }
  125. animator.addCompletion { _ in
  126. completion()
  127. }
  128. animator.startAnimation()
  129. }
  130. func popUpBouncy(duration: Double = 0.1) {
  131. alpha = 0.0
  132. let transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
  133. self.transform = transform
  134. UIView.animate(
  135. withDuration:duration,
  136. delay: duration,
  137. usingSpringWithDamping: 0.2,
  138. initialSpringVelocity: 10,
  139. animations: {
  140. self.alpha = 1.0
  141. let transform = CGAffineTransform(scaleX: 1, y: 1)
  142. self.transform = transform
  143. }
  144. )
  145. }
  146. func animateHidden(isHidden: Bool, duration: Double = 0.5, completion: (() -> Void)? = nil) {
  147. if self.isHidden {
  148. self.isHidden = isHidden
  149. UIView.animate(
  150. withDuration: duration,
  151. animations: {
  152. self.alpha = isHidden ? 0 : 1
  153. },
  154. completion: { _ in
  155. completion?()
  156. }
  157. )
  158. } else {
  159. UIView.animate(
  160. withDuration: duration,
  161. animations: {
  162. self.alpha = isHidden ? 0 : 1
  163. },
  164. completion: { _ in
  165. self.isHidden = isHidden
  166. completion?()
  167. }
  168. )
  169. }
  170. }
  171. func travelSubView(block: (_ view: UIView, _ stop: inout Bool) -> Void) {
  172. var stop = false
  173. block(self, &stop)
  174. if !stop {
  175. self.subviews.forEach { $0.travelSubView(block: block) }
  176. }
  177. }
  178. func rotate(duration: Double = 0.1, angle: CGFloat = .pi) {
  179. let animator = UIViewPropertyAnimator(
  180. duration: duration,
  181. curve: .easeInOut
  182. ) {
  183. self.transform = CGAffineTransform(rotationAngle: .pi)
  184. }
  185. animator.startAnimation()
  186. }
  187. }
  188. // MARK: - FOR Badge
  189. extension UIView {
  190. /*
  191. * Assign badge with only text.
  192. */
  193. /// - parameter text: The badge value, use nil to remove exsiting badge.
  194. @objc public func badge(text badgeText: String?) {
  195. badge(text: badgeText, appearance: BadgeAppearance())
  196. }
  197. /// - parameter text: The badge value, use nil to remove exsiting badge.
  198. /// - parameter appearance: The appearance of the badge.
  199. public func badge(text badgeText: String?, appearance: BadgeAppearance) {
  200. badge(text: badgeText, badgeEdgeInsets: nil, appearance: appearance)
  201. }
  202. /*
  203. * Assign badge with text and edge insets.
  204. */
  205. @available(*, deprecated, message: "Use badge(text: String?, appearance:BadgeAppearance)")
  206. /// badge
  207. ///
  208. /// - Parameters:
  209. /// - badgeText: badge's Text
  210. /// - badgeEdgeInsets: badge's Inset
  211. @objc public func badge(text badgeText: String?, badgeEdgeInsets: UIEdgeInsets) {
  212. badge(text: badgeText, badgeEdgeInsets: badgeEdgeInsets, appearance: BadgeAppearance())
  213. }
  214. /*
  215. * Assign badge with text,insets, and appearance.
  216. */
  217. public func badge (
  218. text badgeText:String?,
  219. badgeEdgeInsets: UIEdgeInsets?,
  220. appearance: BadgeAppearance
  221. ) {
  222. //Create badge label
  223. var badgeLabel: BadgeLabel!
  224. var doesBadgeExist = false
  225. //Find badge in subviews if exists
  226. for view in subviews {
  227. if view.tag == 1, let label = view as? BadgeLabel {
  228. badgeLabel = label
  229. }
  230. }
  231. //If assigned text is nil (request to remove badge) and badge label is not nil:
  232. if badgeText == nil && badgeLabel != nil {
  233. if appearance.animate {
  234. UIView.animate(
  235. withDuration: appearance.duration,
  236. animations: {
  237. badgeLabel.alpha = 0.0
  238. badgeLabel.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
  239. },
  240. completion: { _ in
  241. badgeLabel.removeFromSuperview()
  242. }
  243. )
  244. } else {
  245. badgeLabel.removeFromSuperview()
  246. }
  247. return
  248. } else if badgeText == nil && badgeLabel == nil {
  249. return
  250. }
  251. //Badge label is nil (There was no previous badge)
  252. if (badgeLabel == nil) {
  253. //init badge label variable
  254. badgeLabel = BadgeLabel()
  255. //assign tag to badge label
  256. badgeLabel.tag = 1
  257. } else {
  258. doesBadgeExist = true
  259. }
  260. let oldWidth: CGFloat? = doesBadgeExist ? badgeLabel.frame.width : nil
  261. //Set the text on the badge label
  262. badgeLabel.text = badgeText
  263. //Set font size
  264. badgeLabel.font = UIFont.systemFont(ofSize: appearance.textSize)
  265. badgeLabel.sizeToFit()
  266. //set the allignment
  267. badgeLabel.textAlignment = appearance.textAlignment
  268. //set background color
  269. badgeLabel.layer.backgroundColor = appearance.backgroundColor.cgColor
  270. //set text color
  271. badgeLabel.textColor = appearance.textColor
  272. //get current badge size
  273. let badgeSize = badgeLabel.frame.size
  274. //calculate width and height with minimum height and width of 20
  275. let height = max(18, Double(badgeSize.height) + 5.0)
  276. let width = max(height, Double(badgeSize.width) + 10.0)
  277. badgeLabel.frame.size = CGSize(width: width, height: height)
  278. //add to subview
  279. if doesBadgeExist {
  280. //remove view to delete constraints
  281. badgeLabel.removeFromSuperview()
  282. }
  283. addSubview(badgeLabel)
  284. //The distance from the center of the view (vertically)
  285. let centerY =
  286. appearance.distanceFromCenterY == 0 ? -(bounds.size.height / 2) : appearance.distanceFromCenterY
  287. //The distance from the center of the view (horizontally)
  288. let centerX =
  289. appearance.distanceFromCenterX == 0 ? (bounds.size.width / 2) : appearance.distanceFromCenterX
  290. //disable auto resizing mask
  291. badgeLabel.translatesAutoresizingMaskIntoConstraints = false
  292. //add height constraint
  293. addConstraint(
  294. NSLayoutConstraint(
  295. item: badgeLabel!,
  296. attribute: .height,
  297. relatedBy: .equal,
  298. toItem: nil,
  299. attribute: .notAnAttribute,
  300. multiplier: 1.0,
  301. constant: CGFloat(height)
  302. )
  303. )
  304. //add width constraint
  305. addConstraint(
  306. NSLayoutConstraint(
  307. item: badgeLabel!,
  308. attribute: .width,
  309. relatedBy: .equal,
  310. toItem: nil,
  311. attribute: .notAnAttribute,
  312. multiplier: 1.0,
  313. constant: CGFloat(width)
  314. )
  315. )
  316. //add vertical constraint
  317. addConstraint(
  318. NSLayoutConstraint(
  319. item: badgeLabel!,
  320. attribute: .centerX,
  321. relatedBy: .equal,
  322. toItem: self,
  323. attribute: .centerX,
  324. multiplier: 1.0,
  325. constant: centerX
  326. )
  327. )
  328. //add horizontal constraint
  329. addConstraint(
  330. NSLayoutConstraint(
  331. item: badgeLabel!,
  332. attribute: .centerY,
  333. relatedBy: .equal,
  334. toItem: self,
  335. attribute: .centerY,
  336. multiplier: 1.0,
  337. constant: centerY
  338. )
  339. )
  340. badgeLabel.layer.borderColor = appearance.borderColor.cgColor
  341. badgeLabel.layer.borderWidth = appearance.borderWidth
  342. //corner radius
  343. badgeLabel.layer.cornerRadius = badgeLabel.frame.size.height / 2
  344. //setup shadow
  345. if appearance.allowShadow {
  346. badgeLabel.layer.shadowOffset = CGSize(width: 1, height: 1)
  347. badgeLabel.layer.shadowRadius = 1
  348. badgeLabel.layer.shadowOpacity = 0.5
  349. badgeLabel.layer.shadowColor = UIColor.black.cgColor
  350. }
  351. //badge does not exist, meaning we are adding a new one
  352. if !doesBadgeExist {
  353. //should it animate?
  354. if appearance.animate {
  355. badgeLabel.transform = CGAffineTransform(scaleX: 0, y: 0)
  356. UIView.animate(
  357. withDuration: appearance.duration,
  358. delay: 0,
  359. usingSpringWithDamping: 0.5,
  360. initialSpringVelocity: 0.5,
  361. options: [],
  362. animations: {
  363. badgeLabel.transform = .identity
  364. },
  365. completion: nil
  366. )
  367. }
  368. } else {
  369. if appearance.animate, let oldWidth = oldWidth {
  370. let currentWidth = badgeLabel.frame.width
  371. badgeLabel.frame.size.width = oldWidth
  372. UIView.animate(withDuration: appearance.duration) {
  373. badgeLabel.frame.size.width = currentWidth
  374. }
  375. }
  376. }
  377. }
  378. }
  379. extension Reactive where Base: UIView {
  380. /// Bindable sink for `hidden` property.
  381. public var isAnimateHidden: Binder<Bool> {
  382. return Binder(self.base) { view, hidden in
  383. view.animateHidden(isHidden: hidden)
  384. }
  385. }
  386. }
  387. extension UIView{
  388. func addArc(path: UIBezierPath, offset: CGFloat, radius: CGFloat){
  389. var offsets: CGFloat = offset
  390. path.addArc(withCenter: CGPoint(x: (offset + radius), y: self.frame.height),
  391. radius: radius,
  392. startAngle: CGFloat(180.0).toRadians(),
  393. endAngle: CGFloat(0.0).toRadians(),
  394. clockwise: true)
  395. offsets += (15*2 + radius)
  396. if offset < self.frame.width{
  397. addArc(path: path, offset: offsets, radius: radius)
  398. }
  399. }
  400. func bottomArc(){
  401. let path = UIBezierPath()
  402. path.move(to: CGPoint(x: 0.0, y: 0.0))
  403. path.addLine(to: CGPoint(x: 0, y: self.frame.height))
  404. path.addLine(to: CGPoint(x: 15, y: self.frame.height))
  405. addArc(path: path, offset: 15.0, radius: 15)
  406. path.addLine(to: CGPoint(x: self.frame.size.width, y: self.frame.size.height))
  407. path.addLine(to: CGPoint(x: self.frame.size.width, y: 0))
  408. path.close()
  409. let shapeLayer = CAShapeLayer()
  410. shapeLayer.path = path.cgPath
  411. self.layer.mask = shapeLayer
  412. }
  413. }
  414. extension CGFloat {
  415. func toRadians() -> CGFloat {
  416. return self * CGFloat(Double.pi) / 180.0
  417. }
  418. }
  419. extension UIView {
  420. func applyGradient(isVertical: Bool, colorArray: [UIColor]) {
  421. layer.sublayers?.filter({ $0 is CAGradientLayer }).forEach({ $0.removeFromSuperlayer() })
  422. let gradientLayer = CAGradientLayer()
  423. gradientLayer.colors = colorArray.map({ $0.cgColor })
  424. if isVertical {
  425. //top to bottom
  426. gradientLayer.locations = [0.0, 1.0]
  427. } else {
  428. //left to right
  429. gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
  430. gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.0)
  431. }
  432. // backgroundColor = .clear
  433. gradientLayer.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height + 10)
  434. layer.insertSublayer(gradientLayer, at: 0)
  435. }
  436. }
  437. extension UIView {
  438. func setupCornerRadius(_ cornerRadius: CGFloat = 0, maskedCorners: CACornerMask? = nil) {
  439. layer.cornerRadius = cornerRadius
  440. if let corners = maskedCorners {
  441. layer.maskedCorners = corners
  442. }
  443. }
  444. func animateClick(completion: @escaping () -> Void) {
  445. UIView.animate(withDuration: 0.15) {
  446. self.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
  447. } completion: { _ in
  448. UIView.animate(withDuration: 0.15) {
  449. self.transform = CGAffineTransform.identity
  450. } completion: { _ in completion() }
  451. }
  452. }
  453. func addSubviews(_ views: UIView...) {
  454. views.forEach { addSubview($0) }
  455. }
  456. func addShadow() {
  457. layer.shadowColor = UIColor.black.cgColor
  458. layer.shadowOffset = .zero
  459. layer.shadowOpacity = 0.4
  460. layer.shadowRadius = 7
  461. }
  462. }
  463. extension UIView {
  464. func anchor(top : NSLayoutYAxisAnchor? = nil,
  465. paddingTop : CGFloat = 0 ,
  466. bottom : NSLayoutYAxisAnchor? = nil,
  467. paddingBottom : CGFloat = 0 ,
  468. left: NSLayoutXAxisAnchor? = nil,
  469. paddingLeft: CGFloat = 0,
  470. right: NSLayoutXAxisAnchor? = nil,
  471. paddingRight: CGFloat = 0,
  472. width: CGFloat? = nil,
  473. height: CGFloat? = nil ){
  474. if let top = top {
  475. topAnchor.constraint(equalTo: top, constant: paddingTop).isActive = true
  476. }
  477. if let bottom = bottom {
  478. bottomAnchor.constraint(equalTo: bottom, constant: paddingBottom).isActive = true
  479. }
  480. if let left = left {
  481. leadingAnchor.constraint(equalTo: left, constant: paddingLeft).isActive = true
  482. }
  483. if let right = right {
  484. trailingAnchor.constraint(equalTo: right, constant: paddingRight).isActive = true
  485. }
  486. if let width = width ,width != 0 {
  487. widthAnchor.constraint(equalToConstant: width).isActive = true
  488. }
  489. if let height = height, height != 0 {
  490. heightAnchor.constraint(equalToConstant: height).isActive = true
  491. }
  492. }
  493. func anchorGreaterOreEqualTo(
  494. top : NSLayoutYAxisAnchor? = nil,
  495. paddingTop : CGFloat = 0 ,
  496. bottom : NSLayoutYAxisAnchor? = nil,
  497. paddingBottom : CGFloat = 0 ,
  498. left: NSLayoutXAxisAnchor? = nil,
  499. paddingLeft: CGFloat = 0,
  500. right: NSLayoutXAxisAnchor? = nil,
  501. paddingRight: CGFloat = 0
  502. ) {
  503. if let top = top {
  504. topAnchor.constraint(greaterThanOrEqualTo: top, constant: paddingTop).isActive = true
  505. }
  506. if let bottom = bottom {
  507. bottomAnchor.constraint(greaterThanOrEqualTo: bottom, constant: paddingBottom).isActive = true
  508. }
  509. if let left = left {
  510. leadingAnchor.constraint(greaterThanOrEqualTo: left, constant: paddingLeft).isActive = true
  511. }
  512. if let right = right {
  513. trailingAnchor.constraint(greaterThanOrEqualTo: right, constant: paddingRight).isActive = true
  514. }
  515. }
  516. func anchorLessThanEqualTo(
  517. top : NSLayoutYAxisAnchor? = nil,
  518. paddingTop : CGFloat = 0 ,
  519. bottom : NSLayoutYAxisAnchor? = nil,
  520. paddingBottom : CGFloat = 0 ,
  521. left: NSLayoutXAxisAnchor? = nil,
  522. paddingLeft: CGFloat = 0,
  523. right: NSLayoutXAxisAnchor? = nil,
  524. paddingRight: CGFloat = 0
  525. ) {
  526. if let top = top {
  527. topAnchor.constraint(lessThanOrEqualTo: top, constant: paddingTop).isActive = true
  528. }
  529. if let bottom = bottom {
  530. bottomAnchor.constraint(lessThanOrEqualTo: bottom, constant: paddingBottom).isActive = true
  531. }
  532. if let left = left {
  533. leadingAnchor.constraint(lessThanOrEqualTo: left, constant: paddingLeft).isActive = true
  534. }
  535. if let right = right {
  536. trailingAnchor.constraint(lessThanOrEqualTo: right, constant: paddingRight).isActive = true
  537. }
  538. }
  539. func center(centerX : NSLayoutXAxisAnchor? , paddingX : CGFloat ,
  540. centerY : NSLayoutYAxisAnchor? , paddingY : CGFloat) {
  541. translatesAutoresizingMaskIntoConstraints = false
  542. if let centerX = centerX {
  543. centerXAnchor.constraint(equalTo: centerX , constant: paddingX).isActive = true
  544. }
  545. if let centerY = centerY {
  546. centerYAnchor.constraint(equalTo: centerY , constant: paddingY).isActive = true
  547. }
  548. }
  549. }
  550. extension UIStackView {
  551. func addArrangedSubviews(_ subviews: [UIView]) {
  552. subviews.forEach { addArrangedSubview($0) }
  553. }
  554. }