From ad8d7fce0b873e629f0f0e39455036e1d151bdde Mon Sep 17 00:00:00 2001 From: Michael Skiba Date: Wed, 4 Jan 2017 11:50:34 -0500 Subject: [PATCH 1/4] added activity indicator view - added activity indicator - made classes open to allow more customizations - added loading state to the gallery item --- .../RIGAutoCenteringScrollView.swift | 30 +++++------ RIGImageGallery/RIGImageGalleryItem.swift | 2 + .../RIGImageGalleryViewController.swift | 22 +++++--- .../RIGSingleImageViewController.swift | 50 +++++++++++++++---- .../View Controller/ViewController.swift | 14 ++++-- 5 files changed, 83 insertions(+), 35 deletions(-) diff --git a/RIGImageGallery/RIGAutoCenteringScrollView.swift b/RIGImageGallery/RIGAutoCenteringScrollView.swift index d7af949..9f812f5 100644 --- a/RIGImageGallery/RIGAutoCenteringScrollView.swift +++ b/RIGImageGallery/RIGAutoCenteringScrollView.swift @@ -8,17 +8,17 @@ import UIKit -class RIGAutoCenteringScrollView: UIScrollView { +open class RIGAutoCenteringScrollView: UIScrollView { - var allowZoom: Bool = false + internal var allowZoom: Bool = false - var baseInsets: UIEdgeInsets = UIEdgeInsets() { + internal var baseInsets: UIEdgeInsets = UIEdgeInsets() { didSet { updateZoomScale(preserveScale: true) } } - var zoomImage: UIImage? { + open var zoomImage: UIImage? { didSet { if oldValue === zoomImage { return @@ -46,18 +46,18 @@ class RIGAutoCenteringScrollView: UIScrollView { fileprivate var contentView: UIImageView? - override init(frame: CGRect) { + public override init(frame: CGRect) { super.init(frame: frame) showsVerticalScrollIndicator = false showsHorizontalScrollIndicator = false delegate = self } - required init?(coder aDecoder: NSCoder) { + public required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - override var frame: CGRect { + open override var frame: CGRect { didSet { updateZoomScale(preserveScale: true) } @@ -68,11 +68,13 @@ class RIGAutoCenteringScrollView: UIScrollView { extension RIGAutoCenteringScrollView { func toggleZoom(animated: Bool = true) { - if zoomScale != minimumZoomScale { - setZoomScale(minimumZoomScale, animated: animated) - } - else { - setZoomScale(maximumZoomScale, animated: animated) + if self.isUserInteractionEnabled { + if zoomScale != minimumZoomScale { + setZoomScale(minimumZoomScale, animated: animated) + } + else { + setZoomScale(maximumZoomScale, animated: animated) + } } } @@ -143,11 +145,11 @@ private extension RIGAutoCenteringScrollView { extension RIGAutoCenteringScrollView: UIScrollViewDelegate { - func viewForZooming(in scrollView: UIScrollView) -> UIView? { + open func viewForZooming(in scrollView: UIScrollView) -> UIView? { return allowZoom ? contentView : nil } - func scrollViewDidZoom(_ scrollView: UIScrollView) { + open func scrollViewDidZoom(_ scrollView: UIScrollView) { centerContent() } diff --git a/RIGImageGallery/RIGImageGalleryItem.swift b/RIGImageGallery/RIGImageGalleryItem.swift index 0967f66..7337bdc 100644 --- a/RIGImageGallery/RIGImageGalleryItem.swift +++ b/RIGImageGallery/RIGImageGalleryItem.swift @@ -19,6 +19,8 @@ public struct RIGImageGalleryItem: Equatable { public var placeholderImage: UIImage? /// The title of the image public var title: String? + // The loading state + public var isLoading: Bool = false public init(image: UIImage? = nil, placeholderImage: UIImage? = nil, title: String? = nil) { self.image = image diff --git a/RIGImageGallery/RIGImageGalleryViewController.swift b/RIGImageGallery/RIGImageGalleryViewController.swift index 0a0d13a..5a34cb8 100644 --- a/RIGImageGallery/RIGImageGalleryViewController.swift +++ b/RIGImageGallery/RIGImageGalleryViewController.swift @@ -10,10 +10,10 @@ import UIKit open class RIGImageGalleryViewController: UIPageViewController { - public typealias GalleryPositionUpdateHandler = (_ gallery: RIGImageGalleryViewController, _ position: Int, _ total: Int) -> () - public typealias ActionButtonPressedHandler = (_ gallery: RIGImageGalleryViewController, _ item: RIGImageGalleryItem) -> () - public typealias GalleryEventHandler = (RIGImageGalleryViewController) -> () - public typealias IndexUpdateHandler = (Int) -> () + public typealias GalleryPositionUpdateHandler = (_ gallery: RIGImageGalleryViewController, _ position: Int, _ total: Int) -> Void + public typealias ActionButtonPressedHandler = (_ gallery: RIGImageGalleryViewController, _ item: RIGImageGalleryItem) -> Void + public typealias GalleryEventHandler = (RIGImageGalleryViewController) -> Void + public typealias IndexUpdateHandler = (Int) -> Void /// An optional closure to execute if the action button is tapped open var actionButtonHandler: ActionButtonPressedHandler? @@ -181,6 +181,14 @@ open class RIGImageGalleryViewController: UIPageViewController { traitCollectionChangeHandler?(self) } + /// Allows subclasses of RIGImageGallery to customize the gallery page + /// + /// - Parameter viewerItem: The item to be displayed + /// - Returns: The view controller that will display the item + open func createNewPage(for viewerItem: RIGImageGalleryItem) -> UIViewController { + return RIGSingleImageViewController(viewerItem: viewerItem) + } + } extension RIGImageGalleryViewController: UIGestureRecognizerDelegate { @@ -237,16 +245,14 @@ extension RIGImageGalleryViewController: UIPageViewControllerDataSource { guard let index = indexOf(viewController: viewController), index < images.count - 1 else { return nil } - let zoomView = RIGSingleImageViewController(viewerItem: images[index + 1]) - return zoomView + return createNewPage(for: images[index + 1]) } public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { guard let index = indexOf(viewController: viewController), index > 0 else { return nil } - let zoomView = RIGSingleImageViewController(viewerItem: images[index - 1]) - return zoomView + return createNewPage(for: images[index + -1]) } } diff --git a/RIGImageGallery/RIGSingleImageViewController.swift b/RIGImageGallery/RIGSingleImageViewController.swift index 3d7cc8c..a2039b5 100644 --- a/RIGImageGallery/RIGSingleImageViewController.swift +++ b/RIGImageGallery/RIGSingleImageViewController.swift @@ -8,34 +8,44 @@ import UIKit -class RIGSingleImageViewController: UIViewController { +open class RIGSingleImageViewController: UIViewController { - var viewerItem: RIGImageGalleryItem? { + open var viewerItem: RIGImageGalleryItem? { didSet { viewerItemUpdated() } } - let scrollView = RIGAutoCenteringScrollView() + open let scrollView = RIGAutoCenteringScrollView() + open let activityIndicator: UIActivityIndicatorView = { + let activityIndicator = UIActivityIndicatorView() + activityIndicator.activityIndicatorViewStyle = .gray + activityIndicator.hidesWhenStopped = true + return activityIndicator + }() - convenience init(viewerItem: RIGImageGalleryItem) { + public convenience init(viewerItem: RIGImageGalleryItem) { self.init() self.viewerItem = viewerItem - viewerItemUpdated() } - override func loadView() { + open override func loadView() { automaticallyAdjustsScrollViewInsets = false - view = scrollView + view = UIView() + view.addSubview(scrollView) + view.addSubview(activityIndicator) view.backgroundColor = .black view.clipsToBounds = true + configureConstraints() + view.setNeedsLayout() } - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) + open override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + viewerItemUpdated() } - override func viewDidDisappear(_ animated: Bool) { + open override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true) } @@ -45,8 +55,28 @@ class RIGSingleImageViewController: UIViewController { private extension RIGSingleImageViewController { func viewerItemUpdated() { + if viewerItem?.isLoading == true && !activityIndicator.isAnimating { + activityIndicator.startAnimating() + } + else if viewerItem?.isLoading == false && activityIndicator.isAnimating { + activityIndicator.stopAnimating() + } scrollView.allowZoom = viewerItem?.image != nil + scrollView.isUserInteractionEnabled = viewerItem?.isLoading == false + scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true) scrollView.zoomImage = viewerItem?.image ?? viewerItem?.placeholderImage } + func configureConstraints() { + scrollView.translatesAutoresizingMaskIntoConstraints = false + activityIndicator.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + scrollView.topAnchor.constraint(equalTo: view.topAnchor), + scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + activityIndicator.centerXAnchor.constraint(equalTo: view.layoutMarginsGuide.centerXAnchor), + activityIndicator.centerYAnchor.constraint(equalTo: view.layoutMarginsGuide.centerYAnchor), + ]) + } } diff --git a/RIGImageGalleryDemo/View Controller/ViewController.swift b/RIGImageGalleryDemo/View Controller/ViewController.swift index f73b31e..a5e0c39 100644 --- a/RIGImageGalleryDemo/View Controller/ViewController.swift +++ b/RIGImageGalleryDemo/View Controller/ViewController.swift @@ -127,12 +127,20 @@ private extension ViewController { let items: [UIImage] = ["1", "2", "3", "4", "5", "6"].flatMap(UIImage.init(named:)) - let rigItems = items.map { item in - RIGImageGalleryItem(image: item) + let rigItems: [RIGImageGalleryItem] = items.map { item in + var item = RIGImageGalleryItem(image: item) + item.isLoading = true + return item } let rigController = RIGImageGalleryViewController(images: rigItems) + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.seconds(2)) { + for index in 0.. ((Data?, URLResponse?, Error?) -> ()) { + func handleImageLoadAtIndex(_ index: Int) -> ((Data?, URLResponse?, Error?) -> Void) { return { [weak self] (data: Data?, response: URLResponse?, error: Error?) in guard let image = data.flatMap(UIImage.init), error == nil else { if let error = error { From 337096766fc7e0cdee817d4fc8202d0bf7fb2ace Mon Sep 17 00:00:00 2001 From: Michael Skiba Date: Wed, 4 Jan 2017 12:52:58 -0500 Subject: [PATCH 2/4] fixes broken test --- RIGImageGallery/RIGSingleImageViewController.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/RIGImageGallery/RIGSingleImageViewController.swift b/RIGImageGallery/RIGSingleImageViewController.swift index a2039b5..40b902c 100644 --- a/RIGImageGallery/RIGSingleImageViewController.swift +++ b/RIGImageGallery/RIGSingleImageViewController.swift @@ -27,6 +27,7 @@ open class RIGSingleImageViewController: UIViewController { public convenience init(viewerItem: RIGImageGalleryItem) { self.init() self.viewerItem = viewerItem + viewerItemUpdated() } open override func loadView() { @@ -64,7 +65,9 @@ private extension RIGSingleImageViewController { scrollView.allowZoom = viewerItem?.image != nil scrollView.isUserInteractionEnabled = viewerItem?.isLoading == false scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true) - scrollView.zoomImage = viewerItem?.image ?? viewerItem?.placeholderImage + if !view.frame.isEmpty { + scrollView.zoomImage = viewerItem?.image ?? viewerItem?.placeholderImage + } } func configureConstraints() { From eba8fda6c1b9044a12a4c24d72fb0dacd8591643 Mon Sep 17 00:00:00 2001 From: Michael Skiba Date: Thu, 5 Jan 2017 15:52:55 -0500 Subject: [PATCH 3/4] fixed visual glitch fixed image not re-scaling properly when loading updated readme cleaned up example --- README.md | 6 ++-- .../RIGAutoCenteringScrollView.swift | 35 +++++++++++-------- RIGImageGallery/RIGImageGalleryItem.swift | 5 +-- .../RIGImageGalleryViewController.swift | 1 + .../RIGSingleImageViewController.swift | 3 +- .../View Controller/ViewController.swift | 27 ++++++-------- 6 files changed, 41 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 69870f2..b192b6c 100644 --- a/README.md +++ b/README.md @@ -61,8 +61,9 @@ func createPhotoGallery() -> RIGImageGalleryViewController { "https://placehold.it/150x350", ].flatMap(URL.init(string:)) - let rigItems = urls.map { _ in - RIGImageGalleryItem(placeholderImage: UIImage(named: "placeholder")) + let rigItems: [RIGImageGalleryItem] = urls.map { _ in + RIGImageGalleryItem(placeholderImage: UIImage(named: "placeholder") ?? UIImage(), + isLoading: true) } let rigController = RIGImageGalleryViewController(images: rigItems) @@ -71,6 +72,7 @@ func createPhotoGallery() -> RIGImageGalleryViewController { let request = imageSession.dataTask(with: URLRequest(url: URL)) { [weak rigController] data, _, error in if let image = data.flatMap(UIImage.init), error == nil { rigController?.images[index].image = image + rigController?.images[index].isLoading = false } } request.resume() diff --git a/RIGImageGallery/RIGAutoCenteringScrollView.swift b/RIGImageGallery/RIGAutoCenteringScrollView.swift index 9f812f5..9810e91 100644 --- a/RIGImageGallery/RIGAutoCenteringScrollView.swift +++ b/RIGImageGallery/RIGAutoCenteringScrollView.swift @@ -24,32 +24,24 @@ open class RIGAutoCenteringScrollView: UIScrollView { return } if let img = zoomImage { - let imageView: UIImageView - if let img = contentView { - imageView = img - } - else { - imageView = UIImageView() - contentView = imageView - addSubview(imageView) - } - imageView.frame = CGRect(origin: CGPoint(), size: img.size) - imageView.image = img + contentView.isHidden = false + contentView.image = img } else { - contentView?.removeFromSuperview() - contentView = nil + contentView.isHidden = true } updateZoomScale(preserveScale: false) } } - fileprivate var contentView: UIImageView? + fileprivate var contentView = UIImageView() public override init(frame: CGRect) { super.init(frame: frame) showsVerticalScrollIndicator = false showsHorizontalScrollIndicator = false + addSubview(contentView) + configureConstraints() delegate = self } @@ -82,6 +74,16 @@ extension RIGAutoCenteringScrollView { private extension RIGAutoCenteringScrollView { + func configureConstraints() { + contentView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + contentView.leadingAnchor.constraint(equalTo: leadingAnchor), + contentView.trailingAnchor.constraint(equalTo: trailingAnchor), + contentView.topAnchor.constraint(equalTo: topAnchor), + contentView.bottomAnchor.constraint(equalTo: bottomAnchor), + ]) + } + func updateZoomScale(preserveScale: Bool) { guard let image = zoomImage else { contentSize = frame.size @@ -90,6 +92,8 @@ private extension RIGAutoCenteringScrollView { setZoomScale(1, animated: false) return } + updateConstraintsIfNeeded() + layoutIfNeeded() let adjustedFrame = UIEdgeInsetsInsetRect(frame, baseInsets) @@ -139,6 +143,9 @@ private extension RIGAutoCenteringScrollView { } contentInset = UIEdgeInsets(top: vertical + baseInsets.top, left: horizontal + baseInsets.left, bottom: vertical + baseInsets.bottom, right: horizontal + baseInsets.right) + + updateConstraintsIfNeeded() + layoutIfNeeded() } } diff --git a/RIGImageGallery/RIGImageGalleryItem.swift b/RIGImageGallery/RIGImageGalleryItem.swift index 7337bdc..de49b5a 100644 --- a/RIGImageGallery/RIGImageGalleryItem.swift +++ b/RIGImageGallery/RIGImageGalleryItem.swift @@ -20,12 +20,13 @@ public struct RIGImageGalleryItem: Equatable { /// The title of the image public var title: String? // The loading state - public var isLoading: Bool = false + public var isLoading: Bool - public init(image: UIImage? = nil, placeholderImage: UIImage? = nil, title: String? = nil) { + public init(image: UIImage? = nil, placeholderImage: UIImage? = nil, title: String? = nil, isLoading: Bool = false) { self.image = image self.placeholderImage = placeholderImage self.title = title + self.isLoading = isLoading } } diff --git a/RIGImageGallery/RIGImageGalleryViewController.swift b/RIGImageGallery/RIGImageGalleryViewController.swift index 5a34cb8..24cc04f 100644 --- a/RIGImageGallery/RIGImageGalleryViewController.swift +++ b/RIGImageGallery/RIGImageGalleryViewController.swift @@ -129,6 +129,7 @@ open class RIGImageGalleryViewController: UIPageViewController { dataSource = self delegate = self automaticallyAdjustsScrollViewInsets = false + view.backgroundColor = .black handleImagesUpdate(oldValue: []) configureDoneButton() configureActionButton() diff --git a/RIGImageGallery/RIGSingleImageViewController.swift b/RIGImageGallery/RIGSingleImageViewController.swift index 40b902c..20c407e 100644 --- a/RIGImageGallery/RIGSingleImageViewController.swift +++ b/RIGImageGallery/RIGSingleImageViewController.swift @@ -35,7 +35,6 @@ open class RIGSingleImageViewController: UIViewController { view = UIView() view.addSubview(scrollView) view.addSubview(activityIndicator) - view.backgroundColor = .black view.clipsToBounds = true configureConstraints() view.setNeedsLayout() @@ -64,10 +63,10 @@ private extension RIGSingleImageViewController { } scrollView.allowZoom = viewerItem?.image != nil scrollView.isUserInteractionEnabled = viewerItem?.isLoading == false - scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true) if !view.frame.isEmpty { scrollView.zoomImage = viewerItem?.image ?? viewerItem?.placeholderImage } + scrollView.setZoomScale(scrollView.minimumZoomScale, animated: false) } func configureConstraints() { diff --git a/RIGImageGalleryDemo/View Controller/ViewController.swift b/RIGImageGalleryDemo/View Controller/ViewController.swift index a5e0c39..fa36717 100644 --- a/RIGImageGalleryDemo/View Controller/ViewController.swift +++ b/RIGImageGalleryDemo/View Controller/ViewController.swift @@ -107,19 +107,23 @@ private extension ViewController { let urls = type(of: self).urls - let rigItems = urls.map { _ in - RIGImageGalleryItem(placeholderImage: UIImage(named: "placeholder") ?? UIImage()) + let rigItems: [RIGImageGalleryItem] = urls.map { url in + RIGImageGalleryItem(placeholderImage: #imageLiteral(resourceName: "placeholder"), + title: url.pathComponents.last ?? "", + isLoading: true) } let rigController = RIGImageGalleryViewController(images: rigItems) - for (index, URL) in urls.enumerated() { + for (index, URL) in urls.enumerated() { let completion = rigController.handleImageLoadAtIndex(index) - let request = imageSession.dataTask(with: URLRequest(url: URL), completionHandler: completion) + let request = imageSession.dataTask(with: URLRequest(url: URL), + completionHandler: completion) request.resume() } rigController.setCurrentImage(2, animated: false) + return rigController } @@ -128,19 +132,11 @@ private extension ViewController { let items: [UIImage] = ["1", "2", "3", "4", "5", "6"].flatMap(UIImage.init(named:)) let rigItems: [RIGImageGalleryItem] = items.map { item in - var item = RIGImageGalleryItem(image: item) - item.isLoading = true - return item + RIGImageGalleryItem(image: item) } let rigController = RIGImageGalleryViewController(images: rigItems) - DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.seconds(2)) { - for index in 0.. Date: Fri, 6 Jan 2017 10:06:28 -0500 Subject: [PATCH 4/4] Code review updates --- .../RIGImageGalleryViewController.swift | 1 - .../RIGSingleImageViewController.swift | 42 +++++++++++-------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/RIGImageGallery/RIGImageGalleryViewController.swift b/RIGImageGallery/RIGImageGalleryViewController.swift index 24cc04f..5a34cb8 100644 --- a/RIGImageGallery/RIGImageGalleryViewController.swift +++ b/RIGImageGallery/RIGImageGalleryViewController.swift @@ -129,7 +129,6 @@ open class RIGImageGalleryViewController: UIPageViewController { dataSource = self delegate = self automaticallyAdjustsScrollViewInsets = false - view.backgroundColor = .black handleImagesUpdate(oldValue: []) configureDoneButton() configureActionButton() diff --git a/RIGImageGallery/RIGSingleImageViewController.swift b/RIGImageGallery/RIGSingleImageViewController.swift index 20c407e..0412ee4 100644 --- a/RIGImageGallery/RIGSingleImageViewController.swift +++ b/RIGImageGallery/RIGSingleImageViewController.swift @@ -17,12 +17,18 @@ open class RIGSingleImageViewController: UIViewController { } open let scrollView = RIGAutoCenteringScrollView() - open let activityIndicator: UIActivityIndicatorView = { - let activityIndicator = UIActivityIndicatorView() - activityIndicator.activityIndicatorViewStyle = .gray - activityIndicator.hidesWhenStopped = true - return activityIndicator - }() + open var activityIndicator: UIActivityIndicatorView? { + didSet { + oldValue?.removeFromSuperview() + if let newValue = activityIndicator { + view.addSubview(newValue) + NSLayoutConstraint.activate([ + newValue.centerXAnchor.constraint(equalTo: view.layoutMarginsGuide.centerXAnchor), + newValue.centerYAnchor.constraint(equalTo: view.layoutMarginsGuide.centerYAnchor), + ]) + } + } + } public convenience init(viewerItem: RIGImageGalleryItem) { self.init() @@ -30,14 +36,16 @@ open class RIGSingleImageViewController: UIViewController { viewerItemUpdated() } - open override func loadView() { + open override func viewDidLoad() { + super.viewDidLoad() automaticallyAdjustsScrollViewInsets = false - view = UIView() - view.addSubview(scrollView) - view.addSubview(activityIndicator) view.clipsToBounds = true + view.addSubview(scrollView) + let indicatorView = UIActivityIndicatorView() + indicatorView.activityIndicatorViewStyle = .gray + indicatorView.hidesWhenStopped = true + self.activityIndicator = indicatorView configureConstraints() - view.setNeedsLayout() } open override func viewDidLayoutSubviews() { @@ -55,11 +63,11 @@ open class RIGSingleImageViewController: UIViewController { private extension RIGSingleImageViewController { func viewerItemUpdated() { - if viewerItem?.isLoading == true && !activityIndicator.isAnimating { - activityIndicator.startAnimating() + if viewerItem?.isLoading == true && activityIndicator?.isAnimating == false { + activityIndicator?.startAnimating() } - else if viewerItem?.isLoading == false && activityIndicator.isAnimating { - activityIndicator.stopAnimating() + else if viewerItem?.isLoading == false && activityIndicator?.isAnimating == true { + activityIndicator?.stopAnimating() } scrollView.allowZoom = viewerItem?.image != nil scrollView.isUserInteractionEnabled = viewerItem?.isLoading == false @@ -71,14 +79,12 @@ private extension RIGSingleImageViewController { func configureConstraints() { scrollView.translatesAutoresizingMaskIntoConstraints = false - activityIndicator.translatesAutoresizingMaskIntoConstraints = false + activityIndicator?.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor), scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor), scrollView.topAnchor.constraint(equalTo: view.topAnchor), scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor), - activityIndicator.centerXAnchor.constraint(equalTo: view.layoutMarginsGuide.centerXAnchor), - activityIndicator.centerYAnchor.constraint(equalTo: view.layoutMarginsGuide.centerYAnchor), ]) } }