CameraViewControllerConstraint.swift 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. //
  2. // CameraViewControllerConstraint.swift
  3. // CameraViewControllerConstraint
  4. //
  5. // Created by Pedro Paulo de Amorim.
  6. // Copyright (c) 2016 zero. All rights reserved.
  7. //
  8. // Modified by Kevin Kieffer on 2019/08/06. Changes as follows:
  9. // Changed configCameraOverlayWidthConstraint() to use a multipler to provide a rectangle instead of a square,
  10. // the rectangle being created by using an aspect ratio from the CroppingParameters
  11. //
  12. // Cleaned up the constraints for the flash, swap, and library buttons to work properly in both landscape orientations
  13. import UIKit
  14. import AVFoundation
  15. /**
  16. * This extension provides the configuration of
  17. * constraints for CameraViewController.
  18. */
  19. extension CameraViewController {
  20. /**
  21. * To attach the view to the edges of the superview, it needs
  22. to be pinned on the sides of the self.view, based on the
  23. edges of this superview.
  24. * This configure the cameraView to show, in real time, the
  25. * camera.
  26. */
  27. func configCameraViewConstraints() {
  28. [.left, .right, .top, .bottom].forEach({
  29. view.addConstraint(NSLayoutConstraint(
  30. item: cameraView,
  31. attribute: $0,
  32. relatedBy: .equal,
  33. toItem: view,
  34. attribute: $0,
  35. multiplier: 1.0,
  36. constant: 0))
  37. })
  38. }
  39. func removeCameraButtonConstraints() {
  40. view.autoRemoveConstraint(cameraButtonEdgeConstraint)
  41. view.autoRemoveConstraint(cameraButtonGravityConstraint)
  42. }
  43. /**
  44. * Add the constraints based on the device orientation,
  45. * this pin the button on the bottom part of the screen
  46. * when the device is portrait, when landscape, pin
  47. * the button on the right part of the screen.
  48. */
  49. func configCameraButtonEdgeConstraint(_ statusBarOrientation: UIInterfaceOrientation) {
  50. let attribute : NSLayoutConstraint.Attribute = {
  51. switch statusBarOrientation {
  52. case .portrait: return .bottom
  53. case .landscapeRight: return .right
  54. case .landscapeLeft: return .left
  55. default: return .top
  56. }
  57. }()
  58. let constraintPortrait = statusBarOrientation == .portrait || statusBarOrientation == .landscapeRight
  59. cameraButtonEdgeConstraint = NSLayoutConstraint(
  60. item: cameraButton,
  61. attribute: attribute,
  62. relatedBy: .equal,
  63. toItem: view,
  64. attribute: attribute,
  65. multiplier: 1.0,
  66. constant: constraintPortrait ? -16 : 16)
  67. view.addConstraint(cameraButtonEdgeConstraint!)
  68. }
  69. /**
  70. * Add the constraints based on the device orientation,
  71. * centerX the button based on the width of screen.
  72. * When the device is landscape orientation, centerY
  73. * the button based on the height of screen.
  74. */
  75. func configCameraButtonGravityConstraint(_ portrait: Bool) {
  76. let attribute : NSLayoutConstraint.Attribute = portrait ? .centerX : .centerY
  77. cameraButtonGravityConstraint = NSLayoutConstraint(
  78. item: cameraButton,
  79. attribute: attribute,
  80. relatedBy: .equal,
  81. toItem: view,
  82. attribute: attribute,
  83. multiplier: 1.0,
  84. constant: 0)
  85. view.addConstraint(cameraButtonGravityConstraint!)
  86. }
  87. /**
  88. * Remove the constraints of container.
  89. */
  90. func removeContainerConstraints() {
  91. view.autoRemoveConstraint(containerButtonsEdgeOneConstraint)
  92. view.autoRemoveConstraint(containerButtonsEdgeTwoConstraint)
  93. view.autoRemoveConstraint(containerButtonsGravityConstraint)
  94. }
  95. /**
  96. * Configure the edges constraints of container that
  97. * handle the center position of SwapButton and
  98. * LibraryButton.
  99. */
  100. func configContainerEdgeConstraint(_ statusBarOrientation : UIInterfaceOrientation) {
  101. let attributeOne : NSLayoutConstraint.Attribute
  102. let attributeTwo : NSLayoutConstraint.Attribute
  103. switch statusBarOrientation {
  104. case .portrait:
  105. attributeOne = .left
  106. attributeTwo = .right
  107. break
  108. case .landscapeRight:
  109. attributeOne = .bottom
  110. attributeTwo = .top
  111. break
  112. case .landscapeLeft:
  113. attributeOne = .top
  114. attributeTwo = .bottom
  115. break
  116. default:
  117. attributeOne = .right
  118. attributeTwo = .left
  119. break
  120. }
  121. containerButtonsEdgeOneConstraint = NSLayoutConstraint(
  122. item: containerSwapLibraryButton,
  123. attribute: attributeOne,
  124. relatedBy: .equal,
  125. toItem: cameraButton,
  126. attribute: attributeTwo,
  127. multiplier: 1.0,
  128. constant: 0)
  129. view.addConstraint(containerButtonsEdgeOneConstraint!)
  130. containerButtonsEdgeTwoConstraint = NSLayoutConstraint(
  131. item: containerSwapLibraryButton,
  132. attribute: attributeTwo,
  133. relatedBy: .equal,
  134. toItem: view,
  135. attribute: attributeTwo,
  136. multiplier: 1.0,
  137. constant: 0)
  138. view.addConstraint(containerButtonsEdgeTwoConstraint!)
  139. }
  140. /**
  141. * Configure the gravity of container, based on the
  142. * orientation of the device.
  143. */
  144. func configContainerGravityConstraint(_ statusBarOrientation : UIInterfaceOrientation) {
  145. let attributeCenter : NSLayoutConstraint.Attribute = statusBarOrientation.isPortrait ? .centerY : .centerX
  146. containerButtonsGravityConstraint = NSLayoutConstraint(
  147. item: containerSwapLibraryButton,
  148. attribute: attributeCenter,
  149. relatedBy: .equal,
  150. toItem: cameraButton,
  151. attribute: attributeCenter,
  152. multiplier: 1.0,
  153. constant: 0)
  154. view.addConstraint(containerButtonsGravityConstraint!)
  155. }
  156. /**
  157. * Remove the SwapButton constraints to be updated when
  158. * the device was rotated.
  159. */
  160. func removeSwapButtonConstraints() {
  161. view.autoRemoveConstraint(swapButtonEdgeOneConstraint)
  162. view.autoRemoveConstraint(swapButtonEdgeTwoConstraint)
  163. view.autoRemoveConstraint(swapButtonGravityConstraint)
  164. }
  165. /**
  166. * If the device is portrait, pin the SwapButton on the
  167. * right side of the CameraButton.
  168. * If landscape, pin the SwapButton on the top of the
  169. * CameraButton.
  170. */
  171. func configSwapButtonEdgeConstraint(_ statusBarOrientation : UIInterfaceOrientation) {
  172. let attributeOne : NSLayoutConstraint.Attribute
  173. let attributeTwo : NSLayoutConstraint.Attribute
  174. switch statusBarOrientation {
  175. case .portrait:
  176. attributeOne = .top
  177. attributeTwo = .bottom
  178. break
  179. case .landscapeRight:
  180. attributeOne = .left
  181. attributeTwo = .right
  182. break
  183. case .landscapeLeft:
  184. attributeOne = .right
  185. attributeTwo = .left
  186. break
  187. default:
  188. attributeOne = .bottom
  189. attributeTwo = .top
  190. break
  191. }
  192. swapButtonEdgeOneConstraint = NSLayoutConstraint(
  193. item: swapButton,
  194. attribute: attributeOne,
  195. relatedBy: .equal,
  196. toItem: containerSwapLibraryButton,
  197. attribute: attributeOne,
  198. multiplier: 1.0,
  199. constant: 0)
  200. view.addConstraint(swapButtonEdgeOneConstraint!)
  201. swapButtonEdgeTwoConstraint = NSLayoutConstraint(
  202. item: swapButton,
  203. attribute: attributeTwo,
  204. relatedBy: .equal,
  205. toItem: containerSwapLibraryButton,
  206. attribute: attributeTwo,
  207. multiplier: 1.0,
  208. constant: 0)
  209. view.addConstraint(swapButtonEdgeTwoConstraint!)
  210. }
  211. /**
  212. * Configure the center of SwapButton, based on the
  213. * axis center of CameraButton.
  214. */
  215. func configSwapButtonGravityConstraint(_ statusBarOrientation: UIInterfaceOrientation) {
  216. let portrait = statusBarOrientation == .portrait
  217. let landLeft = statusBarOrientation == .landscapeLeft
  218. swapButtonGravityConstraint = NSLayoutConstraint(
  219. item: swapButton,
  220. attribute: portrait ? .right : (landLeft ? .bottom : .top),
  221. relatedBy: portrait || landLeft ? .lessThanOrEqual : .greaterThanOrEqual,
  222. toItem: containerSwapLibraryButton,
  223. attribute: portrait ? .centerX : .centerY,
  224. multiplier: 1.0,
  225. constant: (portrait || landLeft ? -4.0 : 4.0) * DeviceConfig.SCREEN_MULTIPLIER)
  226. view.addConstraint(swapButtonGravityConstraint!)
  227. }
  228. func removeCloseButtonConstraints() {
  229. view.autoRemoveConstraint(closeButtonEdgeConstraint)
  230. view.autoRemoveConstraint(closeButtonGravityConstraint)
  231. }
  232. /**
  233. * Pin the close button to the left of the superview.
  234. */
  235. func configCloseButtonEdgeConstraint(_ statusBarOrientation : UIInterfaceOrientation) {
  236. let attribute : NSLayoutConstraint.Attribute = {
  237. switch statusBarOrientation {
  238. case .portrait: return .left
  239. case .landscapeRight, .landscapeLeft: return .centerX
  240. default: return .right
  241. }
  242. }()
  243. closeButtonEdgeConstraint = NSLayoutConstraint(
  244. item: closeButton,
  245. attribute: attribute,
  246. relatedBy: .equal,
  247. toItem: attribute != .centerX ? view : cameraButton,
  248. attribute: attribute,
  249. multiplier: 1.0,
  250. constant: attribute != .centerX ? 16 : 0)
  251. view.addConstraint(closeButtonEdgeConstraint!)
  252. }
  253. /**
  254. * Add the constraint for the CloseButton, based on
  255. * the device orientation.
  256. * If portrait, it pin the CloseButton on the CenterY
  257. * of the CameraButton.
  258. * Else if landscape, pin this button on the Bottom
  259. * of superview.
  260. */
  261. func configCloseButtonGravityConstraint(_ statusBarOrientation : UIInterfaceOrientation) {
  262. let attribute : NSLayoutConstraint.Attribute
  263. let constant : CGFloat
  264. switch statusBarOrientation {
  265. case .portrait:
  266. attribute = .centerY
  267. constant = 0.0
  268. break
  269. case .landscapeRight:
  270. attribute = .bottom
  271. constant = -16.0
  272. break
  273. case .landscapeLeft:
  274. attribute = .top
  275. constant = 16.0
  276. break
  277. default:
  278. attribute = .centerX
  279. constant = 0.0
  280. break
  281. }
  282. closeButtonGravityConstraint = NSLayoutConstraint(
  283. item: closeButton,
  284. attribute: attribute,
  285. relatedBy: .equal,
  286. toItem: attribute == .bottom || attribute == .top ? view : cameraButton,
  287. attribute: attribute,
  288. multiplier: 1.0,
  289. constant: constant)
  290. view.addConstraint(closeButtonGravityConstraint!)
  291. }
  292. /**
  293. * Remove the LibraryButton constraints to be updated when
  294. * the device was rotated.
  295. */
  296. func removeLibraryButtonConstraints() {
  297. view.autoRemoveConstraint(libraryButtonEdgeOneConstraint)
  298. view.autoRemoveConstraint(libraryButtonEdgeTwoConstraint)
  299. view.autoRemoveConstraint(libraryButtonGravityConstraint)
  300. }
  301. /**
  302. * Add the constraint of the LibraryButton, if the device
  303. * orientation is portrait, pin the right side of SwapButton
  304. * to the left side of LibraryButton.
  305. * If landscape, pin the bottom side of CameraButton on the
  306. * top side of LibraryButton.
  307. */
  308. func configLibraryEdgeButtonConstraint(_ statusBarOrientation : UIInterfaceOrientation) {
  309. let attributeOne : NSLayoutConstraint.Attribute
  310. let attributeTwo : NSLayoutConstraint.Attribute
  311. switch statusBarOrientation {
  312. case .portrait:
  313. attributeOne = .top
  314. attributeTwo = .bottom
  315. break
  316. case .landscapeRight:
  317. attributeOne = .left
  318. attributeTwo = .right
  319. break
  320. case .landscapeLeft:
  321. attributeOne = .right
  322. attributeTwo = .left
  323. break
  324. default:
  325. attributeOne = .bottom
  326. attributeTwo = .top
  327. break
  328. }
  329. libraryButtonEdgeOneConstraint = NSLayoutConstraint(
  330. item: libraryButton,
  331. attribute: attributeOne,
  332. relatedBy: .equal,
  333. toItem: containerSwapLibraryButton,
  334. attribute: attributeOne,
  335. multiplier: 1.0,
  336. constant: 0)
  337. view.addConstraint(libraryButtonEdgeOneConstraint!)
  338. libraryButtonEdgeTwoConstraint = NSLayoutConstraint(
  339. item: libraryButton,
  340. attribute: attributeTwo,
  341. relatedBy: .equal,
  342. toItem: containerSwapLibraryButton,
  343. attribute: attributeTwo,
  344. multiplier: 1.0,
  345. constant: 0)
  346. view.addConstraint(libraryButtonEdgeTwoConstraint!)
  347. }
  348. /**
  349. * Set the center gravity of the LibraryButton based
  350. * on the position of CameraButton.
  351. */
  352. func configLibraryGravityButtonConstraint(_ statusBarOrientation: UIInterfaceOrientation) {
  353. let portrait = statusBarOrientation == .portrait
  354. let landLeft = statusBarOrientation == .landscapeLeft
  355. libraryButtonGravityConstraint = NSLayoutConstraint(
  356. item: libraryButton,
  357. attribute: portrait ? .left : (landLeft ? .top : .bottom),
  358. relatedBy: portrait || landLeft ? .greaterThanOrEqual : .lessThanOrEqual,
  359. toItem: containerSwapLibraryButton,
  360. attribute: portrait ? .centerX : .centerY,
  361. multiplier: 1.0,
  362. constant: (portrait || landLeft ? 4.0 : -4.0) * DeviceConfig.SCREEN_MULTIPLIER)
  363. view.addConstraint(libraryButtonGravityConstraint!)
  364. }
  365. /**
  366. * If the device orientation is portrait, pin the top of
  367. * FlashButton to the top side of superview.
  368. * Else if, pin the FlashButton bottom side on the top side
  369. * of SwapButton.
  370. */
  371. func configFlashEdgeButtonConstraint(_ statusBarOrientation: UIInterfaceOrientation) {
  372. view.autoRemoveConstraint(flashButtonEdgeConstraint)
  373. let constraintPortrait = statusBarOrientation == .portrait || statusBarOrientation == .landscapeLeft
  374. let attribute : NSLayoutConstraint.Attribute = constraintPortrait ? .top : .left
  375. flashButtonEdgeConstraint = NSLayoutConstraint(
  376. item: flashButton,
  377. attribute: attribute,
  378. relatedBy: .equal,
  379. toItem: view,
  380. attribute: attribute,
  381. multiplier: 1.0,
  382. constant: constraintPortrait ? 16 : 16)
  383. view.addConstraint(flashButtonEdgeConstraint!)
  384. }
  385. /**
  386. * If the device orientation is portrait, pin the
  387. right side of FlashButton to the right side of
  388. * superview.
  389. * Else if, centerX the FlashButton on the CenterX
  390. * of CameraButton.
  391. */
  392. func configFlashGravityButtonConstraint(_ statusBarOrientation: UIInterfaceOrientation) {
  393. view.autoRemoveConstraint(flashButtonGravityConstraint)
  394. let constraintPortrait = statusBarOrientation == .portrait || statusBarOrientation == .landscapeLeft
  395. let attribute : NSLayoutConstraint.Attribute = constraintPortrait ? .right : .top
  396. flashButtonGravityConstraint = NSLayoutConstraint(
  397. item: flashButton,
  398. attribute: attribute,
  399. relatedBy: .equal,
  400. toItem: view,
  401. attribute: attribute,
  402. multiplier: 1.0,
  403. constant: constraintPortrait ? -16 : 16)
  404. view.addConstraint(flashButtonGravityConstraint!)
  405. }
  406. /**
  407. * Used to create a perfect was: square now: rectangle with aspect ratio for CameraOverlay.
  408. * This method will determinate the size of CameraOverlay,
  409. * if portrait, it will use the width of superview to
  410. * determinate the height of the view. Else if landscape,
  411. * it uses the height of the superview to create the width
  412. * of the CameraOverlay.
  413. */
  414. func configCameraOverlayWidthConstraint(_ portrait: Bool) {
  415. view.autoRemoveConstraint(cameraOverlayWidthConstraint)
  416. cameraOverlayWidthConstraint = NSLayoutConstraint(
  417. item: cameraOverlay,
  418. attribute: portrait ? .height : .width,
  419. relatedBy: .equal,
  420. toItem: cameraOverlay,
  421. attribute: portrait ? .width : .height,
  422. multiplier: portrait ? croppingParameters.aspectRatioHeightToWidth : 1.0/croppingParameters.aspectRatioHeightToWidth,
  423. constant: 0)
  424. view.addConstraint(cameraOverlayWidthConstraint!)
  425. }
  426. /**
  427. * This method will center the relative position of
  428. * CameraOverlay, based on the biggest size of the
  429. * superview.
  430. */
  431. func configCameraOverlayCenterConstraint(_ portrait: Bool) {
  432. view.autoRemoveConstraint(cameraOverlayCenterConstraint)
  433. let attribute : NSLayoutConstraint.Attribute = portrait ? .centerY : .centerX
  434. cameraOverlayCenterConstraint = NSLayoutConstraint(
  435. item: cameraOverlay,
  436. attribute: attribute,
  437. relatedBy: .equal,
  438. toItem: view,
  439. attribute: attribute,
  440. multiplier: 1.0,
  441. constant: 0)
  442. view.addConstraint(cameraOverlayCenterConstraint!)
  443. }
  444. /**
  445. * Remove the CameraOverlay constraints to be updated when
  446. * the device was rotated.
  447. */
  448. func removeCameraOverlayEdgesConstraints() {
  449. view.autoRemoveConstraint(cameraOverlayEdgeOneConstraint)
  450. view.autoRemoveConstraint(cameraOverlayEdgeTwoConstraint)
  451. }
  452. /**
  453. * It needs to get a determined smallest size of the screen
  454. to create the smallest size to be used on CameraOverlay.
  455. It uses the orientation of the screen to determinate where
  456. the view will be pinned.
  457. */
  458. func configCameraOverlayEdgeOneContraint(_ portrait: Bool, padding: CGFloat) {
  459. let attribute : NSLayoutConstraint.Attribute = portrait ? .left : .bottom
  460. cameraOverlayEdgeOneConstraint = NSLayoutConstraint(
  461. item: cameraOverlay,
  462. attribute: attribute,
  463. relatedBy: .equal,
  464. toItem: view,
  465. attribute: attribute,
  466. multiplier: 1.0,
  467. constant: padding)
  468. view.addConstraint(cameraOverlayEdgeOneConstraint!)
  469. }
  470. /**
  471. * It needs to get a determined smallest size of the screen
  472. to create the smallest size to be used on CameraOverlay.
  473. It uses the orientation of the screen to determinate where
  474. the view will be pinned.
  475. */
  476. func configCameraOverlayEdgeTwoConstraint(_ portrait: Bool, padding: CGFloat) {
  477. let attributeTwo : NSLayoutConstraint.Attribute = portrait ? .right : .top
  478. cameraOverlayEdgeTwoConstraint = NSLayoutConstraint(
  479. item: cameraOverlay,
  480. attribute: attributeTwo,
  481. relatedBy: .equal,
  482. toItem: view,
  483. attribute: attributeTwo,
  484. multiplier: 1.0,
  485. constant: -padding)
  486. view.addConstraint(cameraOverlayEdgeTwoConstraint!)
  487. }
  488. }