From 93bab86e0913805337ab3ed4770bc1b05f98473f Mon Sep 17 00:00:00 2001 From: Rodrigo Leite Date: Mon, 28 Nov 2016 11:36:35 -0200 Subject: [PATCH 1/2] add page control to the component --- Example/Optik/ViewController.swift | 4 +- Optik/Classes/AlbumViewController.swift | 94 +++++++++++++++++++++++-- Optik/Classes/Optik.swift | 18 +++-- 3 files changed, 104 insertions(+), 12 deletions(-) diff --git a/Example/Optik/ViewController.swift b/Example/Optik/ViewController.swift index db83faf..64399d5 100644 --- a/Example/Optik/ViewController.swift +++ b/Example/Optik/ViewController.swift @@ -45,7 +45,7 @@ internal final class ViewController: UIViewController { @IBAction private func presentLocalImageViewer(_ sender: UIButton) { let viewController = Optik.imageViewer(withImages: localImages, initialImageDisplayIndex: currentLocalImageIndex, - delegate: self) + delegate: self, enablePageControl: true) present(viewController, animated: true, completion: nil) } @@ -62,7 +62,7 @@ internal final class ViewController: UIViewController { let urls = [url1, url2, url3, url4] let imageDownloader = AlamofireImageDownloader() - let viewController = Optik.imageViewer(withURLs: urls, imageDownloader: imageDownloader) + let viewController = Optik.imageViewer(withURLs: urls, imageDownloader: imageDownloader, enablePageControl: true) present(viewController, animated: true, completion: nil) } diff --git a/Optik/Classes/AlbumViewController.swift b/Optik/Classes/AlbumViewController.swift index 9d00651..89cde88 100644 --- a/Optik/Classes/AlbumViewController.swift +++ b/Optik/Classes/AlbumViewController.swift @@ -58,7 +58,7 @@ internal final class AlbumViewController: UIViewController { return viewControllers[0] as? ImageViewController } - private var imageData: ImageData + fileprivate var imageData: ImageData private var initialImageDisplayIndex: Int private var activityIndicatorColor: UIColor? private var dismissButtonImage: UIImage? @@ -69,13 +69,16 @@ internal final class AlbumViewController: UIViewController { private var transitionController: TransitionController = TransitionController() + fileprivate var pageControl: UIPageControl? + // MARK: - Init/Deinit init(imageData: ImageData, initialImageDisplayIndex: Int, activityIndicatorColor: UIColor?, dismissButtonImage: UIImage?, - dismissButtonPosition: DismissButtonPosition) { + dismissButtonPosition: DismissButtonPosition, + enablePageControl: Bool) { self.imageData = imageData self.initialImageDisplayIndex = initialImageDisplayIndex @@ -83,9 +86,16 @@ internal final class AlbumViewController: UIViewController { self.dismissButtonImage = dismissButtonImage self.dismissButtonPosition = dismissButtonPosition - pageViewController = UIPageViewController(transitionStyle: .scroll, - navigationOrientation: .horizontal, + if enablePageControl { + pageViewController = UIPageViewController(transitionStyle: .scroll, + navigationOrientation: .vertical, options: [UIPageViewControllerOptionInterPageSpacingKey : Constants.SpacingBetweenImages]) + pageControl = UIPageControl() + } else { + pageViewController = UIPageViewController(transitionStyle: .scroll, + navigationOrientation: .horizontal, + options: [UIPageViewControllerOptionInterPageSpacingKey : Constants.SpacingBetweenImages]) + } super.init(nibName: nil, bundle: nil) @@ -147,6 +157,7 @@ internal final class AlbumViewController: UIViewController { didMove(toParentViewController: pageViewController) setupDismissButton() + setupPageControl() setupPanGestureRecognizer() } @@ -163,6 +174,56 @@ internal final class AlbumViewController: UIViewController { } } + private func setupPageControl() { + + if let page = pageControl { + page.currentPage = 0 + page.pageIndicatorTintColor = UIColor.red + page.translatesAutoresizingMaskIntoConstraints = false + page.transform = CGAffineTransform(rotationAngle: CGFloat(M_PI/2)) + view.addSubview(page) + + view.addConstraint( + NSLayoutConstraint(item: page, + attribute: .trailing, + relatedBy: .equal, + toItem: view, + attribute: .trailing, + multiplier: 1, + constant: -10) + ) + view.addConstraint( + NSLayoutConstraint(item: page, + attribute: .centerY, + relatedBy: .equal, + toItem: view, + attribute: .centerY, + multiplier: 1, + constant: 0) + ) + view.addConstraint( + NSLayoutConstraint(item: page, + attribute: .width, + relatedBy: .equal, + toItem: nil, + attribute: .notAnAttribute, + multiplier: 1, + constant: 30) + ) + view.addConstraint( + NSLayoutConstraint(item: page, + attribute: .height, + relatedBy: .equal, + toItem: nil, + attribute: .notAnAttribute, + multiplier: 1, + constant: 25) + ) + } + + } + + private func setupDismissButton() { let dismissButton = UIButton(type: .custom) dismissButton.translatesAutoresizingMaskIntoConstraints = false @@ -226,12 +287,14 @@ internal final class AlbumViewController: UIViewController { return nil } + pageControl?.numberOfPages = images.count return ImageViewController(image: images[index], index: index) case .remote(let urls, let imageDownloader): guard index >= 0 && index < urls.count else { return nil } + pageControl?.numberOfPages = urls.count let imageViewController = ImageViewController(activityIndicatorColor: activityIndicatorColor, index: index) let url = urls[index] @@ -245,6 +308,7 @@ internal final class AlbumViewController: UIViewController { imageViewController.image = image }) } + return imageViewController } @@ -306,6 +370,28 @@ extension AlbumViewController: UIPageViewControllerDelegate { if let currentImageIndex = currentImageViewController?.index { imageViewerDelegate?.imageViewerDidDisplayImage(at: currentImageIndex) + pageControl?.currentPage = currentImageIndex + } + } + + func pageViewController(pageViewController: UIPageViewController, spineLocationForInterfaceOrientation orientation: UIInterfaceOrientation) -> UIPageViewControllerSpineLocation { + return .max + } + + func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int { + return numberOfImages() + } + + func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int { + return currentImageViewController?.index ?? 0 + } + + private func numberOfImages() -> Int { + switch imageData { + case .local(images: let images): + return images.count + case .remote(urls: let urls, imageDownloader: _): + return urls.count } } diff --git a/Optik/Classes/Optik.swift b/Optik/Classes/Optik.swift index fc54d43..f0d8c0b 100644 --- a/Optik/Classes/Optik.swift +++ b/Optik/Classes/Optik.swift @@ -25,11 +25,13 @@ public func imageViewer(withImages images: [UIImage], initialImageDisplayIndex: Int = 0, delegate: ImageViewerDelegate? = nil, dismissButtonImage: UIImage? = nil, - dismissButtonPosition: DismissButtonPosition = .topLeading) -> UIViewController { + dismissButtonPosition: DismissButtonPosition = .topLeading, + enablePageControl: Bool) -> UIViewController { let albumViewController = imageViewer(withData: .local(images: images), initialImageDisplayIndex: initialImageDisplayIndex, dismissButtonImage: dismissButtonImage, - dismissButtonPosition: dismissButtonPosition) + dismissButtonPosition: dismissButtonPosition, + enablePageControl: enablePageControl) albumViewController.modalPresentationStyle = .custom albumViewController.imageViewerDelegate = delegate @@ -53,12 +55,14 @@ public func imageViewer(withURLs urls: [URL], imageDownloader: ImageDownloader, activityIndicatorColor: UIColor = .white, dismissButtonImage: UIImage? = nil, - dismissButtonPosition: DismissButtonPosition = .topLeading) -> UIViewController { + dismissButtonPosition: DismissButtonPosition = .topLeading, + enablePageControl: Bool) -> UIViewController { return imageViewer(withData: .remote(urls: urls, imageDownloader: imageDownloader), initialImageDisplayIndex: initialImageDisplayIndex, activityIndicatorColor: activityIndicatorColor, dismissButtonImage: dismissButtonImage, - dismissButtonPosition: dismissButtonPosition) + dismissButtonPosition: dismissButtonPosition, + enablePageControl: enablePageControl) } // MARK: - Private functions @@ -67,7 +71,8 @@ private func imageViewer(withData imageData: ImageData, initialImageDisplayIndex: Int, activityIndicatorColor: UIColor? = nil, dismissButtonImage: UIImage?, - dismissButtonPosition: DismissButtonPosition) -> AlbumViewController { + dismissButtonPosition: DismissButtonPosition, + enablePageControl: Bool) -> AlbumViewController { let bundle = Bundle(for: AlbumViewController.self) let defaultDismissButtonImage = UIImage(named: "DismissIcon", in: bundle, compatibleWith: nil) @@ -75,5 +80,6 @@ private func imageViewer(withData imageData: ImageData, initialImageDisplayIndex: initialImageDisplayIndex, activityIndicatorColor: activityIndicatorColor, dismissButtonImage: dismissButtonImage ?? defaultDismissButtonImage, - dismissButtonPosition: dismissButtonPosition) + dismissButtonPosition: dismissButtonPosition, + enablePageControl: enablePageControl) } From ced8a392096dac0fb335ac7400b900dfd60afb92 Mon Sep 17 00:00:00 2001 From: Rodrigo Leite Date: Mon, 28 Nov 2016 12:08:24 -0200 Subject: [PATCH 2/2] add actionButton --- .../AppIcon.appiconset/Contents.json | 12 +++- Example/Optik/Images.xcassets/Contents.json | 6 ++ .../heart-empty.imageset/Contents.json | 12 ++++ .../heart-empty.imageset/heart.pdf | Bin 0 -> 38700 bytes .../heart-full.imageset/Contents.json | 12 ++++ .../heart-full.imageset/heart-fill.pdf | Bin 0 -> 44772 bytes Example/Optik/ViewController.swift | 16 +++++ Optik/Classes/ActionButtonPosition.swift | 34 +++++++++ Optik/Classes/AlbumViewController.swift | 32 +++++++-- Optik/Classes/ImageViewController.swift | 65 ++++++++++++++++-- Optik/Classes/ImageViewerDelegate.swift | 16 +++++ Optik/Classes/Optik.swift | 18 +++-- 12 files changed, 207 insertions(+), 16 deletions(-) create mode 100644 Example/Optik/Images.xcassets/Contents.json create mode 100644 Example/Optik/Images.xcassets/heart-empty.imageset/Contents.json create mode 100644 Example/Optik/Images.xcassets/heart-empty.imageset/heart.pdf create mode 100644 Example/Optik/Images.xcassets/heart-full.imageset/Contents.json create mode 100644 Example/Optik/Images.xcassets/heart-full.imageset/heart-fill.pdf create mode 100644 Optik/Classes/ActionButtonPosition.swift diff --git a/Example/Optik/Images.xcassets/AppIcon.appiconset/Contents.json b/Example/Optik/Images.xcassets/AppIcon.appiconset/Contents.json index d3942e9..b8236c6 100644 --- a/Example/Optik/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/Example/Optik/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,5 +1,15 @@ { "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, { "idiom" : "iphone", "size" : "29x29", @@ -35,4 +45,4 @@ "version" : 1, "author" : "xcode" } -} +} \ No newline at end of file diff --git a/Example/Optik/Images.xcassets/Contents.json b/Example/Optik/Images.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Example/Optik/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/Optik/Images.xcassets/heart-empty.imageset/Contents.json b/Example/Optik/Images.xcassets/heart-empty.imageset/Contents.json new file mode 100644 index 0000000..16bd039 --- /dev/null +++ b/Example/Optik/Images.xcassets/heart-empty.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "heart.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/Optik/Images.xcassets/heart-empty.imageset/heart.pdf b/Example/Optik/Images.xcassets/heart-empty.imageset/heart.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0b103aa8781fbe3e57381351633ed09d948f0f27 GIT binary patch literal 38700 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 Mu_vD_G>VV?Z>ao9VE_OC literal 0 HcmV?d00001 diff --git a/Example/Optik/ViewController.swift b/Example/Optik/ViewController.swift index db83faf..69e0d7d 100644 --- a/Example/Optik/ViewController.swift +++ b/Example/Optik/ViewController.swift @@ -82,4 +82,20 @@ extension ViewController: ImageViewerDelegate { currentLocalImageIndex = index } + func actionButtonTapped(button: UIButton, at index: Int) { + if button.currentImage == UIImage(named: "heart-full")! { + button.setImage(UIImage(named: "heart-empty"), for: .normal) + } else { + button.setImage(UIImage(named: "heart-full"), for: .normal) + } + } + + func imageForActionButton (at index: Int) -> UIImage { + if index % 2 == 0 { + return UIImage(named: "heart-full")! + } else { + return UIImage(named: "heart-empty")! + } + } + } diff --git a/Optik/Classes/ActionButtonPosition.swift b/Optik/Classes/ActionButtonPosition.swift new file mode 100644 index 0000000..4f37fb1 --- /dev/null +++ b/Optik/Classes/ActionButtonPosition.swift @@ -0,0 +1,34 @@ +// +// ActionButtonPosition.swift +// Pods +// +// Created by Rodrigo Leite on 28/11/16. +// +// + +import Foundation + +/** + Defines the position of the action button. + + - topLeading: action button is constrained to the top and leading anchors of its superview. + - topTrailing: action button is constrained to the top and trailing anchors of its superview. + */ +public enum ActionButtonPosition { + + case bottomLeading + + func xAnchorAttribute() -> NSLayoutAttribute { + switch self { + case .bottomLeading: + return .leading + } + } + + func yAnchorAttribute() -> NSLayoutAttribute { + switch self { + case .bottomLeading: + return .bottom + } + } +} diff --git a/Optik/Classes/AlbumViewController.swift b/Optik/Classes/AlbumViewController.swift index 9d00651..d5b1911 100644 --- a/Optik/Classes/AlbumViewController.swift +++ b/Optik/Classes/AlbumViewController.swift @@ -63,7 +63,7 @@ internal final class AlbumViewController: UIViewController { private var activityIndicatorColor: UIColor? private var dismissButtonImage: UIImage? private var dismissButtonPosition: DismissButtonPosition - + private var actionButtonPosition: ActionButtonPosition private var cachedRemoteImages: [URL: UIImage] = [:] private var viewDidAppear: Bool = false @@ -75,13 +75,15 @@ internal final class AlbumViewController: UIViewController { initialImageDisplayIndex: Int, activityIndicatorColor: UIColor?, dismissButtonImage: UIImage?, - dismissButtonPosition: DismissButtonPosition) { + dismissButtonPosition: DismissButtonPosition, + actionButtonPosition: ActionButtonPosition) { self.imageData = imageData self.initialImageDisplayIndex = initialImageDisplayIndex self.activityIndicatorColor = activityIndicatorColor self.dismissButtonImage = dismissButtonImage self.dismissButtonPosition = dismissButtonPosition + self.actionButtonPosition = actionButtonPosition pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, @@ -115,6 +117,8 @@ internal final class AlbumViewController: UIViewController { self.setNeedsStatusBarAppearanceUpdate() }) } + updateActionButtonImage(at: initialImageDisplayIndex) + } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { @@ -226,13 +230,16 @@ internal final class AlbumViewController: UIViewController { return nil } - return ImageViewController(image: images[index], index: index) + let imageViewController = ImageViewController(image: images[index], index: index, actionButtonPosition: .bottomLeading) + imageViewController.actionButton?.addTarget(self, action: #selector(AlbumViewController.didSelectImage(_:)), for: .touchUpInside) + return imageViewController + case .remote(let urls, let imageDownloader): guard index >= 0 && index < urls.count else { return nil } - let imageViewController = ImageViewController(activityIndicatorColor: activityIndicatorColor, index: index) + let imageViewController = ImageViewController(activityIndicatorColor: activityIndicatorColor, index: index, actionButtonPosition: actionButtonPosition) let url = urls[index] if let image = cachedRemoteImages[url] { @@ -246,10 +253,20 @@ internal final class AlbumViewController: UIViewController { }) } + imageViewController.actionButton?.addTarget(self, action: #selector(AlbumViewController.didSelectImage(_:)), for: .touchUpInside) return imageViewController } } + fileprivate func updateActionButtonImage(at index: Int) { + if let imageViewController = pageViewController.viewControllers?.first as? ImageViewController{ + let actionButton = imageViewController.actionButton + let image = imageViewerDelegate?.imageForActionButton(at: index) + actionButton?.setImage(image, for: .normal) + } + } + + @objc private func didTapDismissButton(_ sender: UIButton) { dismiss(animated: true, completion: nil) } @@ -258,6 +275,11 @@ internal final class AlbumViewController: UIViewController { transitionController.didPan(withGestureRecognizer: sender, sourceView: view) } + @objc private func didSelectImage(_ sender: UIButton) { + if let currentImageIndex = currentImageViewController?.index { + imageViewerDelegate?.actionButtonTapped(button: sender, at: currentImageIndex) + } + } } // MARK: - Protocol conformance @@ -306,6 +328,8 @@ extension AlbumViewController: UIPageViewControllerDelegate { if let currentImageIndex = currentImageViewController?.index { imageViewerDelegate?.imageViewerDidDisplayImage(at: currentImageIndex) + self.updateActionButtonImage(at: currentImageIndex) + } } diff --git a/Optik/Classes/ImageViewController.swift b/Optik/Classes/ImageViewController.swift index 681a0a4..2189cde 100644 --- a/Optik/Classes/ImageViewController.swift +++ b/Optik/Classes/ImageViewController.swift @@ -30,13 +30,15 @@ internal final class ImageViewController: UIViewController { } } private(set) var imageView: UIImageView? - + var actionButton: UIButton? + let index: Int // MARK: - Private properties private var activityIndicatorColor: UIColor? - + private var actionButtonPosition: ActionButtonPosition? + private var scrollView: UIScrollView? { didSet { guard let scrollView = scrollView else { @@ -58,15 +60,17 @@ internal final class ImageViewController: UIViewController { } private var effectiveImageSize: CGSize? - + // MARK: - Init/Deinit - init(image: UIImage? = nil, activityIndicatorColor: UIColor? = nil, index: Int) { + init(image: UIImage? = nil, activityIndicatorColor: UIColor? = nil, index: Int, actionButtonPosition: ActionButtonPosition) { self.image = image self.activityIndicatorColor = activityIndicatorColor self.index = index - + self.actionButtonPosition = actionButtonPosition super.init(nibName: nil, bundle: nil) + self.actionButton = UIButton() + } required init?(coder aDecoder: NSCoder) { @@ -159,6 +163,57 @@ internal final class ImageViewController: UIViewController { self.activityIndicatorView = activityIndicatorView } + if let button = actionButton { + button.translatesAutoresizingMaskIntoConstraints = false + button.imageView?.contentMode = .scaleAspectFill + + let xAnchorAttribute = actionButtonPosition?.xAnchorAttribute() + let yAnchorAttribute = actionButtonPosition?.yAnchorAttribute() + + view.addSubview(button) + if let xAnchor = xAnchorAttribute, + let yAnchor = yAnchorAttribute { + + view.addConstraint( + NSLayoutConstraint(item: button, + attribute: yAnchor, + relatedBy: .equal, + toItem: view, + attribute: yAnchor, + multiplier: 1, + constant: -20) + ) + view.addConstraint( + NSLayoutConstraint(item: button, + attribute: xAnchor, + relatedBy: .equal, + toItem: view, + attribute: xAnchor, + multiplier: 1, + constant: 20) + ) + view.addConstraint( + NSLayoutConstraint(item: button, + attribute: .width, + relatedBy: .equal, + toItem: nil, + attribute: .width, + multiplier: 1, + constant: 44) + ) + view.addConstraint( + NSLayoutConstraint(item: button, + attribute: .height, + relatedBy: .equal, + toItem: nil, + attribute: .height, + multiplier: 1, + constant: 44) + ) + } + + } + setupTapGestureRecognizer() } diff --git a/Optik/Classes/ImageViewerDelegate.swift b/Optik/Classes/ImageViewerDelegate.swift index 831a532..8e78482 100644 --- a/Optik/Classes/ImageViewerDelegate.swift +++ b/Optik/Classes/ImageViewerDelegate.swift @@ -30,4 +30,20 @@ public protocol ImageViewerDelegate: class { */ func imageViewerDidDisplayImage(at index: Int) + /** + Tells when the user touch in the action button + + - parameter index: Index of the image. + */ + func actionButtonTapped(button: UIButton, at index: Int) + + + /** + Ask delegate for the image that should appear in the button when the image appears. + - parameter index: index of the image + + - return: UIImage + */ + func imageForActionButton (at index: Int) -> UIImage + } diff --git a/Optik/Classes/Optik.swift b/Optik/Classes/Optik.swift index fc54d43..5ef5298 100644 --- a/Optik/Classes/Optik.swift +++ b/Optik/Classes/Optik.swift @@ -25,11 +25,13 @@ public func imageViewer(withImages images: [UIImage], initialImageDisplayIndex: Int = 0, delegate: ImageViewerDelegate? = nil, dismissButtonImage: UIImage? = nil, - dismissButtonPosition: DismissButtonPosition = .topLeading) -> UIViewController { + dismissButtonPosition: DismissButtonPosition = .topLeading, + actionButtonPosition: ActionButtonPosition = .bottomLeading) -> UIViewController { let albumViewController = imageViewer(withData: .local(images: images), initialImageDisplayIndex: initialImageDisplayIndex, dismissButtonImage: dismissButtonImage, - dismissButtonPosition: dismissButtonPosition) + dismissButtonPosition: dismissButtonPosition, + actionButtonPosition: actionButtonPosition) albumViewController.modalPresentationStyle = .custom albumViewController.imageViewerDelegate = delegate @@ -53,12 +55,14 @@ public func imageViewer(withURLs urls: [URL], imageDownloader: ImageDownloader, activityIndicatorColor: UIColor = .white, dismissButtonImage: UIImage? = nil, - dismissButtonPosition: DismissButtonPosition = .topLeading) -> UIViewController { + dismissButtonPosition: DismissButtonPosition = .topLeading, + actionButtonPosition: ActionButtonPosition = .bottomLeading) -> UIViewController { return imageViewer(withData: .remote(urls: urls, imageDownloader: imageDownloader), initialImageDisplayIndex: initialImageDisplayIndex, activityIndicatorColor: activityIndicatorColor, dismissButtonImage: dismissButtonImage, - dismissButtonPosition: dismissButtonPosition) + dismissButtonPosition: dismissButtonPosition, + actionButtonPosition: actionButtonPosition) } // MARK: - Private functions @@ -67,7 +71,8 @@ private func imageViewer(withData imageData: ImageData, initialImageDisplayIndex: Int, activityIndicatorColor: UIColor? = nil, dismissButtonImage: UIImage?, - dismissButtonPosition: DismissButtonPosition) -> AlbumViewController { + dismissButtonPosition: DismissButtonPosition, + actionButtonPosition: ActionButtonPosition = .bottomLeading) -> AlbumViewController { let bundle = Bundle(for: AlbumViewController.self) let defaultDismissButtonImage = UIImage(named: "DismissIcon", in: bundle, compatibleWith: nil) @@ -75,5 +80,6 @@ private func imageViewer(withData imageData: ImageData, initialImageDisplayIndex: initialImageDisplayIndex, activityIndicatorColor: activityIndicatorColor, dismissButtonImage: dismissButtonImage ?? defaultDismissButtonImage, - dismissButtonPosition: dismissButtonPosition) + dismissButtonPosition: dismissButtonPosition, + actionButtonPosition: actionButtonPosition) }