Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed a few issues and implemented features #47

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added Documentation/storyboard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions Example/ChoiceViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// ChoiceViewController.swift
// Example
//
// Created by Sebastian Westemeyer on 13.12.18.
// Copyright © 2018 aFrogleap. All rights reserved.
//

import UIKit
import SimpleImageViewer

class ChoiceViewController: UITableViewController {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "showSingleImage") {
let destination = segue.destination as! ImageViewerController
let configuration = ImageViewerConfiguration { config in
config.image = UIImage(named: "2")
}
configuration.showCloseButton = false
destination.configuration = configuration
}
}
}
44 changes: 44 additions & 0 deletions Example/EmbeddingViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// EmbeddingViewController.swift
// Example
//
// Created by Sebastian Westemeyer on 13.12.18.
// Copyright © 2018 aFrogleap. All rights reserved.
//

import UIKit
import SimpleImageViewer

class EmbeddingViewController: UIViewController {
fileprivate var viewerController: ImageViewerController!
fileprivate var fooImage: UIImage!
fileprivate var barImage: UIImage!

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "embedImage") {
fooImage = UIImage(named: "1")
barImage = UIImage(named: "2")
viewerController = segue.destination as? ImageViewerController
let configuration = ImageViewerConfiguration(configurationClosure: nil)
configuration.showCloseButton = false
configuration.backgroundColor = .purple
configuration.imageBlock = { (completion) in self.updateImage(completion) }
viewerController.configuration = configuration
}
}
}

private extension EmbeddingViewController {
@IBAction func nextButtonPressed(_ sender: UIButton) {
viewerController.reloadImage()
}

func updateImage(_ completion: ImageCompletion) {
// switch images
let image = fooImage
fooImage = barImage
barImage = image
// set image
completion(fooImage)
}
}
21 changes: 21 additions & 0 deletions Example/Resources/Assets.xcassets/skip_next.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "baseline_skip_next_white_18dp.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
197 changes: 190 additions & 7 deletions Example/Resources/Base.lproj/Main.storyboard

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Example/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class ViewController: UICollectionViewController, UICollectionViewDelegateFlowLa
config.imageView = cell.imageView
}

present(ImageViewerController(configuration: configuration), animated: true)
present(ImageViewerController.imageViewerController(configuration: configuration), animated: true)
}

override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
Expand Down
8 changes: 5 additions & 3 deletions ImageViewer/ImageViewerConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ public final class ImageViewerConfiguration {
public var image: UIImage?
public var imageView: UIImageView?
public var imageBlock: ImageBlock?

public var showCloseButton: Bool = true
public var backgroundColor: UIColor?

public typealias ConfigurationClosure = (ImageViewerConfiguration) -> ()

public init(configurationClosure: ConfigurationClosure) {
configurationClosure(self)
public init(configurationClosure: ConfigurationClosure?) {
configurationClosure?(self)
}
}
80 changes: 63 additions & 17 deletions ImageViewer/ImageViewerController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,63 @@ public final class ImageViewerController: UIViewController {
@IBOutlet fileprivate var scrollView: UIScrollView!
@IBOutlet fileprivate var imageView: UIImageView!
@IBOutlet fileprivate var activityIndicator: UIActivityIndicatorView!

@IBOutlet fileprivate var closeButton: UIButton!

fileprivate var transitionHandler: ImageViewerTransitioningHandler?
fileprivate let configuration: ImageViewerConfiguration?
public var configuration: ImageViewerConfiguration?

public override var prefersStatusBarHidden: Bool {
return true
}

public init(configuration: ImageViewerConfiguration?) {
self.configuration = configuration
super.init(nibName: String(describing: type(of: self)), bundle: Bundle(for: type(of: self)))

modalPresentationStyle = .overFullScreen
modalTransitionStyle = .crossDissolve
modalPresentationCapturesStatusBarAppearance = true
public static func imageViewerController(configuration: ImageViewerConfiguration?) -> ImageViewerController {
let storyboard = UIStoryboard(name: "ImageViewerController", bundle: Bundle(for: ImageViewerController.self))
let viewController = storyboard.instantiateViewController(withIdentifier: "ImageViewerController") as! ImageViewerController
viewController.configuration = configuration
return viewController
}

required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
super.init(coder: aDecoder)

modalPresentationStyle = .overFullScreen
modalTransitionStyle = .crossDissolve
modalPresentationCapturesStatusBarAppearance = true
}

override public func viewDidLoad() {
super.viewDidLoad()
imageView.image = configuration?.imageView?.image ?? configuration?.image


guard let config = configuration else {
fatalError("configuration has not been set")
}

if !config.showCloseButton {
closeButton.isHidden = true
}

loadImage()

setupScrollView()
setupGestureRecognizers()
setupTransitions()
setupActivityIndicator()

if let color = configuration?.backgroundColor {
view.setBackgroundColors(color)
}
}

override public func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
resetZoomScale()
}

public func reloadImage() {
resetZoomScale()
loadImage()
}

public func resetZoomScale() {
scrollView.zoomScale = scrollView.minimumZoomScale
}
}

Expand All @@ -57,7 +85,24 @@ extension ImageViewerController: UIGestureRecognizerDelegate {
}
}

private extension UIView {
func setBackgroundColors(_ color: UIColor) {
for subview in subviews {
subview.backgroundColor = .clear
}
backgroundColor = color
}
}

