Skip to content
This repository was archived by the owner on Jun 30, 2021. It is now read-only.

Commit 25063be

Browse files
committed
Add tap listener for the view and animation on tap
- Add helper class `LPShadowView` to easily animate shadows in views. - Both `LPShadowImageView` and `LPCounterView` now derive from `LPShadowView` - Add animations for on touch events - Increase requirement to iOS 10.0 since using `UIViewPropretyAnimator` - Add missing documentation and new docs for - Add new `LPThumbnailViewDelegate` to allow user to listen for taps in view and action on them.
1 parent e136464 commit 25063be

29 files changed

+1036
-218
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
## Version 1.1.0
2+
3+
- Add tap listener for the view and animation on tap
4+
- Add helper class `LPShadowView` to easily animate shadows in views.
5+
- Both `LPShadowImageView` and `LPCounterView` now derive from `LPShadowView`
6+
- Add animations for on touch events
7+
- Increase requirement to iOS 10.0 since using `UIViewPropretyAnimator`
8+
- Add missing documentation and new docs for
9+
- Add new `LPThumbnailViewDelegate` to allow user to listen for taps in view and action on them.
10+
11+
## Version 1.0.0
12+
13+
Initial release

Example/LPThumbnailViewExample/ViewController.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@
99
import UIKit
1010
import LPThumbnailView
1111

12-
class ViewController: UIViewController {
12+
class ViewController: UIViewController, LPThumbnailViewDelegate {
1313

1414
var touchCount = 0
1515

1616
@IBOutlet weak var thumbnailView: LPThumbnailView!
1717

1818
override func viewDidLoad() {
1919
super.viewDidLoad()
20+
// Set delegate
21+
self.thumbnailView.delegate = self
2022
}
2123

2224
override func didReceiveMemoryWarning() {
@@ -72,4 +74,8 @@ class ViewController: UIViewController {
7274
}))
7375
self.present(alert, animated: true, completion: nil)
7476
}
77+
78+
func thumbnailViewWasTapped(_ view: LPThumbnailView) {
79+
print("Thumbnail tapped!")
80+
}
7581
}

Example/Pods/Pods.xcodeproj/project.pbxproj

Lines changed: 112 additions & 100 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

LPThumbnailView.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Pod::Spec.new do |s|
33

44
s.name = "LPThumbnailView"
5-
s.version = "1.0.0"
5+
s.version = "1.1.0"
66
s.summary = "An image thumbnail view for iOS."
77

88
s.description = <<-DESC

LPThumbnailView.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
A08A6F0E1FDB95F200A111E2 /* LPThumbnailViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A08A6F0D1FDB95F200A111E2 /* LPThumbnailViewTests.swift */; };
1414
A08A6F101FDB95F200A111E2 /* LPThumbnailView.h in Headers */ = {isa = PBXBuildFile; fileRef = A08A6F021FDB95F100A111E2 /* LPThumbnailView.h */; settings = {ATTRIBUTES = (Public, ); }; };
1515
A08A6F1A1FDB97F600A111E2 /* LPThumbnailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A08A6F191FDB97F600A111E2 /* LPThumbnailView.swift */; };
16+
A0C2426B1FE07E6D006EC771 /* LPShadowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0C2426A1FE07E6D006EC771 /* LPShadowView.swift */; };
17+
A0C2426D1FE0826F006EC771 /* LPThumbnailViewAnimationStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0C2426C1FE0826F006EC771 /* LPThumbnailViewAnimationStyle.swift */; };
18+
A0C2426F1FE08282006EC771 /* LPThumbnailViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0C2426E1FE08282006EC771 /* LPThumbnailViewDelegate.swift */; };
1619
/* End PBXBuildFile section */
1720

1821
/* Begin PBXContainerItemProxy section */
@@ -35,6 +38,9 @@
3538
A08A6F0D1FDB95F200A111E2 /* LPThumbnailViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LPThumbnailViewTests.swift; sourceTree = "<group>"; };
3639
A08A6F0F1FDB95F200A111E2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
3740
A08A6F191FDB97F600A111E2 /* LPThumbnailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LPThumbnailView.swift; sourceTree = "<group>"; };
41+
A0C2426A1FE07E6D006EC771 /* LPShadowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LPShadowView.swift; sourceTree = "<group>"; };
42+
A0C2426C1FE0826F006EC771 /* LPThumbnailViewAnimationStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LPThumbnailViewAnimationStyle.swift; sourceTree = "<group>"; };
43+
A0C2426E1FE08282006EC771 /* LPThumbnailViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LPThumbnailViewDelegate.swift; sourceTree = "<group>"; };
3844
/* End PBXFileReference section */
3945

