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.

198 lines
6.8 KiB

  1. // The MIT License (MIT)
  2. //
  3. // Copyright (c) 2016 Luke Zhao <me@lkzhao.com>
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. import UIKit
  23. #if os(iOS)
  24. protocol HeroDebugViewDelegate: class {
  25. func onProcessSliderChanged(progress: Float)
  26. func onPerspectiveChanged(translation: CGPoint, rotation: CGFloat, scale: CGFloat)
  27. func on3D(wants3D: Bool)
  28. func onDisplayArcCurve(wantsCurve: Bool)
  29. func onDone()
  30. }
  31. class HeroDebugView: UIView {
  32. var backgroundView: UIView!
  33. var debugSlider: UISlider!
  34. var perspectiveButton: UIButton!
  35. var doneButton: UIButton!
  36. var arcCurveButton: UIButton?
  37. weak var delegate: HeroDebugViewDelegate?
  38. var panGR: UIPanGestureRecognizer!
  39. var pinchGR: UIPinchGestureRecognizer!
  40. var showControls: Bool = false {
  41. didSet {
  42. layoutSubviews()
  43. }
  44. }
  45. var showOnTop: Bool = false
  46. var rotation: CGFloat = π / 6
  47. var scale: CGFloat = 0.6
  48. var translation: CGPoint = .zero
  49. var progress: Float {
  50. return debugSlider.value
  51. }
  52. init(initialProcess: Float, showCurveButton: Bool, showOnTop: Bool) {
  53. super.init(frame: .zero)
  54. self.showOnTop = showOnTop
  55. backgroundView = UIView(frame: .zero)
  56. backgroundView.backgroundColor = UIColor(white: 1.0, alpha: 0.95)
  57. backgroundView.layer.shadowColor = UIColor.darkGray.cgColor
  58. backgroundView.layer.shadowOpacity = 0.3
  59. backgroundView.layer.shadowRadius = 5
  60. backgroundView.layer.shadowOffset = CGSize.zero
  61. addSubview(backgroundView)
  62. doneButton = UIButton(type: .system)
  63. doneButton.setTitle("Done", for: .normal)
  64. doneButton.addTarget(self, action: #selector(onDone), for: .touchUpInside)
  65. backgroundView.addSubview(doneButton)
  66. perspectiveButton = UIButton(type: .system)
  67. perspectiveButton.setTitle("3D View", for: .normal)
  68. perspectiveButton.addTarget(self, action: #selector(onPerspective), for: .touchUpInside)
  69. backgroundView.addSubview(perspectiveButton)
  70. if showCurveButton {
  71. arcCurveButton = UIButton(type: .system)
  72. arcCurveButton!.setTitle("Show Arcs", for: .normal)
  73. arcCurveButton!.addTarget(self, action: #selector(onDisplayArcCurve), for: .touchUpInside)
  74. backgroundView.addSubview(arcCurveButton!)
  75. }
  76. debugSlider = UISlider(frame: .zero)
  77. debugSlider.layer.zPosition = 1000
  78. debugSlider.minimumValue = 0
  79. debugSlider.maximumValue = 1
  80. debugSlider.addTarget(self, action: #selector(onSlide), for: .valueChanged)
  81. debugSlider.isUserInteractionEnabled = true
  82. debugSlider.value = initialProcess
  83. backgroundView.addSubview(debugSlider)
  84. panGR = UIPanGestureRecognizer(target: self, action: #selector(pan))
  85. panGR.delegate = self
  86. panGR.maximumNumberOfTouches = 1
  87. addGestureRecognizer(panGR)
  88. pinchGR = UIPinchGestureRecognizer(target: self, action: #selector(pinch))
  89. pinchGR.delegate = self
  90. addGestureRecognizer(pinchGR)
  91. }
  92. required public init?(coder aDecoder: NSCoder) {
  93. fatalError("init(coder:) has not been implemented")
  94. }
  95. public override func layoutSubviews() {
  96. super.layoutSubviews()
  97. var backgroundFrame = bounds
  98. let safeInset: CGFloat
  99. if #available(iOS 11.0, *) {
  100. safeInset = showOnTop ? safeAreaInsets.top : safeAreaInsets.bottom
  101. } else {
  102. safeInset = 0
  103. }
  104. backgroundFrame.size.height = 72 + safeInset
  105. if showOnTop {
  106. backgroundFrame.origin.y = showControls ? 0 : -80
  107. } else {
  108. backgroundFrame.origin.y = bounds.maxY - CGFloat(showControls ? 72.0 + safeInset : -8.0)
  109. }
  110. backgroundView.frame = backgroundFrame
  111. var sliderFrame = bounds.insetBy(dx: 10, dy: 0)
  112. sliderFrame.size.height = 44
  113. sliderFrame.origin.y = showOnTop ? 28 + safeInset : 28
  114. debugSlider.frame = sliderFrame
  115. perspectiveButton.sizeToFit()
  116. perspectiveButton.frame.origin = CGPoint(x: bounds.maxX - perspectiveButton.bounds.width - 10, y: showOnTop ? 4 + safeInset : 4)
  117. doneButton.sizeToFit()
  118. doneButton.frame.origin = CGPoint(x: 10, y: showOnTop ? 4 + safeInset : 4)
  119. arcCurveButton?.sizeToFit()
  120. arcCurveButton?.center = CGPoint(x: center.x, y: doneButton.center.y)
  121. }
  122. var startRotation: CGFloat = 0
  123. @objc public func pan() {
  124. if panGR.state == .began {
  125. startRotation = rotation
  126. }
  127. rotation = startRotation + panGR.translation(in: nil).x / 150
  128. if rotation > π {
  129. rotation -= 2 * π
  130. } else if rotation < -π {
  131. rotation += 2 * π
  132. }
  133. delegate?.onPerspectiveChanged(translation: translation, rotation: rotation, scale: scale)
  134. }
  135. var startLocation: CGPoint = .zero
  136. var startTranslation: CGPoint = .zero
  137. var startScale: CGFloat = 1
  138. @objc public func pinch() {
  139. switch pinchGR.state {
  140. case .began:
  141. startLocation = pinchGR.location(in: nil)
  142. startTranslation = translation
  143. startScale = scale
  144. fallthrough
  145. case .changed:
  146. if pinchGR.numberOfTouches >= 2 {
  147. scale = min(1, max(0.2, startScale * pinchGR.scale))
  148. translation = startTranslation + pinchGR.location(in: nil) - startLocation
  149. delegate?.onPerspectiveChanged(translation: translation, rotation: rotation, scale: scale)
  150. }
  151. default:
  152. break
  153. }
  154. }
  155. @objc public func onDone() {
  156. delegate?.onDone()
  157. }
  158. @objc public func onPerspective() {
  159. perspectiveButton.isSelected = !perspectiveButton.isSelected
  160. delegate?.on3D(wants3D: perspectiveButton.isSelected)
  161. }
  162. @objc public func onDisplayArcCurve() {
  163. arcCurveButton!.isSelected = !arcCurveButton!.isSelected
  164. delegate?.onDisplayArcCurve(wantsCurve: arcCurveButton!.isSelected)
  165. }
  166. @objc public func onSlide() {
  167. delegate?.onProcessSliderChanged(progress: debugSlider.value)
  168. }
  169. }
  170. extension HeroDebugView: UIGestureRecognizerDelegate {
  171. public override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
  172. return perspectiveButton.isSelected
  173. }
  174. }
  175. #endif