Popover.swift 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. //
  2. // Popover.swift
  3. // Popover
  4. //
  5. // Created by corin8823 on 8/16/15.
  6. // Copyright (c) 2015 corin8823. All rights reserved.
  7. //
  8. import Foundation
  9. import UIKit
  10. public enum PopoverOption {
  11. case arrowSize(CGSize)
  12. case animationIn(TimeInterval)
  13. case animationOut(TimeInterval)
  14. case cornerRadius(CGFloat)
  15. case sideEdge(CGFloat)
  16. case blackOverlayColor(UIColor)
  17. case overlayBlur(UIBlurEffect.Style)
  18. case type(PopoverType)
  19. case color(UIColor)
  20. case dismissOnBlackOverlayTap(Bool)
  21. case showBlackOverlay(Bool)
  22. case springDamping(CGFloat)
  23. case initialSpringVelocity(CGFloat)
  24. case sideOffset(CGFloat)
  25. case borderColor(UIColor)
  26. }
  27. @objc public enum PopoverType: Int {
  28. case up
  29. case down
  30. case left
  31. case right
  32. case auto
  33. }
  34. @objcMembers
  35. open class Popover: UIView {
  36. // custom property
  37. open var arrowSize: CGSize = CGSize(width: 16.0, height: 10.0)
  38. open var animationIn: TimeInterval = 0.6
  39. open var animationOut: TimeInterval = 0.3
  40. open var cornerRadius: CGFloat = 6.0
  41. open var sideEdge: CGFloat = 20.0
  42. open var popoverType: PopoverType = .down
  43. open var blackOverlayColor: UIColor = UIColor(white: 0.0, alpha: 0.2)
  44. open var overlayBlur: UIBlurEffect?
  45. open var popoverColor: UIColor = UIColor.white
  46. open var dismissOnBlackOverlayTap: Bool = true
  47. open var showBlackOverlay: Bool = true
  48. open var highlightFromView: Bool = false
  49. open var highlightCornerRadius: CGFloat = 0
  50. open var springDamping: CGFloat = 0.7
  51. open var initialSpringVelocity: CGFloat = 3
  52. open var sideOffset: CGFloat = 6.0
  53. open var borderColor: UIColor?
  54. // custom closure
  55. open var willShowHandler: (() -> ())?
  56. open var willDismissHandler: (() -> ())?
  57. open var didShowHandler: (() -> ())?
  58. open var didDismissHandler: (() -> ())?
  59. public fileprivate(set) var blackOverlay: UIControl = UIControl()
  60. fileprivate var containerView: UIView!
  61. fileprivate var contentView: UIView!
  62. fileprivate var contentViewFrame: CGRect!
  63. fileprivate var arrowShowPoint: CGPoint!
  64. public init() {
  65. super.init(frame: .zero)
  66. self.backgroundColor = .clear
  67. self.accessibilityViewIsModal = true
  68. }
  69. public init(showHandler: (() -> ())?, dismissHandler: (() -> ())?) {
  70. super.init(frame: .zero)
  71. self.backgroundColor = .clear
  72. self.didShowHandler = showHandler
  73. self.didDismissHandler = dismissHandler
  74. self.accessibilityViewIsModal = true
  75. }
  76. public init(options: [PopoverOption]?, showHandler: (() -> ())? = nil, dismissHandler: (() -> ())? = nil) {
  77. super.init(frame: .zero)
  78. self.backgroundColor = .clear
  79. self.setOptions(options)
  80. self.didShowHandler = showHandler
  81. self.didDismissHandler = dismissHandler
  82. self.accessibilityViewIsModal = true
  83. }
  84. required public init?(coder aDecoder: NSCoder) {
  85. fatalError("init(coder:) has not been implemented")
  86. }
  87. override open func layoutSubviews() {
  88. super.layoutSubviews()
  89. self.contentView.frame = self.bounds
  90. }
  91. open func showAsDialog(_ contentView: UIView) {
  92. guard let rootView = UIApplication.shared.keyWindow else {
  93. return
  94. }
  95. self.showAsDialog(contentView, inView: rootView)
  96. }
  97. open func showAsDialog(_ contentView: UIView, inView: UIView) {
  98. self.arrowSize = .zero
  99. let point = CGPoint(x: inView.center.x,
  100. y: inView.center.y - contentView.frame.height / 2)
  101. self.show(contentView, point: point, inView: inView)
  102. }
  103. open func show(_ contentView: UIView, fromView: UIView) {
  104. guard let rootView = UIApplication.shared.keyWindow else {
  105. return
  106. }
  107. self.show(contentView, fromView: fromView, inView: rootView)
  108. }
  109. open func show(_ contentView: UIView, fromView: UIView, inView: UIView) {
  110. let point: CGPoint
  111. //TODO: add left/right auto
  112. if self.popoverType == .auto {
  113. if let point = fromView.superview?.convert(fromView.frame.origin, to: nil),
  114. point.y + fromView.frame.height + self.arrowSize.height + contentView.frame.height > inView.frame.height {
  115. self.popoverType = .up
  116. } else {
  117. self.popoverType = .down
  118. }
  119. }
  120. switch self.popoverType {
  121. case .up:
  122. point = inView.convert(
  123. CGPoint(
  124. x: fromView.frame.origin.x + (fromView.frame.size.width / 2),
  125. y: fromView.frame.origin.y
  126. ), from: fromView.superview)
  127. case .down, .auto:
  128. point = inView.convert(
  129. CGPoint(
  130. x: fromView.frame.origin.x + (fromView.frame.size.width / 2),
  131. y: fromView.frame.origin.y + fromView.frame.size.height
  132. ), from: fromView.superview)
  133. case .left:
  134. point = inView.convert(
  135. CGPoint(x: fromView.frame.origin.x - sideOffset,
  136. y: fromView.frame.origin.y + 0.5 * fromView.frame.height
  137. ), from: fromView.superview)
  138. case .right:
  139. point = inView.convert(
  140. CGPoint(x: fromView.frame.origin.x + fromView.frame.size.width + sideOffset,
  141. y: fromView.frame.origin.y + 0.5 * fromView.frame.height
  142. ), from: fromView.superview)
  143. }
  144. if self.highlightFromView {
  145. self.createHighlightLayer(fromView: fromView, inView: inView)
  146. }
  147. self.show(contentView, point: point, inView: inView)
  148. }
  149. open func show(_ contentView: UIView, point: CGPoint) {
  150. guard let rootView = UIApplication.shared.keyWindow else {
  151. return
  152. }
  153. self.show(contentView, point: point, inView: rootView)
  154. }
  155. open func show(_ contentView: UIView, point: CGPoint, inView: UIView) {
  156. if self.dismissOnBlackOverlayTap || self.showBlackOverlay {
  157. self.blackOverlay.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  158. self.blackOverlay.frame = inView.bounds
  159. inView.addSubview(self.blackOverlay)
  160. if showBlackOverlay {
  161. if let overlayBlur = self.overlayBlur {
  162. let effectView = UIVisualEffectView(effect: overlayBlur)
  163. effectView.frame = self.blackOverlay.bounds
  164. effectView.isUserInteractionEnabled = false
  165. self.blackOverlay.addSubview(effectView)
  166. } else {
  167. if !self.highlightFromView {
  168. self.blackOverlay.backgroundColor = self.blackOverlayColor
  169. }
  170. self.blackOverlay.alpha = 0
  171. }
  172. }
  173. if self.dismissOnBlackOverlayTap {
  174. self.blackOverlay.addTarget(self, action: #selector(Popover.dismiss), for: .touchUpInside)
  175. }
  176. }
  177. self.containerView = inView
  178. self.contentView = contentView
  179. self.contentView.backgroundColor = UIColor.clear
  180. self.contentView.layer.cornerRadius = self.cornerRadius
  181. self.contentView.layer.masksToBounds = true
  182. self.arrowShowPoint = point
  183. self.show()
  184. }
  185. open override func accessibilityPerformEscape() -> Bool {
  186. self.dismiss()
  187. return true
  188. }
  189. @objc open func dismiss() {
  190. if self.superview != nil {
  191. self.willDismissHandler?()
  192. UIView.animate(withDuration: self.animationOut, delay: 0,
  193. options: UIView.AnimationOptions(),
  194. animations: {
  195. self.transform = CGAffineTransform(scaleX: 0.0001, y: 0.0001)
  196. self.blackOverlay.alpha = 0
  197. }){ _ in
  198. self.contentView.removeFromSuperview()
  199. self.blackOverlay.removeFromSuperview()
  200. self.removeFromSuperview()
  201. self.transform = CGAffineTransform.identity
  202. self.didDismissHandler?()
  203. }
  204. }
  205. }
  206. override open func draw(_ rect: CGRect) {
  207. super.draw(rect)
  208. let arrow = UIBezierPath()
  209. let color = self.popoverColor
  210. let arrowPoint = self.containerView.convert(self.arrowShowPoint, to: self)
  211. switch self.popoverType {
  212. case .up:
  213. arrow.move(to: CGPoint(x: arrowPoint.x, y: self.bounds.height))
  214. arrow.addLine(
  215. to: CGPoint(
  216. x: arrowPoint.x - self.arrowSize.width * 0.5,
  217. y: self.isCornerLeftArrow ? self.arrowSize.height : self.bounds.height - self.arrowSize.height
  218. )
  219. )
  220. arrow.addLine(to: CGPoint(x: self.cornerRadius, y: self.bounds.height - self.arrowSize.height))
  221. arrow.addArc(
  222. withCenter: CGPoint(
  223. x: self.cornerRadius,
  224. y: self.bounds.height - self.arrowSize.height - self.cornerRadius
  225. ),
  226. radius: self.cornerRadius,
  227. startAngle: self.radians(90),
  228. endAngle: self.radians(180),
  229. clockwise: true)
  230. arrow.addLine(to: CGPoint(x: 0, y: self.cornerRadius))
  231. arrow.addArc(
  232. withCenter: CGPoint(
  233. x: self.cornerRadius,
  234. y: self.cornerRadius
  235. ),
  236. radius: self.cornerRadius,
  237. startAngle: self.radians(180),
  238. endAngle: self.radians(270),
  239. clockwise: true)
  240. arrow.addLine(to: CGPoint(x: self.bounds.width - self.cornerRadius, y: 0))
  241. arrow.addArc(
  242. withCenter: CGPoint(
  243. x: self.bounds.width - self.cornerRadius,
  244. y: self.cornerRadius
  245. ),
  246. radius: self.cornerRadius,
  247. startAngle: self.radians(270),
  248. endAngle: self.radians(0),
  249. clockwise: true)
  250. arrow.addLine(to: CGPoint(x: self.bounds.width, y: self.bounds.height - self.arrowSize.height - self.cornerRadius))
  251. arrow.addArc(
  252. withCenter: CGPoint(
  253. x: self.bounds.width - self.cornerRadius,
  254. y: self.bounds.height - self.arrowSize.height - self.cornerRadius
  255. ),
  256. radius: self.cornerRadius,
  257. startAngle: self.radians(0),
  258. endAngle: self.radians(90),
  259. clockwise: true)
  260. arrow.addLine(
  261. to: CGPoint(
  262. x: arrowPoint.x + self.arrowSize.width * 0.5,
  263. y: self.isCornerRightArrow ? self.arrowSize.height : self.bounds.height - self.arrowSize.height
  264. )
  265. )
  266. case .down, .auto:
  267. arrow.move(to: CGPoint(x: arrowPoint.x, y: 0))
  268. if self.isCloseToCornerRightArrow && !self.isCornerRightArrow {
  269. if !isBehindCornerRightArrow {
  270. arrow.addLine(to: CGPoint(x: self.bounds.width - self.cornerRadius, y: self.arrowSize.height))
  271. arrow.addArc(
  272. withCenter: CGPoint(x: self.bounds.width - self.cornerRadius, y: self.arrowSize.height + self.cornerRadius),
  273. radius: self.cornerRadius,
  274. startAngle: self.radians(270.0),
  275. endAngle: self.radians(0),
  276. clockwise: true)
  277. } else {
  278. arrow.addLine(to: CGPoint(x: self.bounds.width, y: self.arrowSize.height + self.cornerRadius))
  279. arrow.addLine(to: CGPoint(x: self.bounds.width, y: self.arrowSize.height))
  280. }
  281. } else {
  282. arrow.addLine(
  283. to: CGPoint(
  284. x: self.isBehindCornerLeftArrow ? self.frame.minX - self.arrowSize.width * 0.5 : arrowPoint.x + self.arrowSize.width * 0.5,
  285. y: self.isCornerRightArrow ? self.arrowSize.height + self.bounds.height : self.arrowSize.height
  286. )
  287. )
  288. arrow.addLine(to: CGPoint(x: self.bounds.width - self.cornerRadius, y: self.arrowSize.height))
  289. arrow.addArc(
  290. withCenter: CGPoint(
  291. x: self.bounds.width - self.cornerRadius,
  292. y: self.arrowSize.height + self.cornerRadius
  293. ),
  294. radius: self.cornerRadius,
  295. startAngle: self.radians(270.0),
  296. endAngle: self.radians(0),
  297. clockwise: true)
  298. }
  299. arrow.addLine(to: CGPoint(x: self.bounds.width, y: self.bounds.height - self.cornerRadius))
  300. arrow.addArc(
  301. withCenter: CGPoint(
  302. x: self.bounds.width - self.cornerRadius,
  303. y: self.bounds.height - self.cornerRadius
  304. ),
  305. radius: self.cornerRadius,
  306. startAngle: self.radians(0),
  307. endAngle: self.radians(90),
  308. clockwise: true)
  309. arrow.addLine(to: CGPoint(x: 0, y: self.bounds.height))
  310. arrow.addArc(
  311. withCenter: CGPoint(
  312. x: self.cornerRadius,
  313. y: self.bounds.height - self.cornerRadius
  314. ),
  315. radius: self.cornerRadius,
  316. startAngle: self.radians(90),
  317. endAngle: self.radians(180),
  318. clockwise: true)
  319. arrow.addLine(to: CGPoint(x: 0, y: self.arrowSize.height + self.cornerRadius))
  320. if !isBehindCornerLeftArrow {
  321. arrow.addArc(
  322. withCenter: CGPoint(
  323. x: self.cornerRadius,
  324. y: self.arrowSize.height + self.cornerRadius
  325. ),
  326. radius: self.cornerRadius,
  327. startAngle: self.radians(180),
  328. endAngle: self.radians(270),
  329. clockwise: true)
  330. }
  331. if isBehindCornerRightArrow {
  332. arrow.addLine(to: CGPoint(
  333. x: self.bounds.width - self.arrowSize.width * 0.5,
  334. y: self.isCornerLeftArrow ? self.arrowSize.height + self.bounds.height : self.arrowSize.height))
  335. } else if isCloseToCornerLeftArrow && !isCornerLeftArrow {
  336. () // skipping this line in that case
  337. } else {
  338. arrow.addLine(to: CGPoint(x: arrowPoint.x - self.arrowSize.width * 0.5,
  339. y: self.isCornerLeftArrow ? self.arrowSize.height + self.bounds.height : self.arrowSize.height))
  340. }
  341. case .left:
  342. arrow.move(to: CGPoint(x: self.bounds.width, y: self.bounds.height * 0.5))
  343. arrow.addLine(
  344. to: CGPoint(
  345. x: self.bounds.width - self.arrowSize.height,
  346. y: self.bounds.height * 0.5 + self.arrowSize.width * 0.5
  347. ))
  348. arrow.addLine(to: CGPoint(x:self.bounds.width - self.arrowSize.height, y: self.bounds.height - self.cornerRadius))
  349. arrow.addArc(
  350. withCenter: CGPoint(
  351. x: self.bounds.width - self.arrowSize.height - self.cornerRadius,
  352. y: self.bounds.height - self.cornerRadius
  353. ),
  354. radius: self.cornerRadius,
  355. startAngle: self.radians(0.0),
  356. endAngle: self.radians(90),
  357. clockwise: true)
  358. arrow.addLine(to: CGPoint(x: self.cornerRadius, y: self.bounds.height))
  359. arrow.addArc(
  360. withCenter: CGPoint(
  361. x: self.cornerRadius,
  362. y: self.bounds.height - self.cornerRadius
  363. ),
  364. radius: self.cornerRadius,
  365. startAngle: self.radians(90),
  366. endAngle: self.radians(180),
  367. clockwise: true)
  368. arrow.addLine(to: CGPoint(x: 0, y: self.cornerRadius))
  369. arrow.addArc(
  370. withCenter: CGPoint(
  371. x: self.cornerRadius,
  372. y: self.cornerRadius
  373. ),
  374. radius: self.cornerRadius,
  375. startAngle: self.radians(180),
  376. endAngle: self.radians(270),
  377. clockwise: true)
  378. arrow.addLine(to: CGPoint(x: self.bounds.width - self.arrowSize.height - self.cornerRadius, y: 0))
  379. arrow.addArc(
  380. withCenter: CGPoint(x: self.bounds.width - self.arrowSize.height - self.cornerRadius,
  381. y: self.cornerRadius
  382. ),
  383. radius: self.cornerRadius,
  384. startAngle: self.radians(270),
  385. endAngle: self.radians(0),
  386. clockwise: true)
  387. arrow.addLine(to: CGPoint(x: self.bounds.width - self.arrowSize.height,
  388. y: self.bounds.height * 0.5 - self.arrowSize.width * 0.5
  389. ))
  390. case .right:
  391. arrow.move(to: CGPoint(x: arrowPoint.x, y: self.bounds.height * 0.5))
  392. arrow.addLine(
  393. to: CGPoint(
  394. x: arrowPoint.x + self.arrowSize.height,
  395. y: self.bounds.height * 0.5 + 0.5 * self.arrowSize.width
  396. ))
  397. arrow.addLine(
  398. to: CGPoint(
  399. x: arrowPoint.x + self.arrowSize.height,
  400. y: self.bounds.height - self.cornerRadius
  401. ))
  402. arrow.addArc(
  403. withCenter: CGPoint(
  404. x: arrowPoint.x + self.arrowSize.height + self.cornerRadius,
  405. y: self.bounds.height - self.cornerRadius
  406. ),
  407. radius: self.cornerRadius,
  408. startAngle: self.radians(180.0),
  409. endAngle: self.radians(90),
  410. clockwise: false)
  411. arrow.addLine(to: CGPoint(x: self.bounds.width + arrowPoint.x - self.cornerRadius, y: self.bounds.height))
  412. arrow.addArc(
  413. withCenter: CGPoint(
  414. x: self.bounds.width + arrowPoint.x - self.cornerRadius,
  415. y: self.bounds.height - self.cornerRadius
  416. ),
  417. radius: self.cornerRadius,
  418. startAngle: self.radians(90),
  419. endAngle: self.radians(0),
  420. clockwise: false)
  421. arrow.addLine(to: CGPoint(x: self.bounds.width + arrowPoint.x, y: self.cornerRadius))
  422. arrow.addArc(
  423. withCenter: CGPoint(
  424. x: self.bounds.width + arrowPoint.x - self.cornerRadius,
  425. y: self.cornerRadius
  426. ),
  427. radius: self.cornerRadius,
  428. startAngle: self.radians(0),
  429. endAngle: self.radians(-90),
  430. clockwise: false)
  431. arrow.addLine(to: CGPoint(x: arrowPoint.x + self.arrowSize.height - self.cornerRadius, y: 0))
  432. arrow.addArc(
  433. withCenter: CGPoint(x: arrowPoint.x + self.arrowSize.height + self.cornerRadius,
  434. y: self.cornerRadius
  435. ),
  436. radius: self.cornerRadius,
  437. startAngle: self.radians(-90),
  438. endAngle: self.radians(-180),
  439. clockwise: false)
  440. arrow.addLine(to: CGPoint(x: arrowPoint.x + self.arrowSize.height,
  441. y: self.bounds.height * 0.5 - self.arrowSize.width * 0.5))
  442. }
  443. color.setFill()
  444. arrow.fill()
  445. if let borderColor = borderColor {
  446. borderColor.setStroke()
  447. arrow.stroke()
  448. }
  449. }
  450. }
  451. private extension Popover {
  452. func setOptions(_ options: [PopoverOption]?){
  453. if let options = options {
  454. for option in options {
  455. switch option {
  456. case let .arrowSize(value):
  457. self.arrowSize = value
  458. case let .animationIn(value):
  459. self.animationIn = value
  460. case let .animationOut(value):
  461. self.animationOut = value
  462. case let .cornerRadius(value):
  463. self.cornerRadius = value
  464. case let .sideEdge(value):
  465. self.sideEdge = value
  466. case let .blackOverlayColor(value):
  467. self.blackOverlayColor = value
  468. case let .overlayBlur(style):
  469. self.overlayBlur = UIBlurEffect(style: style)
  470. case let .type(value):
  471. self.popoverType = value
  472. case let .color(value):
  473. self.popoverColor = value
  474. case let .dismissOnBlackOverlayTap(value):
  475. self.dismissOnBlackOverlayTap = value
  476. case let .showBlackOverlay(value):
  477. self.showBlackOverlay = value
  478. case let .springDamping(value):
  479. self.springDamping = value
  480. case let .initialSpringVelocity(value):
  481. self.initialSpringVelocity = value
  482. case let .sideOffset(value):
  483. self.sideOffset = value
  484. case let .borderColor(value):
  485. self.borderColor = value
  486. }
  487. }
  488. }
  489. }
  490. func create() {
  491. var frame = self.contentView.frame
  492. switch self.popoverType {
  493. case .up, .down, .auto:
  494. frame.origin.x = self.arrowShowPoint.x - frame.size.width * 0.5
  495. case .left, .right:
  496. frame.origin.y = self.arrowShowPoint.y - frame.size.height * 0.5
  497. }
  498. var sideEdge: CGFloat = 0.0
  499. if frame.size.width < self.containerView.frame.size.width {
  500. sideEdge = self.sideEdge
  501. }
  502. let outerSideEdge = frame.maxX - self.containerView.bounds.size.width
  503. if outerSideEdge > 0 {
  504. frame.origin.x -= (outerSideEdge + sideEdge)
  505. } else {
  506. if frame.minX < 0 {
  507. frame.origin.x += abs(frame.minX) + sideEdge
  508. }
  509. }
  510. self.frame = frame
  511. let arrowPoint = self.containerView.convert(self.arrowShowPoint, to: self)
  512. var anchorPoint: CGPoint
  513. switch self.popoverType {
  514. case .up:
  515. frame.origin.y = self.arrowShowPoint.y - frame.height - self.arrowSize.height
  516. anchorPoint = CGPoint(x: arrowPoint.x / frame.size.width, y: 1)
  517. case .down, .auto:
  518. frame.origin.y = self.arrowShowPoint.y
  519. anchorPoint = CGPoint(x: arrowPoint.x / frame.size.width, y: 0)
  520. case .left:
  521. frame.origin.x = self.arrowShowPoint.x - frame.size.width - self.arrowSize.height
  522. anchorPoint = CGPoint(x: 1, y: 0.5)
  523. case .right:
  524. frame.origin.x = self.arrowShowPoint.x
  525. anchorPoint = CGPoint(x: 0, y: 0.5)
  526. }
  527. if self.arrowSize == .zero {
  528. anchorPoint = CGPoint(x: 0.5, y: 0.5)
  529. }
  530. let lastAnchor = self.layer.anchorPoint
  531. self.layer.anchorPoint = anchorPoint
  532. let x = self.layer.position.x + (anchorPoint.x - lastAnchor.x) * self.layer.bounds.size.width
  533. let y = self.layer.position.y + (anchorPoint.y - lastAnchor.y) * self.layer.bounds.size.height
  534. self.layer.position = CGPoint(x: x, y: y)
  535. switch self.popoverType {
  536. case .up, .down, .auto:
  537. frame.size.height += self.arrowSize.height
  538. case .left, .right:
  539. frame.size.width += self.arrowSize.height
  540. }
  541. self.frame = frame
  542. }
  543. func createHighlightLayer(fromView: UIView, inView: UIView) {
  544. let path = UIBezierPath(rect: inView.bounds)
  545. let highlightRect = inView.convert(fromView.frame, from: fromView.superview)
  546. let highlightPath = UIBezierPath(roundedRect: highlightRect, cornerRadius: self.highlightCornerRadius)
  547. path.append(highlightPath)
  548. path.usesEvenOddFillRule = true
  549. let fillLayer = CAShapeLayer()
  550. fillLayer.path = path.cgPath
  551. fillLayer.fillRule = CAShapeLayerFillRule.evenOdd
  552. fillLayer.fillColor = self.blackOverlayColor.cgColor
  553. self.blackOverlay.layer.addSublayer(fillLayer)
  554. }
  555. func show() {
  556. self.setNeedsDisplay()
  557. switch self.popoverType {
  558. case .up:
  559. self.contentView.frame.origin.y = 0.0
  560. case .down, .auto:
  561. self.contentView.frame.origin.y = self.arrowSize.height
  562. case .left, .right:
  563. self.contentView.frame.origin.x = 0
  564. }
  565. self.addSubview(self.contentView)
  566. self.containerView.addSubview(self)
  567. self.create()
  568. self.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)
  569. self.willShowHandler?()
  570. UIView.animate(
  571. withDuration: self.animationIn,
  572. delay: 0,
  573. usingSpringWithDamping: self.springDamping,
  574. initialSpringVelocity: self.initialSpringVelocity,
  575. options: UIView.AnimationOptions(),
  576. animations: {
  577. self.transform = CGAffineTransform.identity
  578. }){ _ in
  579. self.didShowHandler?()
  580. }
  581. UIView.animate(
  582. withDuration: self.animationIn / 3,
  583. delay: 0,
  584. options: .curveLinear,
  585. animations: {
  586. self.blackOverlay.alpha = 1
  587. }, completion: nil)
  588. }
  589. var isCloseToCornerLeftArrow: Bool {
  590. return self.arrowShowPoint.x < self.frame.origin.x + arrowSize.width/2 + cornerRadius
  591. }
  592. var isCloseToCornerRightArrow: Bool {
  593. return self.arrowShowPoint.x > (self.frame.origin.x + self.bounds.width) - arrowSize.width/2 - cornerRadius
  594. }
  595. var isCornerLeftArrow: Bool {
  596. return self.arrowShowPoint.x == self.frame.origin.x
  597. }
  598. var isCornerRightArrow: Bool {
  599. return self.arrowShowPoint.x == self.frame.origin.x + self.bounds.width
  600. }
  601. var isBehindCornerLeftArrow: Bool {
  602. return self.arrowShowPoint.x < self.frame.origin.x
  603. }
  604. var isBehindCornerRightArrow: Bool {
  605. return self.arrowShowPoint.x > self.frame.origin.x + self.bounds.width
  606. }
  607. func radians(_ degrees: CGFloat) -> CGFloat {
  608. return CGFloat.pi * degrees / 180
  609. }
  610. }