4046
/* Begin PBXFrameworksBuildPhase section */
@@ -82,6 +88,9 @@
8288
A08A6F191FDB97F600A111E2 /* LPThumbnailView.swift */,
8389
A01A5E481FDBA68E00A963AE /* LPThumbnailCounterView.swift */,
8490
A01A5E4A1FDBADA900A963AE /* LPShadowImageView.swift */,
91+
A0C2426A1FE07E6D006EC771 /* LPShadowView.swift */,
92+
A0C2426C1FE0826F006EC771 /* LPThumbnailViewAnimationStyle.swift */,
93+
A0C2426E1FE08282006EC771 /* LPThumbnailViewDelegate.swift */,
8594
);
8695
path = LPThumbnailView;
8796
sourceTree = "<group>";
@@ -208,6 +217,9 @@
208217
files = (
209218
A01A5E491FDBA68E00A963AE /* LPThumbnailCounterView.swift in Sources */,
210219
A08A6F1A1FDB97F600A111E2 /* LPThumbnailView.swift in Sources */,
220+
A0C2426F1FE08282006EC771 /* LPThumbnailViewDelegate.swift in Sources */,
221+
A0C2426D1FE0826F006EC771 /* LPThumbnailViewAnimationStyle.swift in Sources */,
222+
A0C2426B1FE07E6D006EC771 /* LPShadowView.swift in Sources */,
211223
A01A5E4B1FDBADA900A963AE /* LPShadowImageView.swift in Sources */,
212224
);
213225
runOnlyForDeploymentPostprocessing = 0;

LPThumbnailView/LPShadowImageView.swift

Lines changed: 3 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,14 @@
66
// Copyright © 2017 Luis Padron. All rights reserved.
77
//
88