private extension ImageViewerController {
func loadImage() {
if configuration?.imageBlock == nil {
imageView.image = configuration?.imageView?.image ?? configuration?.image
} else {
setupActivityIndicator()
}
}

func setupScrollView() {
scrollView.decelerationRate = UIScrollView.DecelerationRate.fast
scrollView.alwaysBounceVertical = true
Expand Down Expand Up @@ -86,10 +131,11 @@ private extension ImageViewerController {
guard let block = configuration?.imageBlock else { return }
activityIndicator.startAnimating()
block { [weak self] image in
guard let image = image else { return }
DispatchQueue.main.async {
self?.activityIndicator.stopAnimating()
self?.imageView.image = image
if image != nil {
self?.imageView.image = image
}
}
}
}
Expand Down Expand Up @@ -118,7 +164,7 @@ private extension ImageViewerController {

@objc func imageViewPanned(_ recognizer: UIPanGestureRecognizer) {
guard transitionHandler != nil else { return }

let translation = recognizer.translation(in: imageView)
let velocity = recognizer.velocity(in: imageView)

Expand Down
7 changes: 6 additions & 1 deletion ImageViewer/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>
<key>UISupportedInterfaceOrientations</key>
<array/>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
Expand Down
90 changes: 90 additions & 0 deletions ImageViewer/Resources/ImageViewerController.storyboard
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="kZj-RK-OKA">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--ImageViewerController-->
<scene sceneID="hR8-hj-Ztr">
<objects>
<viewController storyboardIdentifier="ImageViewerController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="kZj-RK-OKA" userLabel="ImageViewerController" customClass="ImageViewerController" customModule="SimpleImageViewer" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="xMj-hu-ovV">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" contentInsetAdjustmentBehavior="never" maximumZoomScale="6" translatesAutoresizingMaskIntoConstraints="NO" id="Q9r-OG-GXk">
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
<subviews>
<imageView contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="0iu-Oi-AF0">
<rect key="frame" x="0.0" y="0.0" width="375" height="647"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</imageView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="0iu-Oi-AF0" secondAttribute="bottom" id="0Oe-bm-8xl"/>
<constraint firstItem="0iu-Oi-AF0" firstAttribute="leading" secondItem="Q9r-OG-GXk" secondAttribute="leading" id="0nB-5g-cvJ"/>
<constraint firstAttribute="trailing" secondItem="0iu-Oi-AF0" secondAttribute="trailing" id="HRO-x2-zwG"/>
<constraint firstItem="0iu-Oi-AF0" firstAttribute="top" secondItem="Q9r-OG-GXk" secondAttribute="top" id="oZ8-YS-rWi"/>
</constraints>
<connections>
<outlet property="delegate" destination="kZj-RK-OKA" id="RtE-r4-x9G"/>
</connections>
</scrollView>
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="Poi-jR-Hpv">
<rect key="frame" x="169" y="315" width="37" height="37"/>
</activityIndicatorView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ufl-Cy-QEF">
<rect key="frame" x="0.0" y="20" width="44" height="44"/>
<constraints>
<constraint firstAttribute="width" constant="44" id="IaR-c8-3Kz"/>
<constraint firstAttribute="height" constant="44" id="XuX-4G-9fY"/>
</constraints>
<state key="normal" image="closeButton">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="closeButtonPressed" destination="kZj-RK-OKA" eventType="touchUpInside" id="aAX-fr-D8m"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="5fz-PG-yOy" firstAttribute="trailing" secondItem="Q9r-OG-GXk" secondAttribute="trailing" id="185-Mt-Beq"/>
<constraint firstItem="5fz-PG-yOy" firstAttribute="bottom" secondItem="Q9r-OG-GXk" secondAttribute="bottom" id="C6z-o2-xdi"/>
<constraint firstItem="Poi-jR-Hpv" firstAttribute="centerX" secondItem="5fz-PG-yOy" secondAttribute="centerX" id="Iy4-VA-2lE"/>
<constraint firstItem="ufl-Cy-QEF" firstAttribute="top" secondItem="xMj-hu-ovV" secondAttribute="topMargin" id="T3f-Eh-Jjc"/>
<constraint firstItem="0iu-Oi-AF0" firstAttribute="height" secondItem="5fz-PG-yOy" secondAttribute="height" id="bJZ-Rc-5xf"/>
<constraint firstItem="0iu-Oi-AF0" firstAttribute="width" secondItem="xMj-hu-ovV" secondAttribute="width" id="gaa-My-BoV"/>
<constraint firstItem="Poi-jR-Hpv" firstAttribute="centerY" secondItem="xMj-hu-ovV" secondAttribute="centerY" id="kh0-NS-H2G"/>
<constraint firstItem="Q9r-OG-GXk" firstAttribute="leading" secondItem="5fz-PG-yOy" secondAttribute="leading" id="lWw-kU-pPy"/>
<constraint firstItem="ufl-Cy-QEF" firstAttribute="leading" secondItem="5fz-PG-yOy" secondAttribute="leading" id="o66-bj-QTO"/>
<constraint firstItem="Q9r-OG-GXk" firstAttribute="top" secondItem="5fz-PG-yOy" secondAttribute="top" id="y9S-jd-Pas"/>
</constraints>
<viewLayoutGuide key="safeArea" id="5fz-PG-yOy"/>
</view>
<extendedEdge key="edgesForExtendedLayout" top="YES"/>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<connections>
<outlet property="activityIndicator" destination="Poi-jR-Hpv" id="jJx-U5-VGd"/>
<outlet property="closeButton" destination="ufl-Cy-QEF" id="XzE-R4-1hO"/>
<outlet property="imageView" destination="0iu-Oi-AF0" id="DRT-dP-WD0"/>
<outlet property="scrollView" destination="Q9r-OG-GXk" id="nml-is-CPi"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="LTa-51-cZ2" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="0.0" y="0.0"/>
</scene>
</scenes>
<resources>
<image name="closeButton" width="13" height="13"/>
</resources>
</document>
Loading