Skip to content

Commit 1f97693

Browse files
author
Ryan Nystrom
committed
init
0 parents  commit 1f97693

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+3131
-0
lines changed

.gitignore

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Xcode
2+
#
3+
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4+
5+
## OSX
6+
.DS_Store
7+
8+
## Build generated
9+
build/
10+
DerivedData/
11+
12+
## Various settings
13+
*.pbxuser
14+
!default.pbxuser
15+
*.mode1v3
16+
!default.mode1v3
17+
*.mode2v3
18+
!default.mode2v3
19+
*.perspectivev3
20+
!default.perspectivev3
21+
xcuserdata/
22+
23+
## Other
24+
*.moved-aside
25+
*.xccheckout
26+
*.xcscmblueprint
27+
28+
## Obj-C/Swift specific
29+
*.hmap
30+
*.ipa
31+
*.dSYM.zip
32+
*.dSYM
33+
34+
## Playgrounds
35+
timeline.xctimeline
36+
playground.xcworkspace
37+
38+
# Swift Package Manager
39+
#
40+
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
41+
# Packages/
42+
# Package.pins
43+
.build/
44+
45+
# CocoaPods
46+
#
47+
# We recommend against adding the Pods directory to your .gitignore. However
48+
# you should judge for yourself, the pros and cons are mentioned at:
49+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
50+
#
51+
# Pods/
52+
53+
# Carthage
54+
#
55+
# Add this line if you want to avoid checking in source code from Carthage dependencies.
56+
# Carthage/Checkouts
57+
58+
Carthage/Build
59+
60+
# fastlane
61+
#
62+
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
63+
# screenshots whenever they are needed.
64+
# For more information about the recommended setup visit:
65+
# https://docs.fastlane.tools/best-practices/source-control/#source-control
66+
67+
fastlane/report.xml
68+
fastlane/Preview.html
69+
fastlane/screenshots
70+
fastlane/test_output
71+
node_modules
72+
73+
# Jekyll
74+
_site/
75+
.sass-cache/
76+
.jekyll-metadata
77+
78+
# secrets
79+
Resources/*.xcconfig
80+
*Secrets.swift*
81+
82+
83+
# FBSnapshotTestCase Failure Diffs
84+
FailureDiffs/

ContextMenu.podspec

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Pod::Spec.new do |spec|
2+
spec.name = 'ContextMenu'
3+
spec.version = '0.1.0'
4+
spec.license = { :type => 'MIT' }
5+
spec.homepage = 'https://github.com/GitHawkApp/ContextMenu'
6+
spec.authors = { 'Ryan Nystrom' => '[email protected]' }
7+
spec.summary = 'Context menu inspired by Things 3.'
8+
spec.source = { :git => 'https://github.com/GitHawkApp/ContextMenu.git', :tag => '#{s.version}' }
9+
spec.source_files = 'ContextMenu/*.swift'
10+
end

ContextMenu.xcodeproj/project.pbxproj

+379
Large diffs are not rendered by default.

ContextMenu.xcodeproj/project.xcworkspace/contents.xcworkspacedata

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ContextMenu/CGRect+Area.swift

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//
2+
// CGRect+Area.swift
3+
// ThingsUI
4+
//
5+
// Created by Ryan Nystrom on 3/10/18.
6+
// Copyright © 2018 Ryan Nystrom. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
extension CGRect {
12+
13+
internal func rect(point: CGPoint, xRemainder: Bool, yRemainder: Bool) -> CGRect {
14+
let xDiv = divided(atDistance: point.x, from: .minXEdge)
15+
let x = xRemainder ? xDiv.remainder : xDiv.slice
16+
let yDiv = x.divided(atDistance: point.y, from: .minYEdge)
17+
return yRemainder ? yDiv.remainder : yDiv.slice
18+
}
19+
20+
internal func area(corner: SourceViewCorner) -> CGFloat {
21+
let frame: CGRect
22+
switch corner.position {
23+
case .topLeft:
24+
frame = rect(point: corner.point, xRemainder: false, yRemainder: false)
25+
case .topRight:
26+
frame = rect(point: corner.point, xRemainder: true, yRemainder: false)
27+
case .bottomLeft:
28+
frame = rect(point: corner.point, xRemainder: false, yRemainder: true)
29+
case .bottomRight:
30+
frame = rect(point: corner.point, xRemainder: true, yRemainder: true)
31+
}
32+
return frame.width * frame.height
33+
}
34+
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//
2+
// ClippedContainerViewController.swift
3+
// ThingsUI
4+
//
5+
// Created by Ryan Nystrom on 3/10/18.
6+
// Copyright © 2018 Ryan Nystrom. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
internal class ClippedContainerViewController: UIViewController {
12+
13+
private let options: ContextMenu.Options
14+
private let containedViewController: UINavigationController
15+
16+
init(options: ContextMenu.Options, viewController: UIViewController) {
17+
self.options = options
18+
self.containedViewController = UINavigationController(rootViewController: viewController)
19+
super.init(nibName: nil, bundle: nil)
20+
}
21+
22+
required init?(coder aDecoder: NSCoder) {
23+
fatalError("init(coder:) has not been implemented")
24+
}
25+
26+
override func viewDidLoad() {
27+
super.viewDidLoad()
28+
view.layer.cornerRadius = options.containerStyle.cornerRadius
29+
view.layer.shadowRadius = options.containerStyle.shadowRadius
30+
view.layer.shadowColor = UIColor.black.cgColor
31+
view.layer.shadowOpacity = 0.2
32+
33+
containedViewController.view.layer.cornerRadius = view.layer.cornerRadius
34+
containedViewController.view.clipsToBounds = true
35+
containedViewController.setNavigationBarHidden(options.menuStyle == .minimal, animated: false)
36+
37+
let size = CGSize(width: 1, height: 1)
38+
UIGraphicsBeginImageContext(size)
39+
defer { UIGraphicsEndImageContext() }
40+
41+
options.containerStyle.backgroundColor.setFill()
42+
UIBezierPath(rect: CGRect(origin: .zero, size: size)).fill()
43+
44+
let image = UIGraphicsGetImageFromCurrentImageContext()
45+
let navigationBar = containedViewController.navigationBar
46+
navigationBar.isTranslucent = false
47+
navigationBar.setBackgroundImage(image, for: .any, barMetrics: .default)
48+
navigationBar.shadowImage = image
49+
50+
addChildViewController(containedViewController)
51+
view.addSubview(containedViewController.view)
52+
containedViewController.didMove(toParentViewController: self)
53+
54+
preferredContentSize = containedViewController.preferredContentSize
55+
}
56+
57+
override func viewWillLayoutSubviews() {
58+
super.viewWillLayoutSubviews()
59+
containedViewController.view.frame = view.bounds
60+
}
61+
62+
override func preferredContentSizeDidChange(forChildContentContainer container: UIContentContainer) {
63+
super.preferredContentSizeDidChange(forChildContentContainer: container)
64+
preferredContentSize = container.preferredContentSize
65+
}
66+
67+
}
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//
2+
// ContextMenu+Animations.swift
3+
// ThingsUI
4+
//
5+
// Created by Ryan Nystrom on 3/10/18.
6+
// Copyright © 2018 Ryan Nystrom. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
extension ContextMenu {
12+
13+
public struct AnimationDurations {
14+
15+
public let present: TimeInterval
16+
public let springPresent: TimeInterval
17+
public let springDamping: CGFloat
18+
public let springVelocity: CGFloat
19+
public let dismiss: TimeInterval
20+
public let resize: TimeInterval
21+
22+
public init(
23+
present: TimeInterval = 0.3,
24+
springPresent: TimeInterval = 0.5,
25+
springDamping: CGFloat = 0.8,
26+
springVelocity: CGFloat = 0.5,
27+
dismiss: TimeInterval = 0.15,
28+
resize: TimeInterval = 0.3
29+
) {
30+
self.present = present
31+
self.springPresent = springPresent
32+
self.springDamping = springDamping
33+
self.springVelocity = springVelocity
34+
self.dismiss = dismiss
35+
self.resize = resize
36+
}
37+
}
38+
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//
2+
// ContextMenu+ContainerStyle.swift
3+
// ThingsUI
4+
//
5+
// Created by Ryan Nystrom on 3/10/18.
6+
// Copyright © 2018 Ryan Nystrom. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
extension ContextMenu {
12+
13+
public struct ContainerStyle {
14+
15+
public let cornerRadius: CGFloat
16+
public let shadowRadius: CGFloat
17+
public let xPadding: CGFloat
18+
public let yPadding: CGFloat
19+
public let edgePadding: CGFloat
20+
public let backgroundColor: UIColor
21+
public let overlayColor: UIColor
22+
23+
public init(
24+
cornerRadius: CGFloat = 8,
25+
shadowRadius: CGFloat = 15,
26+
xPadding: CGFloat = 8,
27+
yPadding: CGFloat = 8,
28+
edgePadding: CGFloat = 15,
29+
backgroundColor: UIColor = .white,
30+
overlayColor: UIColor = UIColor(white: 0, alpha: 0.3)
31+
) {
32+
self.cornerRadius = cornerRadius
33+
self.shadowRadius = shadowRadius
34+
self.xPadding = xPadding
35+
self.yPadding = yPadding
36+
self.edgePadding = edgePadding
37+
self.backgroundColor = backgroundColor
38+
self.overlayColor = overlayColor
39+
}
40+
41+
}
42+
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// ContextMenu+ContextMenuPresentationControllerDelegate.swift
3+
// ThingsUI
4+
//
5+
// Created by Ryan Nystrom on 3/10/18.
6+
// Copyright © 2018 Ryan Nystrom. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
extension ContextMenu: ContextMenuPresentationControllerDelegate {
12+
13+
internal func willDismiss(presentationController: ContextMenuPresentationController) {
14+
guard item?.viewController === presentationController.presentedViewController else { return }
15+
item = nil
16+
}
17+
18+
}

ContextMenu/ContextMenu+Item.swift

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//
2+
// File.swift
3+
// ThingsUI
4+
//
5+
// Created by Ryan Nystrom on 3/10/18.
6+
// Copyright © 2018 Ryan Nystrom. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
extension ContextMenu {
12+
13+
internal class Item {
14+
15+
let options: Options
16+
let viewController: ClippedContainerViewController
17+
18+
weak var sourceView: UIView?
19+
20+
init(
21+
viewController: UIViewController,
22+
options: Options,
23+
sourceView: UIView?
24+
) {
25+
self.viewController = ClippedContainerViewController(options: options, viewController: viewController)
26+
self.options = options
27+
self.sourceView = sourceView
28+
}
29+
}
30+
31+
}
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// ContextMenu+Style.swift
3+
// ThingsUI
4+
//
5+
// Created by Ryan Nystrom on 3/10/18.
6+
// Copyright © 2018 Ryan Nystrom. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
extension ContextMenu {
12+
13+
public enum MenuStyle: Int {
14+
case `default`
15+
case minimal
16+
}
17+
18+
}

ContextMenu/ContextMenu+Options.swift

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//
2+
// ContextMenuOptions.swift
3+
// ThingsUI
4+
//
5+
// Created by Ryan Nystrom on 3/10/18.
6+
// Copyright © 2018 Ryan Nystrom. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
extension ContextMenu {
12+
13+
public struct Options {
14+
15+
let durations: AnimationDurations
16+
let containerStyle: ContainerStyle
17+
let menuStyle: MenuStyle
18+
19+
public init(
20+
durations: AnimationDurations = AnimationDurations(),
21+
containerStyle: ContainerStyle = ContainerStyle(),
22+
menuStyle: MenuStyle = .default
23+
) {
24+
self.durations = durations
25+
self.containerStyle = containerStyle
26+
self.menuStyle = menuStyle
27+
}
28+
}
29+
30+
}

0 commit comments

Comments
 (0)