9-
/// Enum to determine the shadow type of a view.
10-
internal enum LPShadowType {
11-
/// Normal shadow, when view is not being touched.
12-
case normal
13-
14-
/// Touched shadow, when view is being touched
15-
case touched
16-
}
17-
189
/**
1910
LPShadowImageView
2011

2112
A simple view which contains an image view.
2213
This is needed to add both rounded corners AND shadow.
2314
Used inside of `LPThumbnailView`
2415
*/
25-
internal class LPShadowImageView: UIView {
26-
// MARK: Static properties
27-
28-
/// The shadow opacity when in the normal state
29-
private static let normalShadowOpactiy: Float = 0.7
30-
31-
/// The shadow opacity when in the touchedstate
32-
private static let touchedShadowOpacity: Float = 0.4
33-
34-
/// The shadow radius when in the normal state
35-
private static let normalShadowRadius: CGFloat = 4
36-
37-
/// The shadow radius when in the touched state
38-
private static let touchedShadowRadius: CGFloat = 3
39-
16+
internal class LPShadowImageView: LPShadowView {
4017
// MARK: Members/Properties
4118

4219
/// The image for the `imageView`
@@ -66,50 +43,15 @@ internal class LPShadowImageView: UIView {
6643
// MARK: Helpers
6744

6845
/// Helper function to initialize the view.
69-
private func initialize() {
70-
self.setShadowTo(.normal, duration: 0.0)
46+
internal override func initialize() {
47+
super.initialize()
7148
self.addSubview(imageView)
7249
self.imageView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
7350
self.imageView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
7451
self.imageView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
7552
self.imageView.heightAnchor.constraint(equalTo: self.heightAnchor).isActive = true
7653
}
7754

78-
/// Sets a normal shadow for this view, i.e when not being touched, duration of 0 means no animation
79-
internal func setShadowTo(_ type: LPShadowType, duration: TimeInterval) {
80-
self.layer.shadowColor = UIColor.black.cgColor
81-
self.layer.shadowOffset = CGSize(width: 0, height: 0)
82-
83-
// Animate the shadow opacity and radius change
84-
let shadowOpacityAnimation = CABasicAnimation(keyPath: "shadowOpacity")
85-
shadowOpacityAnimation.fromValue = type == .normal ? LPShadowImageView.touchedShadowOpacity :
86-
LPShadowImageView.normalShadowOpactiy
87-
shadowOpacityAnimation.toValue = type == .normal ? LPShadowImageView.normalShadowOpactiy :
88-
LPShadowImageView.touchedShadowOpacity
89-
shadowOpacityAnimation.duration = duration
90-
let shadowRadiusAnimation = CABasicAnimation(keyPath: "shadowRadius")
91-
shadowRadiusAnimation.fromValue = type == .normal ? LPShadowImageView.touchedShadowRadius :
92-
LPShadowImageView.normalShadowRadius
93-
shadowRadiusAnimation.toValue = type == .normal ? LPShadowImageView.normalShadowRadius :
94-
LPShadowImageView.touchedShadowRadius
95-
shadowRadiusAnimation.duration = duration
96-
self.layer.add(shadowOpacityAnimation, forKey: "shadowOpacity")
97-
self.layer.add(shadowRadiusAnimation, forKey: "shadowRadius")
98-
99-
self.layer.shadowOpacity = type == .normal ? LPShadowImageView.normalShadowOpactiy :
100-
LPShadowImageView.touchedShadowOpacity
101-
self.layer.shadowRadius = type == .normal ? LPShadowImageView.normalShadowRadius :
102-
LPShadowImageView.touchedShadowRadius
103-
}
104-
105-
/// Sets a touched shadow, i.e when view is touched the shadow becomes smaller
106-
internal func setTouchedShadow(duration: TimeInterval) {
107-
self.layer.shadowColor = UIColor.black.cgColor
108-
self.layer.shadowRadius = 2
109-
self.layer.shadowOpacity = 0.4
110-
self.layer.shadowOffset = CGSize(width: 0, height: 0)
111-
}
112-
11355
// MARK: Subviews
11456

11557
/// The image view which will be used inside of `LPThumbnailView`

LPThumbnailView/LPShadowView.swift

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//
2+
// LPShadowView.swift
3+
// LPThumbnailView
4+
//
5+
// Created by Luis Padron on 12/12/17.
6+
// Copyright © 2017 Luis Padron. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
/**
12+
Enum to determine the shadow type of a view.
13+
*/
14+
internal enum LPShadowType {
15+
/// Normal shadow, when view is not being touched.
16+
case normal
17+
/// Touched shadow, when view is being touched
18+
case touched
19+
}
20+
21+
/**
22+
LPShadowView
23+
24+
A simple view that draws a shadow and has some helper functions for updating/animating shadow.
25+
*/
26+
27+
internal class LPShadowView: UIView {
28+
29+
// MARK: Properties
30+
31+
/// The shadow opacity when in the normal state
32+
internal var normalShadowOpactiy: Float = 0.7
33+
34+
/// The shadow opacity when in the touchedstate
35+
internal var touchedShadowOpacity: Float = 0.4
36+
37+
/// The shadow radius when in the normal state
38+
internal var normalShadowRadius: CGFloat = 4
39+
40+
/// The shadow radius when in the touched state
41+
internal var touchedShadowRadius: CGFloat = 3
42+
43+
// MARK: Init
44+
45+
/// Overriden init
46+
override init(frame: CGRect) {
47+
super.init(frame: frame)
48+
self.initialize()
49+
}
50+
51+
/// Overriden init
52+
required init?(coder aDecoder: NSCoder) {
53+
super.init(coder: aDecoder)
54+
self.initialize()
55+
}
56+
57+
/// Initializes the view with a shadow
58+
internal func initialize() {
59+
self.setShadowTo(.normal, duration: 0.0)
60+
}
61+
62+
// MARK: Shadow functions
63+
64+
/// Updates the shadow of the view to the specified type.
65+
/// If duration = 0, no animation is performed.
66+
internal func setShadowTo(_ type: LPShadowType, duration: TimeInterval) {
67+
self.layer.shadowColor = UIColor.black.cgColor
68+
self.layer.shadowOffset = CGSize(width: 0, height: 0)
69+
70+
// Animate the shadow opacity and radius change
71+
let shadowOpacityAnimation = CABasicAnimation(keyPath: "shadowOpacity")
72+
shadowOpacityAnimation.fromValue = type == .normal ? touchedShadowOpacity : normalShadowOpactiy
73+
shadowOpacityAnimation.toValue = type == .normal ? normalShadowOpactiy : touchedShadowOpacity
74+
shadowOpacityAnimation.duration = duration
75+
let shadowRadiusAnimation = CABasicAnimation(keyPath: "shadowRadius")
76+
shadowRadiusAnimation.fromValue = type == .normal ? touchedShadowRadius : normalShadowRadius
77+
shadowRadiusAnimation.toValue = type == .normal ? normalShadowRadius : touchedShadowRadius
78+
shadowRadiusAnimation.duration = duration
79+
self.layer.add(shadowOpacityAnimation, forKey: "shadowOpacity")
80+
self.layer.add(shadowRadiusAnimation, forKey: "shadowRadius")
81+
82+
self.layer.shadowOpacity = type == .normal ? normalShadowOpactiy : touchedShadowOpacity
83+
self.layer.shadowRadius = type == .normal ? normalShadowRadius : touchedShadowRadius
84+
}
85+
}

LPThumbnailView/LPThumbnailCounterView.swift

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import UIKit
1313

1414
A circular view with a label, used inside of `LPThumbnailView`.
1515
*/
16-
internal class LPThumbnailCounterView: UIView {
16+
internal class LPThumbnailCounterView: LPShadowView {
1717
// MARK: Override
1818

1919
/// Overriden init
@@ -37,13 +37,16 @@ internal class LPThumbnailCounterView: UIView {
3737
// MARK: Helpers
3838

3939
/// Helper function to initialize the view
40-
private func initialize() {
40+
internal override func initialize() {
41+
// Set shadow properties
42+
self.normalShadowRadius = 2
43+
self.normalShadowOpactiy = 0.4
44+
self.touchedShadowRadius = 1
45+
self.touchedShadowOpacity = 0.2
46+
self.setShadowTo(.normal, duration: 0.0)
47+
// Constraint setup
4148
self.translatesAutoresizingMaskIntoConstraints = false
4249
self.layer.cornerRadius = self.bounds.width / 2
43-
self.layer.shadowColor = UIColor.black.cgColor
44-
self.layer.shadowRadius = 2
45-
self.layer.shadowOpacity = 0.4
46-
self.layer.shadowOffset = CGSize(width: 0, height: 0)
4750
// Add constraints for label
4851
self.addSubview(counterLabel)
4952
self.counterLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true

LPThumbnailView/LPThumbnailView.swift

Lines changed: 13 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,6 @@
88

99
import UIKit
1010

11-
/**
12-
LPThumbnailViewAnimationStyle
13-
14-
Enum for determining the type of animation to use when calling `addImage(_:duration:)`.
15-
*/
16-
public enum LPThumbnailViewAnimationStyle {
17-
/// Animation which shows a temporary image entering from the right of `LPThumbnailView` then fades into the actual view.
18-
case enterFromRight
19-
/// Animation which shows a temporary image entering from the left of `LPThumbnailView` then fades into the actual view.
20-
case enterFromLeft
21-
/// Animation which shows a temporary image entering from the top of `LPThumbnailView` then fades into the actual view.
22-
case enterFromTop
23-
/// Animation which shows a temporary image entering from underneath of `LPThumbnailView` then fades into the actual view.
24-
case enterFromBottom
25-
/// Simply cross dissolves from the old image to the image animating to.
26-
case crossDissolve
27-
}
28-
2911
/**
3012
LPThumbnailView
3113

@@ -36,6 +18,11 @@ open class LPThumbnailView: UIView {
3618

3719
// MARK: Public members/properties
3820

21+
/**
22+
The delegate responsible for listening to touches/updates propogated from this view.
23+
*/
24+
public weak var delegate: LPThumbnailViewDelegate? = nil
25+
3926
/**
4027
The images which the `LPThumbnailView` will display.
4128

@@ -324,8 +311,7 @@ private extension LPThumbnailView {
324311
self.backgroundColor = .clear
325312
self.translatesAutoresizingMaskIntoConstraints = false
326313
let gesture = UILongPressGestureRecognizer(target: self, action: #selector(self.viewWasTapped(_:)))
327-
gesture.minimumPressDuration = 0
328-
gesture.allowableMovement = 5
314+
gesture.minimumPressDuration = 0.05
329315
self.addGestureRecognizer(gesture)
330316
self.addSubview(imageView)
331317
self.addSubview(counterView)
@@ -419,35 +405,27 @@ private extension LPThumbnailView {
419405
}
420406
}
421407

422-
/// Called whenever the view is tapped
423-
@objc private func viewWasTapped(_ recognizer: UITapGestureRecognizer) {
408+
/// Called whenever the view is tapped, animates the view and calls the view delegate.
409+
@objc private func viewWasTapped(_ recognizer: UILongPressGestureRecognizer) {
424410
switch recognizer.state {
425411
case .began:
426-
print("Touches began")
427-
if animator.isRunning { print("stopping animation");animator.stopAnimation(true) }
412+
if animator.isRunning { animator.stopAnimation(true) }
428413
animator.addAnimations {
429414
self.transform = CGAffineTransform(scaleX: 0.95, y: 0.95)
430415
self.imageView.setShadowTo(.touched, duration: 0.4)
431-
}
432-
animator.startAnimation()
433-
434-
case .cancelled:
435-
print("Touches cancelled")
436-
if animator.isRunning { print("stopping animation");animator.stopAnimation(true) }
437-
animator.addAnimations {
438-
self.transform = CGAffineTransform.identity
439-
self.imageView.setShadowTo(.normal, duration: 0.4)
416+
self.counterView.setShadowTo(.touched, duration: 0.4)
440417
}
441418
animator.startAnimation()
442419

443420
case .ended:
444-
print("Touches ended")
445-
if animator.isRunning { print("stopping animation");animator.stopAnimation(true) }
421+
if animator.isRunning { animator.stopAnimation(true) }
446422
animator.addAnimations {
447423
self.transform = CGAffineTransform.identity
448424
self.imageView.setShadowTo(.normal, duration: 0.4)
425+
self.counterView.setShadowTo(.normal, duration: 0.4)
449426
}
450427
animator.startAnimation()
428+
self.delegate?.thumbnailViewWasTapped(self)
451429

452430
default: break
453431
}

0 commit comments

Comments
 (0)