18 |
19 |
20 |
--------------------------------------------------------------------------------
/Example/DynamicButtonExample/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // DynamicButtonExample
4 | //
5 | // Created by Yannick LORIOT on 06/09/15.
6 | // Copyright (c) 2015 Yannick LORIOT. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 | var window: UIWindow?
14 |
15 | func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
16 | // Override point for customization after application launch.
17 | return true
18 | }
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/Example/TVExample/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "assets" : [
3 | {
4 | "size" : "1280x768",
5 | "idiom" : "tv",
6 | "filename" : "App Icon - Large.imagestack",
7 | "role" : "primary-app-icon"
8 | },
9 | {
10 | "size" : "400x240",
11 | "idiom" : "tv",
12 | "filename" : "App Icon - Small.imagestack",
13 | "role" : "primary-app-icon"
14 | },
15 | {
16 | "size" : "1920x720",
17 | "idiom" : "tv",
18 | "filename" : "Top Shelf Image.imageset",
19 | "role" : "top-shelf-image"
20 | }
21 | ],
22 | "info" : {
23 | "version" : 1,
24 | "author" : "xcode"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/DynamicButton.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'DynamicButton'
3 | s.version = '6.2.1'
4 | s.license = 'MIT'
5 | s.summary = 'Yet another animated flat buttons in Swift'
6 | s.homepage = 'https://github.com/yannickl/DynamicButton.git'
7 | s.social_media_url = 'https://twitter.com/yannickloriot'
8 | s.authors = { 'Yannick Loriot' => 'contact@yannickloriot.com' }
9 | s.source = { :git => 'https://github.com/yannickl/DynamicButton.git', :tag => s.version }
10 | s.screenshot = 'http://yannickloriot.com/resources/dynamicbutton.gif'
11 |
12 | s.ios.deployment_target = '8.0'
13 | s.tvos.deployment_target = '9.0'
14 |
15 | s.ios.framework = 'UIKit'
16 | s.tvos.framework = 'UIKit'
17 |
18 | s.source_files = 'Sources/**/*.swift'
19 | s.requires_arc = true
20 | end
21 |
--------------------------------------------------------------------------------
/Example/DynamicButtonExampleTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Example/DynamicButtonExampleUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Example/DynamicButton/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | $(MARKETING_VERSION)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Example/DynamicButtonExample/MyCustomLine.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CustomButtonStyle.swift
3 | // DynamicButtonExample
4 | //
5 | // Created by Yannick LORIOT on 31/03/2017.
6 | // Copyright © 2017 Yannick LORIOT. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /// Diagonal line style: \
12 | struct MyCustomLine: DynamicButtonBuildableStyle {
13 | let pathVector: DynamicButtonPathVector
14 |
15 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
16 | let r = size / 2
17 | let c = cos(CGFloat.pi * 0.3)
18 | let s = sin(CGFloat.pi * 0.3)
19 |
20 | let p1 = CGMutablePath()
21 | p1.move(to: CGPoint(x: center.x + r * c, y: center.y + r * s))
22 | p1.addLine(to: CGPoint(x: center.x - r * c, y: center.y - r * s))
23 |
24 | pathVector = DynamicButtonPathVector(p1: p1, p2: p1, p3: p1, p4: p1)
25 | }
26 |
27 | /// "MyCustomLine" style.
28 | static var styleName: String {
29 | return "MyCustomLine"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Example/TVExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(MARKETING_VERSION)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | arm64
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015-present Yannick Loriot
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/Example/DynamicButtonExampleTests/XCTTestCaseTemplate.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * Splitflap
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 | import XCTest
29 |
30 | class XCTTestCaseTemplate: XCTestCase {
31 | override func setUp() {
32 | super.setUp()
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Example/DynamicButtonExample/DynamicButtonCellView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DynamicButtonCellView.swift
3 | // DynamicButtonExample
4 | //
5 | // Created by Yannick LORIOT on 13/09/15.
6 | // Copyright (c) 2015 Yannick LORIOT. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | protocol DynamicButtonCellDelegate: class {
12 | func styleDidSelected(style: DynamicButton.Style)
13 | }
14 |
15 | class DynamicButtonCellView: UICollectionViewCell {
16 | @IBOutlet weak var dynamicButton: DynamicButton!
17 |
18 | weak var delegate: DynamicButtonCellDelegate?
19 |
20 | var buttonStyle: DynamicButton.Style = .hamburger {
21 | didSet {
22 | dynamicButton.setStyle(buttonStyle, animated: false)
23 | }
24 | }
25 |
26 | var lineWidth: CGFloat = 2 {
27 | didSet {
28 | dynamicButton.lineWidth = lineWidth
29 | }
30 | }
31 |
32 | var strokeColor: UIColor = .black {
33 | didSet {
34 | dynamicButton.strokeColor = strokeColor
35 | }
36 | }
37 |
38 | var highlightStokeColor: UIColor? = nil {
39 | didSet {
40 | dynamicButton.highlightStokeColor = highlightStokeColor
41 | }
42 | }
43 |
44 | // MARK: - UIFocusEnvironment Methods
45 | weak override var preferredFocusedView: UIView? {
46 | get {
47 | return dynamicButton
48 | }
49 | }
50 |
51 | // MARK: - Action Methods
52 |
53 | @IBAction func dynamicButtonAction(_ sender: AnyObject) {
54 | delegate?.styleDidSelected(style: buttonStyle)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Example/DynamicButtonExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(MARKETING_VERSION)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationPortraitUpsideDown
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/Example/DynamicButtonExample/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // DynamicButtonExample
4 | //
5 | // Created by Yannick LORIOT on 06/09/15.
6 | // Copyright (c) 2015 Yannick LORIOT. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, DynamicButtonCellDelegate {
12 | @IBOutlet weak var dynamicButtonCollectionView: UICollectionView!
13 | @IBOutlet weak var dynamicButton: DynamicButton!
14 |
15 | private let CellIdentifier = "DynamicButtonCell"
16 |
17 | override func viewDidLoad() {
18 | super.viewDidLoad()
19 |
20 | dynamicButton.style = .custom(MyCustomLine.self)
21 | }
22 |
23 | override func viewDidLayoutSubviews() {
24 | super.viewDidLayoutSubviews()
25 |
26 | dynamicButton.layer.cornerRadius = dynamicButton.bounds.width / 2
27 | }
28 |
29 | // MARK: - UICollectionView DataSource Methods
30 |
31 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
32 | return DynamicButton.Style.all.count
33 | }
34 |
35 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
36 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CellIdentifier, for: indexPath) as! DynamicButtonCellView
37 |
38 | cell.buttonStyle = DynamicButton.Style.all[indexPath.row]
39 | cell.delegate = self
40 |
41 | return cell
42 | }
43 |
44 | func collectionView(_ collectionView: UICollectionView, canFocusItemAt indexPath: IndexPath) -> Bool {
45 | return true
46 | }
47 |
48 | // MARK: - DynamicButtonCell Delegate Methods
49 |
50 | func styleDidSelected(style: DynamicButton.Style) {
51 | dynamicButton.setStyle(style, animated: true)
52 | }
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleLocation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DynamicButtonStyleLocation.swift
3 | // DynamicButton
4 | //
5 | // Created by VICTOR WENG on 2018-10-22.
6 | //
7 |
8 | import UIKit
9 |
10 | /// Location symbol
11 | struct DynamicButtonStyleLocation: DynamicButtonBuildableStyle {
12 | let pathVector: DynamicButtonPathVector
13 |
14 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
15 | let ratio = size/60
16 | let p3 = PathHelper.circle(atCenter: CGPoint(x:offset.x+30*ratio, y:offset.y+23*ratio), radius: size / 5 - lineWidth)
17 |
18 | let p1 = UIBezierPath()
19 | p1.move(to: CGPoint(x: offset.x+30*ratio, y: offset.y+2.0*ratio))
20 | p1.addCurve(to: CGPoint(x: offset.x+7.3*ratio, y: offset.y+23*ratio), controlPoint1: CGPoint(x: offset.x+17.5*ratio, y: offset.y+2.0*ratio), controlPoint2: CGPoint(x: offset.x+7.3*ratio, y: offset.y+10.4*ratio))
21 | p1.addCurve(to: CGPoint(x: offset.x+30*ratio, y: offset.y+58.0*ratio), controlPoint1: CGPoint(x: offset.x+7.3*ratio, y: offset.y+35.6*ratio), controlPoint2: CGPoint(x: offset.x+30*ratio, y: offset.y+58.0*ratio))
22 | p1.addCurve(to: CGPoint(x: offset.x+53.1*ratio, y: offset.y+23*ratio), controlPoint1: CGPoint(x: offset.x+30*ratio, y: offset.y+58.0*ratio), controlPoint2: CGPoint(x: offset.x+53.1*ratio, y: offset.y+35.7*ratio))
23 | p1.addCurve(to: CGPoint(x: offset.x+30*ratio, y: offset.y+2.0*ratio), controlPoint1: CGPoint(x: offset.x+53.1*ratio, y: offset.y+10.3*ratio), controlPoint2: CGPoint(x: offset.x+42.9*ratio, y: offset.y+2.0*ratio))
24 | p1.close()
25 |
26 | pathVector = DynamicButtonPathVector(p1: p3, p2: p1.cgPath, p3: p3, p4: p3)
27 | }
28 |
29 | /// "Location" style.
30 | static var styleName: String {
31 | return "Location"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleDot.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Dot symbol style: .
30 | struct DynamicButtonStyleDot: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let p1 = UIBezierPath(roundedRect: CGRect(x: center.x - lineWidth / 2, y: center.y - lineWidth / 2, width: lineWidth, height: lineWidth), cornerRadius: size / 2).cgPath
35 |
36 | pathVector = DynamicButtonPathVector(p1: p1, p2: p1, p3: p1, p4: p1)
37 | }
38 |
39 | /// "Dot" style.
40 | static var styleName: String {
41 | return "Dot"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleVerticalLine.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Vertical line style: |
30 | struct DynamicButtonStyleVerticalLine: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let midSize = size / 2 - lineWidth
35 | let p1 = PathHelper.line(atCenter: center, radius: midSize, angle: .pi / 2)
36 |
37 | pathVector = DynamicButtonPathVector(p1: p1, p2: p1, p3: p1, p4: p1)
38 | }
39 |
40 | /// "Vertical Line" style.
41 | static var styleName: String {
42 | return "Line - Vertical"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleHorizontalLine.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Horizontal line style: ―
30 | struct DynamicButtonStyleHorizontalLine: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let midSize = size / 2 - lineWidth
35 | let p1 = PathHelper.line(atCenter: center, radius: midSize, angle: 0)
36 |
37 | pathVector = DynamicButtonPathVector(p1: p1, p2: p1, p3: p1, p4: p1)
38 | }
39 |
40 | /// "Horizontal Line" style.
41 | static var styleName: String {
42 | return "Line - Horizontal"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleClose.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Close symbol style: X
30 | struct DynamicButtonStyleClose: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let halfSize = size / 2
35 |
36 | let p12 = PathHelper.line(atCenter: center, radius: halfSize, angle: .pi / 4)
37 | let p34 = PathHelper.line(atCenter: center, radius: halfSize, angle: .pi / -4)
38 |
39 | pathVector = DynamicButtonPathVector(p1: p12, p2: p12, p3: p34, p4: p34)
40 | }
41 |
42 | /// "Close" style.
43 | static var styleName: String {
44 | return "Close"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStylePlus.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Plus symbol style: +
30 | struct DynamicButtonStylePlus: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let halfSize = size / 2 - lineWidth / 2
35 |
36 | let p12 = PathHelper.line(atCenter: center, radius: halfSize, angle: .pi / 2)
37 | let p34 = PathHelper.line(atCenter: center, radius: halfSize, angle: 0)
38 |
39 | pathVector = DynamicButtonPathVector(p1: p12, p2: p12, p3: p34, p4: p34)
40 | }
41 |
42 | /// "Plus" style.
43 | static var styleName: String {
44 | return "Plus"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleCirclePlus.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Plus symbol surrounded by a circle style
30 | struct DynamicButtonStyleCirclePlus: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let p1 = PathHelper.line(atCenter: center, radius: size / 3.2, angle: .pi / 2)
35 | let p2 = PathHelper.line(atCenter: center, radius: size / 3.2, angle: 0)
36 | let p3 = PathHelper.circle(atCenter: center, radius: size / 2 - lineWidth)
37 |
38 | pathVector = DynamicButtonPathVector(p1: p1, p2: p1, p3: p2, p4: p3)
39 | }
40 |
41 | /// "Circle Plus" style.
42 | static var styleName: String {
43 | return "Circle Plus"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleCircleClose.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Close symbol surrounded by a circle style
30 | struct DynamicButtonStyleCircleClose: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let p1 = PathHelper.line(atCenter: center, radius: size / 3.2, angle: .pi / 4)
35 | let p2 = PathHelper.line(atCenter: center, radius: size / 3.2, angle: .pi / -4)
36 | let p3 = PathHelper.circle(atCenter: center, radius: size / 2 - lineWidth)
37 |
38 | pathVector = DynamicButtonPathVector(p1: p1, p2: p1, p3: p2, p4: p3)
39 | }
40 |
41 | /// "Circle Close" style.
42 | static var styleName: String {
43 | return "Circle Close"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Example/DynamicButtonExample/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStylePause.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Pause symbol style: ‖
30 | struct DynamicButtonStylePause: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let size = size / 3
35 |
36 | let leftOffset = CGPoint(x: size / -2, y: 0)
37 | let rightOffset = CGPoint(x: size / 2, y: 0)
38 |
39 | let p12 = PathHelper.line(atCenter: center, radius: size, angle: .pi / 2, offset: leftOffset)
40 | let p34 = PathHelper.line(atCenter: center, radius: size, angle: .pi / 2, offset: rightOffset)
41 |
42 | pathVector = DynamicButtonPathVector(p1: p12, p2: p12, p3: p34, p4: p34)
43 | }
44 |
45 | /// "Pause" style.
46 | static var styleName: String {
47 | return "Player - Pause"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleHamburger.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Hamburger button style: ≡
30 | struct DynamicButtonStyleHamburger: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let midSize = size / 2 - lineWidth
35 |
36 | let p1 = PathHelper.line(atCenter: center, radius: midSize, angle: 0, offset: CGPoint(x: 0, y: size / -3.2))
37 | let p2 = PathHelper.line(atCenter: center, radius: midSize, angle: 0)
38 | let p3 = PathHelper.line(atCenter: center, radius: midSize, angle: 0, offset: CGPoint(x: 0, y: size / 3.2))
39 |
40 | pathVector = DynamicButtonPathVector(p1: p1, p2: p2, p3: p3, p4: p2)
41 | }
42 |
43 | /// "Hamburger" style.
44 | static var styleName: String {
45 | return "Hamburger"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonPathVector.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /**
30 | A path vector is a structure compound of 4 paths (p1, p2, p3, p4). It defines the geometric shape used to draw a `DynamicButton`.
31 | */
32 | public struct DynamicButtonPathVector {
33 | /// The path p1.
34 | public let p1: CGPath
35 |
36 | /// The path p2.
37 | public let p2: CGPath
38 |
39 | /// The path p3.
40 | public let p3: CGPath
41 |
42 | /// The path p4.
43 | public let p4: CGPath
44 |
45 | /// Default constructor.
46 | public init(p1 : CGPath, p2 : CGPath, p3 : CGPath, p4 : CGPath) {
47 | self.p1 = p1
48 | self.p2 = p2
49 | self.p3 = p3
50 | self.p4 = p4
51 | }
52 |
53 | /// The path vectore whose each path are equals to zero.
54 | public static let zero: DynamicButtonPathVector = DynamicButtonPathVector(p1: CGMutablePath(), p2: CGMutablePath(), p3: CGMutablePath(), p4: CGMutablePath())
55 | }
56 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleNone.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// No style
30 | struct DynamicButtonStyleNone: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let p1 = UIBezierPath(rect: CGRect(x: center.x - size, y: center.y - size, width: 0, height: 0)).cgPath
35 | let p2 = UIBezierPath(rect: CGRect(x: center.x + size, y: center.y - size, width: 0, height: 0)).cgPath
36 | let p3 = UIBezierPath(rect: CGRect(x: center.x - size, y: center.y + size, width: 0, height: 0)).cgPath
37 | let p4 = UIBezierPath(rect: CGRect(x: center.x + size, y: center.y + size, width: 0, height: 0)).cgPath
38 |
39 | pathVector = DynamicButtonPathVector(p1: p1, p2: p2, p3: p3, p4: p4)
40 | }
41 |
42 | /// "None" style.
43 | static var styleName: String {
44 | return ""
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleCheckMark.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Check mark style: ✓
30 | struct DynamicButtonStyleCheckMark: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let headPoint = CGPoint(x: center.x, y: center.y)
35 | let leftPoint = CGPoint(x: offset.x + size / 4, y: offset.y + size / 4)
36 | let rightPoint = CGPoint(x: offset.x + size, y: offset.y)
37 | let offsetPoint = CGPoint(x: -size / 8, y: size / 4)
38 |
39 | let p1 = PathHelper.line(from: headPoint, to: leftPoint, offset: offsetPoint)
40 | let p2 = PathHelper.line(from: headPoint, to: rightPoint, offset: offsetPoint)
41 |
42 | pathVector = DynamicButtonPathVector(p1: p1, p2: p1, p3: p2, p4: p2)
43 | }
44 |
45 | /// "Check Mark" style.
46 | static var styleName: String {
47 | return "Check Mark"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Example/DynamicButtonExample.xcodeproj/xcshareddata/xcschemes/DynamicButtonTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
16 |
18 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
40 |
41 |
42 |
43 |
49 |
50 |
52 |
53 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleArrowUp.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Upwards arrow style: ↑
30 | struct DynamicButtonStyleArrowUp: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let bottomPoint = CGPoint(x: center.x, y: offset.y + size - lineWidth / 2)
35 | let headPoint = CGPoint(x: center.x, y: offset.y + lineWidth)
36 | let leftPoint = CGPoint(x: center.x - size / 3.2, y: offset.y + size / 3.2)
37 | let rightPoint = CGPoint(x: center.x + size / 3.2, y: offset.y + size / 3.2)
38 |
39 | let p1 = PathHelper.line(from: bottomPoint, to: headPoint)
40 | let p2 = PathHelper.line(from: headPoint, to: leftPoint)
41 | let p3 = PathHelper.line(from: headPoint, to: rightPoint)
42 |
43 | pathVector = DynamicButtonPathVector(p1: p1, p2: p2, p3: p3, p4: p1)
44 | }
45 |
46 | /// "Arrow Up" style.
47 | static var styleName: String {
48 | return "Arrow Up"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleArrowLeft.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Leftwards arrow style: ←
30 | struct DynamicButtonStyleArrowLeft: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let rightPoint = CGPoint(x: offset.x + size - lineWidth / 2, y: center.y)
35 | let headPoint = CGPoint(x: offset.x + lineWidth, y: center.y)
36 | let topPoint = CGPoint(x: offset.x + size / 3.2, y: center.y + size / 3.2)
37 | let bottomPoint = CGPoint(x: offset.x + size / 3.2, y: center.y - size / 3.2)
38 |
39 | let p1 = PathHelper.line(from: rightPoint, to: headPoint)
40 | let p2 = PathHelper.line(from: headPoint, to: topPoint)
41 | let p3 = PathHelper.line(from: headPoint, to: bottomPoint)
42 |
43 | pathVector = DynamicButtonPathVector(p1: p1, p2: p2, p3: p3, p4: p1)
44 | }
45 |
46 | /// "Arrow Left" style.
47 | static var styleName: String {
48 | return "Arrow Left"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleCaretUp.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Up caret style: ⌃
30 | struct DynamicButtonStyleCaretUp: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let thirdSize = size / 3
35 | let sixthSize = size / 6
36 |
37 | let a = CGPoint(x: center.x, y: center.y - sixthSize)
38 | let b = CGPoint(x: center.x - thirdSize, y: center.y + sixthSize)
39 | let c = CGPoint(x: center.x + thirdSize, y: center.y + sixthSize)
40 |
41 | let offsetFromCenter = PathHelper.gravityPointOffset(fromCenter: center, a: a, b: b, c: c)
42 |
43 | let p12 = PathHelper.line(from: a, to: b, offset: offsetFromCenter)
44 | let p34 = PathHelper.line(from: a, to: c, offset: offsetFromCenter)
45 |
46 | pathVector = DynamicButtonPathVector(p1: p12, p2: p12, p3: p34, p4: p34)
47 | }
48 |
49 | /// "Caret Up" style.
50 | static var styleName: String {
51 | return "Caret Up"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleStop.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Stop symbol style: ◼ \{U+2588}
30 | struct DynamicButtonStyleStop: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let thirdSize = size / 3
35 |
36 | let a = CGPoint(x: center.x - thirdSize, y: center.y - thirdSize)
37 | let b = CGPoint(x: center.x - thirdSize, y: center.y + thirdSize)
38 | let c = CGPoint(x: center.x + thirdSize, y: center.y + thirdSize)
39 | let d = CGPoint(x: center.x + thirdSize, y: center.y - thirdSize)
40 |
41 | let p1 = PathHelper.line(from: a, to: b)
42 | let p2 = PathHelper.line(from: b, to: c)
43 | let p3 = PathHelper.line(from: c, to: d)
44 | let p4 = PathHelper.line(from: d, to: a)
45 |
46 | pathVector = DynamicButtonPathVector(p1: p1, p2: p2, p3: p3, p4: p4)
47 | }
48 |
49 | /// "Stop" style.
50 | static var styleName: String {
51 | return "Player - Stop"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleArrowDown.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Downwards arrow style: ↓
30 | struct DynamicButtonStyleArrowDown: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let topPoint = CGPoint(x: center.x, y: offset.y + lineWidth / 2)
35 | let headPoint = CGPoint(x: center.x, y: offset.y + size - lineWidth)
36 | let leftPoint = CGPoint(x: center.x - size / 3.2, y: offset.y + size - size / 3.2)
37 | let rightPoint = CGPoint(x: center.x + size / 3.2, y: offset.y + size - size / 3.2)
38 |
39 | let p1 = PathHelper.line(from: topPoint, to: headPoint)
40 | let p2 = PathHelper.line(from: headPoint, to: leftPoint)
41 | let p3 = PathHelper.line(from: headPoint, to: rightPoint)
42 |
43 | pathVector = DynamicButtonPathVector(p1: p1, p2: p2, p3: p3, p4: p1)
44 | }
45 |
46 | /// "Arrow Down" style.
47 | static var styleName: String {
48 | return "Arrow Down"
49 | }
50 | }
51 |
52 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleCaretDown.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Down caret style: ⌄
30 | struct DynamicButtonStyleCaretDown: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let thirdSize = size / 3
35 | let sixthSize = size / 6
36 |
37 | let a = CGPoint(x: center.x, y: center.y + sixthSize)
38 | let b = CGPoint(x: center.x - thirdSize, y: center.y - sixthSize)
39 | let c = CGPoint(x: center.x + thirdSize, y: center.y - sixthSize)
40 |
41 | let offsetFromCenter = PathHelper.gravityPointOffset(fromCenter: center, a: a, b: b, c: c)
42 |
43 | let p12 = PathHelper.line(from: a, to: b, offset: offsetFromCenter)
44 | let p34 = PathHelper.line(from: a, to: c, offset: offsetFromCenter)
45 |
46 | pathVector = DynamicButtonPathVector(p1: p12, p2: p12, p3: p34, p4: p34)
47 | }
48 |
49 | /// "Caret Down" style.
50 | static var styleName: String {
51 | return "Caret Down"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleCaretLeft.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Left caret style: ‹
30 | struct DynamicButtonStyleCaretLeft: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let thirdSize = size / 3
35 | let sixthSize = size / 6
36 |
37 | let a = CGPoint(x: center.x - sixthSize, y: center.y)
38 | let b = CGPoint(x: center.x + sixthSize, y: center.y + thirdSize)
39 | let c = CGPoint(x: center.x + sixthSize, y: center.y - thirdSize)
40 |
41 | let offsetFromCenter = PathHelper.gravityPointOffset(fromCenter: center, a: a, b: b, c: c)
42 |
43 | let p12 = PathHelper.line(from: a, to: b, offset: offsetFromCenter)
44 | let p34 = PathHelper.line(from: a, to: c, offset: offsetFromCenter)
45 |
46 | pathVector = DynamicButtonPathVector(p1: p12, p2: p12, p3: p34, p4: p34)
47 | }
48 |
49 | /// "Caret Left" style.
50 | static var styleName: String {
51 | return "Caret Left"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleArrowRight.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Rightwards arrow style: →
30 | struct DynamicButtonStyleArrowRight: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let leftPoint = CGPoint(x: offset.x + lineWidth / 2, y: center.y)
35 | let headPoint = CGPoint(x: offset.x + size - lineWidth, y: center.y)
36 | let topPoint = CGPoint(x: offset.x + size - size / 3.2, y: center.y + size / 3.2)
37 | let bottomPoint = CGPoint(x: offset.x + size - size / 3.2, y: center.y - size / 3.2)
38 |
39 | let p1 = PathHelper.line(from: leftPoint, to: headPoint)
40 | let p2 = PathHelper.line(from: headPoint, to: topPoint)
41 | let p3 = PathHelper.line(from: headPoint, to: bottomPoint)
42 |
43 | pathVector = DynamicButtonPathVector(p1: p1, p2: p2, p3: p3, p4: p1)
44 | }
45 |
46 | /// "Arrow Right" style.
47 | static var styleName: String {
48 | return "Arrow Right"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleCaretRight.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Left caret style: ›
30 | struct DynamicButtonStyleCaretRight: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let thirdSize = size / 3
35 | let sixthSize = size / 6
36 |
37 | let a = CGPoint(x: center.x + sixthSize, y: center.y)
38 | let b = CGPoint(x: center.x - sixthSize, y: center.y + thirdSize)
39 | let c = CGPoint(x: center.x - sixthSize, y: center.y - thirdSize)
40 |
41 | let offsetFromCenter = PathHelper.gravityPointOffset(fromCenter: center, a: a, b: b, c: c)
42 |
43 | let p12 = PathHelper.line(from: a, to: b, offset: offsetFromCenter)
44 | let p34 = PathHelper.line(from: a, to: c, offset: offsetFromCenter)
45 |
46 | pathVector = DynamicButtonPathVector(p1: p12, p2: p12, p3: p34, p4: p34)
47 | }
48 |
49 | /// "Caret Right" style.
50 | static var styleName: String {
51 | return "Caret Right"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStylePlay.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Play symbol style: ► \{U+25B6}
30 | struct DynamicButtonStylePlay: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let thirdSize = size / 3
35 | let sixthSize = size / 6
36 |
37 | let a = CGPoint(x: center.x - thirdSize, y: center.y - thirdSize)
38 | let b = CGPoint(x: center.x - thirdSize, y: center.y + thirdSize)
39 | let c = CGPoint(x: center.x + sixthSize, y: center.y)
40 |
41 | let ofc = PathHelper.gravityPointOffset(fromCenter: center, a: a, b: b, c: c)
42 |
43 | let p1 = PathHelper.line(from: a, to: b, offset: ofc)
44 | let p2 = PathHelper.line(from: b, to: c, offset: ofc)
45 | let p3 = PathHelper.line(from: a, to: c, offset: ofc)
46 |
47 | pathVector = DynamicButtonPathVector(p1: p1, p2: p2, p3: p3, p4: p3)
48 | }
49 |
50 | /// "Play" style.
51 | static var styleName: String {
52 | return "Player - Play"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleVerticalMoreOptions.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Vertical more options style: ⋮
30 | struct DynamicButtonStyleVerticalMoreOptions: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let y = center.y - lineWidth / 2
35 | let midSize = size / 2
36 |
37 | let p1 = UIBezierPath(roundedRect: CGRect(x: center.x - lineWidth / 2, y: y - midSize + lineWidth, width: lineWidth, height: lineWidth), cornerRadius: lineWidth / 2).cgPath
38 | let p2 = UIBezierPath(roundedRect: CGRect(x: center.x - lineWidth / 2, y: y, width: lineWidth, height: lineWidth), cornerRadius: lineWidth / 2).cgPath
39 | let p3 = UIBezierPath(roundedRect: CGRect(x: center.x - lineWidth / 2, y: y + midSize - lineWidth, width: lineWidth, height: lineWidth), cornerRadius: lineWidth / 2).cgPath
40 |
41 | pathVector = DynamicButtonPathVector(p1: p1, p2: p2, p3: p3, p4: p2)
42 | }
43 |
44 | /// "Vertical More Options" style.
45 | static var styleName: String {
46 | return "More Options - Vertical"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleHorizontalMoreOptions.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Horizontal more options style: …
30 | struct DynamicButtonStyleHorizontalMoreOptions: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let x = center.x - lineWidth / 2
35 | let midSize = size / 2
36 |
37 | let p1 = UIBezierPath(roundedRect: CGRect(x: x - midSize + lineWidth, y: center.y - lineWidth / 2, width: lineWidth, height: lineWidth), cornerRadius: lineWidth / 2).cgPath
38 | let p2 = UIBezierPath(roundedRect: CGRect(x: x, y: center.y - lineWidth / 2, width: lineWidth, height: lineWidth), cornerRadius: lineWidth / 2).cgPath
39 | let p3 = UIBezierPath(roundedRect: CGRect(x: x + midSize - lineWidth, y: center.y - lineWidth / 2, width: lineWidth, height: lineWidth), cornerRadius: lineWidth / 2).cgPath
40 |
41 | pathVector = DynamicButtonPathVector(p1: p1, p2: p2, p3: p3, p4: p2)
42 | }
43 |
44 | /// "Horizontal More Options" style.
45 | static var styleName: String {
46 | return "More Options - Horizontal"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleReload.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Reload symbol style: ↻
30 | struct DynamicButtonStyleReload: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let sixthSize = size / 6
35 | let fifthPi = CGFloat.pi / 5.5
36 |
37 | let endAngle = ((3 * CGFloat.pi) / 2) - fifthPi
38 | let endPoint = PathHelper.point(fromCenter: center, radius: size / 2 - lineWidth, angle: endAngle)
39 |
40 | let curveBezierPath = UIBezierPath(arcCenter: center, radius: size / 2 - lineWidth, startAngle: -fifthPi, endAngle: endAngle, clockwise: true)
41 |
42 | let p1 = PathHelper.line(from: endPoint, to: PathHelper.point(fromCenter: endPoint, radius: sixthSize, angle: .pi))
43 | let p2 = PathHelper.line(from: endPoint, to: PathHelper.point(fromCenter: endPoint, radius: sixthSize, angle: CGFloat.pi / 2))
44 | let p34 = curveBezierPath.cgPath
45 |
46 | pathVector = DynamicButtonPathVector(p1: p1, p2: p2, p3: p34, p4: p34)
47 | }
48 |
49 | /// "Reload" style.
50 | static var styleName: String {
51 | return "Reload"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleDownload.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Downwards triangle-headed arrow to bar style: ⤓
30 | struct DynamicButtonStyleDownload: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let topPoint = CGPoint(x: center.x, y: offset.y + lineWidth / 2)
35 | let headPoint = CGPoint(x: center.x, y: offset.y + size - lineWidth)
36 | let leftPoint = CGPoint(x: center.x - size / 3.2, y: offset.y + size - lineWidth)
37 | let rightPoint = CGPoint(x: center.x + size / 3.2, y: offset.y + size - lineWidth)
38 |
39 | let p1 = PathHelper.line(from: topPoint, to: headPoint)
40 | let p2 = PathHelper.line(from: headPoint, to: CGPoint(x: center.x - size / 3.2, y: offset.y + size - size / 3.2))
41 | let p3 = PathHelper.line(from: headPoint, to: CGPoint(x: center.x + size / 3.2, y: offset.y + size - size / 3.2))
42 | let p4 = PathHelper.line(from: leftPoint, to: rightPoint)
43 |
44 | pathVector = DynamicButtonPathVector(p1: p1, p2: p2, p3: p3, p4: p4)
45 | }
46 |
47 | /// "Download" style.
48 | static var styleName: String {
49 | return "Download"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleRewind.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Rewind symbol style: ≪
30 | struct DynamicButtonStyleRewind: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let thirdSize = size / 3
35 | let sixthSize = size / 6
36 |
37 | let a = CGPoint(x: center.x - sixthSize, y: center.y)
38 | let b = CGPoint(x: center.x + sixthSize, y: center.y + thirdSize)
39 | let c = CGPoint(x: center.x + sixthSize, y: center.y - thirdSize)
40 |
41 | let ofc = PathHelper.gravityPointOffset(fromCenter: center, a: a, b: b, c: c)
42 |
43 | let p1 = PathHelper.line(from: a, to: b, offset: CGPoint(x: ofc.x - sixthSize, y: ofc.y))
44 | let p2 = PathHelper.line(from: a, to: b, offset: CGPoint(x: ofc.x + sixthSize, y: ofc.y))
45 | let p3 = PathHelper.line(from: a, to: c, offset: CGPoint(x: ofc.x - sixthSize, y: ofc.y))
46 | let p4 = PathHelper.line(from: a, to: c, offset: CGPoint(x: ofc.x + sixthSize, y: ofc.y))
47 |
48 | pathVector = DynamicButtonPathVector(p1: p1, p2: p2, p3: p3, p4: p4)
49 | }
50 |
51 | /// "Rewind" style.
52 | static var styleName: String {
53 | return "Player - Rewind"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyles/DynamicButtonStyleFastForward.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Fast forward style: ≫
30 | struct DynamicButtonStyleFastForward: DynamicButtonBuildableStyle {
31 | let pathVector: DynamicButtonPathVector
32 |
33 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
34 | let thirdSize = size / 3
35 | let sixthSize = size / 6
36 |
37 | let a = CGPoint(x: center.x + sixthSize, y: center.y)
38 | let b = CGPoint(x: center.x - sixthSize, y: center.y + thirdSize)
39 | let c = CGPoint(x: center.x - sixthSize, y: center.y - thirdSize)
40 |
41 | let ofc = PathHelper.gravityPointOffset(fromCenter: center, a: a, b: b, c: c)
42 |
43 | let p1 = PathHelper.line(from: a, to: b, offset: CGPoint(x: ofc.x + sixthSize, y: ofc.y))
44 | let p2 = PathHelper.line(from: a, to: b, offset: CGPoint(x: ofc.x - sixthSize, y: ofc.y))
45 | let p3 = PathHelper.line(from: a, to: c, offset: CGPoint(x: ofc.x + sixthSize, y: ofc.y))
46 | let p4 = PathHelper.line(from: a, to: c, offset: CGPoint(x: ofc.x - sixthSize, y: ofc.y))
47 |
48 | pathVector = DynamicButtonPathVector(p1: p1, p2: p2, p3: p3, p4: p4)
49 | }
50 |
51 | /// "Fast Forward" style.
52 | static var styleName: String {
53 | return "Player - Fast Forward"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonBuildableStyle.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /**
30 | A dynamic button style must adopt the buildable protocol. It allows the `DynamicButton` to know how build and display the style's shape.
31 |
32 | A `DynamicButtonBuildableStyle` provides a `pathVector` vector property and an `init` method to build it.
33 | */
34 | public protocol DynamicButtonBuildableStyle: CustomStringConvertible {
35 | /// The path vector used by the `DynamicButton` to display the shape.
36 | var pathVector: DynamicButtonPathVector { get }
37 |
38 | /**
39 | The init provides some informations in order to build the `pathVector`.
40 |
41 | - parameter center: The center point of the area where path must be drawn.
42 | - parameter size: The size of the area. This is a float because the area is always a square.
43 | - parameter offset: The offset point of the button.
44 | - parameter lineWidth: The line width used to draw the stroke.
45 | */
46 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat)
47 |
48 | /// The style name as a string.
49 | static var styleName: String { get }
50 | }
51 |
52 | extension DynamicButtonBuildableStyle {
53 | internal func animationConfigurations(_ layer1: CAShapeLayer, layer2: CAShapeLayer, layer3: CAShapeLayer, layer4: CAShapeLayer) -> [(keyPath: String, layer: CAShapeLayer, newValue: CGPath?, key: String)] {
54 | return [(keyPath: "path", layer: layer4, newValue: pathVector.p4, key: "animateLine4Path"),
55 | (keyPath: "path", layer: layer1, newValue: pathVector.p1, key: "animateLine1Path"),
56 | (keyPath: "path", layer: layer2, newValue: pathVector.p2, key: "animateLine2Path"),
57 | (keyPath: "path", layer: layer3, newValue: pathVector.p3, key: "animateLine3Path")]
58 | }
59 |
60 | public var description: String {
61 | return Self.styleName
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Example/DynamicButtonExample.xcodeproj/xcshareddata/xcschemes/DynamicButton.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
70 |
71 |
72 |
73 |
75 |
76 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/Sources/PathHelper.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /// Helper methods to manipulate paths.
30 | internal class PathHelper {
31 | // MARK: - Representing the Button as Drawing Lines
32 |
33 | /// Creates a circle from a given center point and a radius.
34 | class func circle(atCenter center: CGPoint, radius: CGFloat) -> CGPath {
35 | let path = CGMutablePath()
36 |
37 | path.move(to: CGPoint(x: center.x + radius, y: center.y))
38 | path.addArc(center: CGPoint(x:center.x, y:center.y), radius: radius, startAngle: 0, endAngle: 2 * .pi, clockwise: false)
39 |
40 | return path
41 | }
42 |
43 | /// Creates an oblique line using a center point, a radius and an angle.
44 | class func line(atCenter center: CGPoint, radius: CGFloat, angle: CGFloat, offset: CGPoint = .zero) -> CGPath {
45 | let path = CGMutablePath()
46 |
47 | let c = cos(angle)
48 | let s = sin(angle)
49 |
50 | path.move(to: CGPoint(x: center.x + offset.x + radius * c, y: center.y + offset.y + radius * s))
51 | path.addLine(to: CGPoint(x: center.x + offset.x - radius * c, y: center.y + offset.y - radius * s))
52 |
53 | return path
54 | }
55 |
56 | /// Creates a line between two point.
57 | class func line(from startPoint: CGPoint, to endPoint: CGPoint, offset: CGPoint = .zero) -> CGPath {
58 | let path = CGMutablePath()
59 |
60 | path.move(to: CGPoint(x: offset.x + startPoint.x, y: offset.y + startPoint.y))
61 | path.addLine(to: CGPoint(x: offset.x + endPoint.x, y: offset.y + endPoint.y))
62 |
63 | return path
64 | }
65 |
66 | // MARK: - Trigonometry Methods
67 |
68 | /// Compute the gravity center from 3 points.
69 | class func gravityPointOffset(fromCenter center: CGPoint, a: CGPoint, b: CGPoint, c: CGPoint) -> CGPoint {
70 | let gravityCenter = CGPoint(x: (a.x + b.x + c.x) / 3, y: (a.y + b.y + c.y) / 3)
71 |
72 | return CGPoint(x: center.x - gravityCenter.x, y: center.y - gravityCenter.y)
73 | }
74 |
75 | // Compute the destination point from a center, an angle and a radius
76 | class func point(fromCenter center: CGPoint, radius: CGFloat, angle: CGFloat) -> CGPoint {
77 | return CGPoint(x: center.x + radius * cos(angle), y: center.y + radius * sin(angle))
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/Example/DynamicButtonExample/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
21 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/Example/DynamicButtonExample.xcodeproj/xcshareddata/xcschemes/DynamicButtonUITests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
45 |
46 |
48 |
54 |
55 |
56 |
57 |
58 |
64 |
65 |
66 |
67 |
68 |
69 |
79 |
80 |
81 |
82 |
88 |
89 |
91 |
92 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change log
2 |
3 | ## [Version 6.2.0](https://github.com/yannickl/DynamicButton/releases/tag/6.2.0)
4 | Released on 2019-05-27.
5 |
6 | **Swift 5 supports**
7 |
8 | ## [Version 6.1.0](https://github.com/yannickl/DynamicButton/releases/tag/6.1.0)
9 | Released on 2018-11-18.
10 |
11 | - [ADD] `location` button style
12 |
13 | ## [Version 6.0.0](https://github.com/yannickl/DynamicButton/releases/tag/6.0.0)
14 | Released on 2018-09-23.
15 |
16 | **Swift 4.2 supports**
17 |
18 | ## [Version 5.0.1](https://github.com/yannickl/DynamicButton/releases/tag/5.0.1)
19 | Released on 2018-08-21.
20 |
21 | - [FIX] style with rounded tips (#21)
22 |
23 | ## [Version 5.0.0](https://github.com/yannickl/DynamicButton/releases/tag/5.0.0)
24 | Released on 2017-09-20.
25 |
26 | **Swift 4 supports**
27 |
28 | ## [Version 4.0.0](https://github.com/yannickl/DynamicButton/releases/tag/4.0.0)
29 | Released on 2017-04-02.
30 |
31 | **Swift 3.1 supports**
32 |
33 | - [REFACTORING] The `DynamicButtonStyle` is now an enum
34 | - [ADD] Each style must now adopt the `DynamicButtonBuildableStyle` protocol
35 | - [ADD] The `DynamicButtonPathVector` struct
36 | - [ADD] `styleByName` property in the `DynamicButtonStyle`
37 | - The default buildable style access controls are now `internal`
38 |
39 | ## [Version 3.1.0](https://github.com/yannickl/DynamicButton/releases/tag/3.1.0)
40 | Released on 2016-11-05.
41 |
42 | - [ADD] `horizontalMoreOptions` button style
43 | - [ADD] `verticalMoreOptions` button style
44 |
45 | ## [Version 3.0.2](https://github.com/yannickl/DynamicButton/releases/tag/3.0.2)
46 | Released on 2016-10-10.
47 |
48 | - [FIX] `contentEdgeInsets` is now take into account.
49 |
50 | ## [Version 3.0.1](https://github.com/yannickl/DynamicButton/releases/tag/3.0.1)
51 | Released on 2016-09-21.
52 |
53 | - [ADD] New designated `init`
54 |
55 | ## [Version 3.0.0](https://github.com/yannickl/DynamicButton/releases/tag/3.0.0)
56 | Released on 2016-09-14.
57 |
58 | **Swift 3 supports**
59 |
60 | - [REFACTORING] Puts all constants in lowerCamelCase
61 | - [ADD] Swift Package Manager supports
62 |
63 | ## [Version 2.1.0](https://github.com/yannickl/DynamicButton/releases/tag/2.1.0)
64 | Released on 2016-05-03.
65 |
66 | - [ADD] `Reload` button style
67 |
68 | ## [Version 2.0.0](https://github.com/yannickl/DynamicButton/releases/tag/2.0.0)
69 | Released on 2016-04-21.
70 |
71 | - [REFACTORING] Replace the DynamicButton.Style enum with the DynamicButtonStyle class.
72 | - [ADD] Custom symbols are now allowed.
73 |
74 | ## [Version 1.8.0](https://github.com/yannickl/DynamicButton/releases/tag/1.8.0)
75 | Released on 2016-03-25.
76 |
77 | - [ADD] `Play` button style
78 | - [ADD] `Stop` button style
79 |
80 | ## [Version 1.7.1](https://github.com/yannickl/DynamicButton/releases/tag/1.7.1)
81 | Released on 2016-03-23.
82 |
83 | - [FIX] Center of gravity of caret symbols
84 |
85 | ## [Version 1.7.0](https://github.com/yannickl/DynamicButton/releases/tag/1.7.0)
86 | Released on 2016-03-22.
87 |
88 | - Swift 2.2 supports
89 |
90 | ## [Version 1.6.0](https://github.com/yannickl/DynamicButton/releases/tag/1.6.0)
91 | Released on 2016-02-10.
92 |
93 | - [ADD] `bounceButtonOnTouch` property
94 |
95 | ## [Version 1.5.0](https://github.com/yannickl/DynamicButton/releases/tag/1.5.0)
96 | Released on 2016-02-04.
97 |
98 | - [ADD] `highlightBackgroundColor` property
99 |
100 | ## [Version 1.4.0](https://github.com/yannickl/DynamicButton/releases/tag/1.4.0)
101 | Released on 2016-02-04.
102 |
103 | - [ADD] `None` button style
104 |
105 | ## [Version 1.3.0](https://github.com/yannickl/DynamicButton/releases/tag/1.3.0)
106 | Released on 2016-02-03.
107 |
108 | - [ADD] `Dot` button style
109 |
110 | ## [Version 1.2.0](https://github.com/yannickl/DynamicButton/releases/tag/1.2.0)
111 | Released on 2015-11-02.
112 |
113 | - [ADD] tvOS 9 support
114 |
115 | ## [Version 1.1.0](https://github.com/yannickl/DynamicButton/releases/tag/1.1.0)
116 | Released on 2015-10-12.
117 |
118 | - [FIX] iOS 8.x support
119 |
120 | ## [Version 1.0.0](https://github.com/yannickl/DynamicButton/releases/tag/1.0.0)
121 | Released on 2015-10-10.
122 |
123 | - Initialize with style
124 | - Available button styles:
125 | - `ArrowDown`
126 | - `ArrowLeft`
127 | - `ArrowRight`
128 | - `ArrowUp`
129 | - `CaretDown`
130 | - `CaretLeft`
131 | - `CaretRight`
132 | - `CaretUp`
133 | - `CheckMark`
134 | - `CircleClose`
135 | - `CirclePlus`
136 | - `Close`
137 | - `Download`
138 | - `FastForward`
139 | - `Hamburger`
140 | - `HorizontalLine`
141 | - `Pause`
142 | - `Plus`
143 | - `Rewind`
144 | - `VerticalLine`
145 | - `lineWidth` property
146 | - `strokeColor` property
147 | - `highlightStokeColor` property
148 | - Cocoapods support
149 | - Carthage support
150 |
--------------------------------------------------------------------------------
/Example/DynamicButtonExampleUITests/DynamicButtonExampleUITests.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import XCTest
28 |
29 | class DynamicButtonExampleUITests: XCTestCase {
30 | override func setUp() {
31 | super.setUp()
32 |
33 | // Put setup code here. This method is called before the invocation of each test method in the class.
34 |
35 | // In UI tests it is usually best to stop immediately when a failure occurs.
36 | continueAfterFailure = false
37 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
38 | XCUIApplication().launch()
39 |
40 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
41 | XCUIDevice.shared.orientation = .landscapeRight
42 | }
43 |
44 | func testStyleSelection() {
45 | let collectionViewsQuery = XCUIApplication().collectionViews
46 |
47 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Arrow Down"]/*[[".cells.buttons[\"Arrow Down\"]",".buttons[\"Arrow Down\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
48 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Arrow Left"]/*[[".cells.buttons[\"Arrow Left\"]",".buttons[\"Arrow Left\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
49 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Arrow Right"]/*[[".cells.buttons[\"Arrow Right\"]",".buttons[\"Arrow Right\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
50 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Arrow Up"]/*[[".cells.buttons[\"Arrow Up\"]",".buttons[\"Arrow Up\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
51 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Caret Down"]/*[[".cells.buttons[\"Caret Down\"]",".buttons[\"Caret Down\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
52 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Caret Left"]/*[[".cells.buttons[\"Caret Left\"]",".buttons[\"Caret Left\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
53 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Caret Right"]/*[[".cells.buttons[\"Caret Right\"]",".buttons[\"Caret Right\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
54 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Caret Up"]/*[[".cells.buttons[\"Caret Up\"]",".buttons[\"Caret Up\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
55 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Check Mark"]/*[[".cells.buttons[\"Check Mark\"]",".buttons[\"Check Mark\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
56 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Circle Close"].press(forDuration: 0.7);/*[[".cells.buttons[\"Circle Close\"]",".tap()",".press(forDuration: 0.7);",".buttons[\"Circle Close\"]"],[[[-1,3,1],[-1,0,1]],[[-1,2],[-1,1]]],[0,0]]@END_MENU_TOKEN@*/
57 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Circle Plus"].press(forDuration: 1.0);/*[[".cells.buttons[\"Circle Plus\"]",".tap()",".press(forDuration: 1.0);",".buttons[\"Circle Plus\"]"],[[[-1,3,1],[-1,0,1]],[[-1,2],[-1,1]]],[0,0]]@END_MENU_TOKEN@*/
58 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Close"].press(forDuration: 0.8);/*[[".cells.buttons[\"Close\"]",".tap()",".press(forDuration: 0.8);",".buttons[\"Close\"]"],[[[-1,3,1],[-1,0,1]],[[-1,2],[-1,1]]],[0,0]]@END_MENU_TOKEN@*/
59 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Dot"]/*[[".cells.buttons[\"Dot\"]",".buttons[\"Dot\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
60 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Download"]/*[[".cells.buttons[\"Download\"]",".buttons[\"Download\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
61 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Hamburger"]/*[[".cells.buttons[\"Hamburger\"]",".buttons[\"Hamburger\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
62 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Line - Horizontal"]/*[[".cells.buttons[\"Line - Horizontal\"]",".buttons[\"Line - Horizontal\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
63 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Line - Vertical"]/*[[".cells.buttons[\"Line - Vertical\"]",".buttons[\"Line - Vertical\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
64 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["More Options - Horizontal"]/*[[".cells.buttons[\"More Options - Horizontal\"]",".buttons[\"More Options - Horizontal\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
65 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["More Options - Vertical"]/*[[".cells.buttons[\"More Options - Vertical\"]",".buttons[\"More Options - Vertical\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
66 | collectionViewsQuery/*@START_MENU_TOKEN@*/.cells.buttons["Player - Fast Forward"]/*[[".cells.buttons[\"Player - Fast Forward\"]",".buttons[\"Player - Fast Forward\"]"],[[[-1,1],[-1,0]]],[1]]@END_MENU_TOKEN@*/.tap()
67 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Player - Pause"]/*[[".cells.buttons[\"Player - Pause\"]",".buttons[\"Player - Pause\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
68 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Player - Play"]/*[[".cells.buttons[\"Player - Play\"]",".buttons[\"Player - Play\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
69 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Player - Rewind"]/*[[".cells.buttons[\"Player - Rewind\"]",".buttons[\"Player - Rewind\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
70 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Player - Stop"]/*[[".cells.buttons[\"Player - Stop\"]",".buttons[\"Player - Stop\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
71 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Plus"]/*[[".cells.buttons[\"Plus\"]",".buttons[\"Plus\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
72 | collectionViewsQuery/*@START_MENU_TOKEN@*/.buttons["Reload"]/*[[".cells.buttons[\"Reload\"]",".buttons[\"Reload\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
73 |
74 | XCUIApplication().collectionViews.children(matching: .cell).element(boundBy: 0).otherElements.children(matching: .button).element.tap()
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Sources/DynamicButtonStyle.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | extension DynamicButton {
30 | /**
31 | A collection of predefined buton styles.
32 | */
33 | public enum Style {
34 | /// Downwards arrow: ↓
35 | case arrowDown
36 | /// Leftwards arrow: ←
37 | case arrowLeft
38 | /// Rightwards arrow: →
39 | case arrowRight
40 | /// Upwards arrow: ↑
41 | case arrowUp
42 | /// Down caret: ⌄
43 | case caretDown
44 | /// Left caret: ‹
45 | case caretLeft
46 | /// Left caret: ›
47 | case caretRight
48 | /// Up caret: ⌃
49 | case caretUp
50 | /// Check mark: ✓
51 | case checkMark
52 | /// Close symbol surrounded by a circle
53 | case circleClose
54 | /// Plus symbol surrounded by a circle
55 | case circlePlus
56 | /// Close symbol: X
57 | case close
58 | /// Dot symbol: .
59 | case dot
60 | /// Downwards triangle-headed arrow to bar: ⤓
61 | case download
62 | /// Fast forward: ≫
63 | case fastForward
64 | /// Hamburger button: ≡
65 | case hamburger
66 | /// Horizontal line: ―
67 | case horizontalLine
68 | /// Horizontal more options: …
69 | case horizontalMoreOptions
70 | /// No style
71 | case none
72 | /// Pause symbol: ‖
73 | case pause
74 | /// Play symbol: ► \{U+25B6}
75 | case play
76 | /// Plus symbol: +
77 | case plus
78 | /// Reload symbol: ↻
79 | case reload
80 | /// Rewind symbol: ≪
81 | case rewind
82 | /// Stop symbol: ◼ \{U+2588}
83 | case stop
84 | /// Vertical line: |
85 | case verticalLine
86 | /// Vertical more options: ⋮
87 | case verticalMoreOptions
88 | /// Location:
89 | case location
90 | /// Custom style with its associate buildable style
91 | case custom(DynamicButtonBuildableStyle.Type)
92 |
93 | /// Returns all the available styles
94 | public static let all: [Style] = Style.buildableByStyle.map { $0 }.sorted { $0.value.styleName < $1.value.styleName }.map { $0.0 }
95 |
96 | // MARK: - Building Button Styles
97 |
98 | public func build(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) -> DynamicButtonBuildableStyle {
99 | switch self {
100 | case .custom(let buildable):
101 | return buildable.init(center: center, size: size, offset: offset, lineWidth: lineWidth)
102 | default:
103 | let buildable = Style.buildableByStyle[self]!
104 |
105 | return buildable.init(center: center, size: size, offset: offset, lineWidth: lineWidth)
106 | }
107 | }
108 |
109 | public var description: String {
110 | let buildable = Style.buildableByStyle[self]!
111 |
112 | return buildable.styleName
113 | }
114 |
115 | // MARK: - Convenient Style Description
116 |
117 | private static let buildableByStyle: [Style: DynamicButtonBuildableStyle.Type] = [
118 | none: DynamicButtonStyleNone.self,
119 | arrowDown: DynamicButtonStyleArrowDown.self,
120 | arrowLeft: DynamicButtonStyleArrowLeft.self,
121 | arrowRight: DynamicButtonStyleArrowRight.self,
122 | arrowUp: DynamicButtonStyleArrowUp.self,
123 | caretDown: DynamicButtonStyleCaretDown.self,
124 | caretLeft: DynamicButtonStyleCaretLeft.self,
125 | caretRight: DynamicButtonStyleCaretRight.self,
126 | caretUp: DynamicButtonStyleCaretUp.self,
127 | checkMark: DynamicButtonStyleCheckMark.self,
128 | circleClose: DynamicButtonStyleCircleClose.self,
129 | circlePlus: DynamicButtonStyleCirclePlus.self,
130 | close: DynamicButtonStyleClose.self,
131 | plus: DynamicButtonStylePlus.self,
132 | dot: DynamicButtonStyleDot.self,
133 | download: DynamicButtonStyleDownload.self,
134 | reload: DynamicButtonStyleReload.self,
135 | rewind: DynamicButtonStyleRewind.self,
136 | fastForward: DynamicButtonStyleFastForward.self,
137 | play: DynamicButtonStylePlay.self,
138 | pause: DynamicButtonStylePause.self,
139 | stop: DynamicButtonStyleStop.self,
140 | hamburger: DynamicButtonStyleHamburger.self,
141 | horizontalLine: DynamicButtonStyleHorizontalLine.self,
142 | verticalLine: DynamicButtonStyleVerticalLine.self,
143 | horizontalMoreOptions: DynamicButtonStyleHorizontalMoreOptions.self,
144 | verticalMoreOptions: DynamicButtonStyleVerticalMoreOptions.self,
145 | location: DynamicButtonStyleLocation.self
146 | ]
147 |
148 | /**
149 | Returns the style with the given style name.
150 |
151 | - parameter styleName: the style name defined by the `DynamicButtonBuildableStyle` protocol.
152 | */
153 | public static let styleByName: [String: Style] = Style.buildableByStyle.reduce([:]) { (acc, entry) in
154 | var acc = acc
155 |
156 | acc[entry.1.styleName] = entry.0
157 |
158 | return acc
159 | }
160 | }
161 | }
162 |
163 | extension DynamicButton.Style: Equatable {
164 | public static func ==(lhs: DynamicButton.Style, rhs: DynamicButton.Style) -> Bool {
165 | switch (lhs, rhs) {
166 | case (.none, .none), (.arrowDown, .arrowDown), (.arrowLeft, .arrowLeft), (.arrowRight, .arrowRight), (.arrowUp, .arrowUp), (.caretDown, .caretDown), (.caretLeft, .caretLeft), (.caretRight, .caretRight), (.caretUp, .caretUp), (.checkMark, .checkMark), (.circleClose, .circleClose), (.circlePlus, .circlePlus), (.close, .close), (.plus, .plus), (.dot, .dot), (.download, .download), (.reload, .reload), (.rewind, .rewind), (.fastForward, .fastForward), (.play, .play), (.pause, .pause), (.stop, .stop), (.hamburger, .hamburger), (.horizontalLine, .horizontalLine), (.verticalLine, .verticalLine), (.horizontalMoreOptions, .horizontalMoreOptions), (.verticalMoreOptions, .verticalMoreOptions), (.location, .location):
167 | return true
168 | case (.custom(let b1), .custom(let b2)):
169 | return b1.styleName == b2.styleName
170 | default:
171 | return false
172 | }
173 | }
174 | }
175 |
176 | extension DynamicButton.Style: Hashable {
177 | public func hash(into hasher: inout Hasher) {
178 | switch self {
179 | case .none:
180 | hasher.combine(0)
181 | case .custom(let buildable):
182 | hasher.combine(buildable.styleName)
183 | default:
184 | hasher.combine(1)
185 | }
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/Example/DynamicButtonExampleTests/DynamicButtonExampleTests.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 | import XCTest
29 |
30 | class DynamicButtonExampleTests: XCTTestCaseTemplate {
31 | func testDefaultStyle() {
32 | let dynamicButton = DynamicButton()
33 |
34 | XCTAssert(dynamicButton.style == .hamburger)
35 | }
36 |
37 | func testInitWithStyle() {
38 | let hamburger = DynamicButton(style: .hamburger)
39 | XCTAssert(hamburger.style == .hamburger)
40 |
41 | let arrowDown = DynamicButton(style: .arrowDown)
42 | XCTAssert(arrowDown.style == .arrowDown)
43 |
44 | let circlePlus = DynamicButton(style: .circlePlus)
45 | XCTAssert(circlePlus.style == .circlePlus)
46 | }
47 |
48 | func testSetStyle() {
49 | let dynamicButton = DynamicButton()
50 |
51 | dynamicButton.style = .close
52 | XCTAssert(dynamicButton.style == .close)
53 |
54 | dynamicButton.style = .download
55 | XCTAssert(dynamicButton.style == .download)
56 |
57 | dynamicButton.style = .fastForward
58 | XCTAssert(dynamicButton.style == .fastForward)
59 | }
60 |
61 | func testSetStyleAnimated() {
62 | let dynamicButton = DynamicButton()
63 |
64 | dynamicButton.setStyle(.close, animated: true)
65 | XCTAssert(dynamicButton.style == .close)
66 |
67 | dynamicButton.setStyle(.download, animated: false)
68 | XCTAssert(dynamicButton.style == .download)
69 |
70 | dynamicButton.setStyle(.fastForward, animated: true)
71 | XCTAssert(dynamicButton.style == .fastForward)
72 | }
73 |
74 | func testLineWidth() {
75 | let dynamicButton = DynamicButton()
76 |
77 | XCTAssert(dynamicButton.lineWidth == 2, "Default line width should be equal to 2")
78 | XCTAssert(dynamicButton.line1Layer.lineWidth == dynamicButton.lineWidth, "Default line width should be equal to 2")
79 | XCTAssert(dynamicButton.line2Layer.lineWidth == dynamicButton.lineWidth, "Default line width should be equal to 2")
80 | XCTAssert(dynamicButton.line3Layer.lineWidth == dynamicButton.lineWidth, "Default line width should be equal to 2")
81 | XCTAssert(dynamicButton.line4Layer.lineWidth == dynamicButton.lineWidth, "Default line width should be equal to 2")
82 |
83 | dynamicButton.lineWidth = 6
84 |
85 | XCTAssertEqual(dynamicButton.lineWidth, 6, "Line width should be equal to 6")
86 | XCTAssertEqual(dynamicButton.line1Layer.lineWidth, dynamicButton.lineWidth, "Line width should be equal to 6")
87 | XCTAssertEqual(dynamicButton.line2Layer.lineWidth, dynamicButton.lineWidth, "Line width should be equal to 6")
88 | XCTAssertEqual(dynamicButton.line3Layer.lineWidth, dynamicButton.lineWidth, "Line width should be equal to 6")
89 | XCTAssertEqual(dynamicButton.line4Layer.lineWidth, dynamicButton.lineWidth, "Line width should be equal to 6")
90 | }
91 |
92 | func testStrokeColor() {
93 | let dynamicButton = DynamicButton()
94 | let blackColor = UIColor.black
95 |
96 | XCTAssertEqual(dynamicButton.strokeColor, blackColor, "Default color should be black")
97 | XCTAssertEqual(dynamicButton.line1Layer.strokeColor, blackColor.cgColor, "Default color should be black")
98 | XCTAssertEqual(dynamicButton.line2Layer.strokeColor, blackColor.cgColor, "Default color should be black")
99 | XCTAssertEqual(dynamicButton.line3Layer.strokeColor, blackColor.cgColor, "Default color should be black")
100 | XCTAssertEqual(dynamicButton.line4Layer.strokeColor, blackColor.cgColor, "Default color should be black")
101 |
102 | let redColor = UIColor.red
103 | dynamicButton.strokeColor = redColor
104 |
105 | XCTAssertEqual(dynamicButton.strokeColor, redColor, "Stroke color should be red")
106 | XCTAssertEqual(dynamicButton.line1Layer.strokeColor, redColor.cgColor, "Stroke color should be red")
107 | XCTAssertEqual(dynamicButton.line2Layer.strokeColor, redColor.cgColor, "Stroke color should be red")
108 | XCTAssertEqual(dynamicButton.line3Layer.strokeColor, redColor.cgColor, "Stroke color should be red")
109 | XCTAssertEqual(dynamicButton.line4Layer.strokeColor, redColor.cgColor, "Stroke color should be red")
110 | }
111 |
112 | func testHighlightStokeColor() {
113 | let dynamicButton = DynamicButton()
114 | let blackColor = UIColor.black
115 |
116 | dynamicButton.highlightAction()
117 |
118 | XCTAssertNil(dynamicButton.highlightStokeColor, "Default highlight stroke color should be nil")
119 | XCTAssertEqual(dynamicButton.line1Layer.strokeColor, blackColor.cgColor, "Highlight stroke color should be black like the stroke color")
120 | XCTAssertEqual(dynamicButton.line2Layer.strokeColor, blackColor.cgColor, "Highlight stroke color should be black like the stroke color")
121 | XCTAssertEqual(dynamicButton.line3Layer.strokeColor, blackColor.cgColor, "Highlight stroke color should be black like the stroke color")
122 | XCTAssertEqual(dynamicButton.line4Layer.strokeColor, blackColor.cgColor, "Highlight stroke color should be black like the stroke color")
123 |
124 | let redColor = UIColor.red
125 | dynamicButton.highlightStokeColor = redColor
126 | dynamicButton.highlightAction()
127 |
128 | XCTAssertEqual(dynamicButton.highlightStokeColor, redColor, "Default color should be red")
129 | XCTAssertEqual(dynamicButton.line1Layer.strokeColor, redColor.cgColor, "Highlight stroke color should be black like the stroke color")
130 | XCTAssertEqual(dynamicButton.line2Layer.strokeColor, redColor.cgColor, "Highlight stroke color should be black like the stroke color")
131 | XCTAssertEqual(dynamicButton.line3Layer.strokeColor, redColor.cgColor, "Highlight stroke color should be black like the stroke color")
132 | XCTAssertEqual(dynamicButton.line4Layer.strokeColor, redColor.cgColor, "Highlight stroke color should be black like the stroke color")
133 | }
134 |
135 | func testHighlightAction() {
136 | let dynamicButton = DynamicButton()
137 | dynamicButton.highlightStokeColor = UIColor.green
138 |
139 | XCTAssertEqual(dynamicButton.line1Layer.strokeColor, dynamicButton.strokeColor.cgColor, "Stroke color should be green like the stroke color")
140 |
141 | dynamicButton.highlightAction()
142 |
143 | XCTAssertEqual(dynamicButton.line1Layer.strokeColor, UIColor.green.cgColor, "Stroke color should be green like the stroke color")
144 | }
145 |
146 | func testUnHighlightAction() {
147 | let dynamicButton = DynamicButton()
148 | dynamicButton.highlightStokeColor = UIColor.orange
149 |
150 | XCTAssertEqual(dynamicButton.line1Layer.strokeColor, dynamicButton.strokeColor.cgColor, "Stroke color should be green like the stroke color")
151 |
152 | dynamicButton.highlightAction()
153 |
154 | XCTAssertEqual(dynamicButton.line1Layer.strokeColor, UIColor.orange.cgColor, "Stroke color should be orange like the stroke color")
155 |
156 | dynamicButton.unhighlightAction()
157 |
158 | XCTAssertEqual(dynamicButton.line1Layer.strokeColor, dynamicButton.strokeColor.cgColor, "Stroke color should be green like the stroke color")
159 | }
160 |
161 | func testBounceButtonOnTouchDefaultValue() {
162 | let dynamicButton = DynamicButton()
163 |
164 | XCTAssertTrue(dynamicButton.bounceButtonOnTouch)
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](http://cocoadocs.org/docsets/DynamicButton/) [](http://cocoadocs.org/docsets/DynamicButton/)
4 |
[](https://travis-ci.org/yannickl/DynamicButton) [](http://codecov.io/github/yannickl/DynamicButton?branch=master) [](https://codebeat.co/projects/github-com-yannickl-dynamicbutton)
5 |
6 | **DynamicButton** is a powerful flat design button written in Swift to display *hamburger button* like with animated transitions between style updates. It also allows you to create your own custom symbol / style buttons!
7 |
8 |
9 |
10 |
11 |
12 |
13 | Requirements • Usage • Installation • Contribution • Contact • License
14 |
15 |
16 | ## Requirements
17 |
18 | - iOS 8.0+ / tvOS 9.0+
19 | - Xcode 10.0+
20 | - Swift 5+
21 |
22 | ## Usage
23 |
24 | ### Basics
25 |
26 | Here is how to create a button and setting its style:
27 |
28 | ```swift
29 | import DynamicButton
30 |
31 | let dynamicButton = DynamicButton(style: .hamburger)
32 | // Equivalent to
33 | // let dynamicButton = DynamicButton()
34 | // dynamicButton.style = .hamburger
35 |
36 | // Animate the style update
37 | dynamicButton.setStyle(.close, animated: true)
38 | ```
39 |
40 | ### Customization
41 |
42 | Button appearance and behavior can be customized using different properties:
43 |
44 | ```swift
45 | let dynamicButton = DynamicButton()
46 | dynamicButton.lineWidth = 3
47 | dynamicButton.strokeColor = .black
48 | dynamicButton.highlightStokeColor = .gray
49 | ```
50 |
51 | ### Supported Symbol Styles
52 |
53 | Here is the symbol list (`DynamicButton.Style`) already implemented by the library:
54 |
55 | - `.arrowDown`: downwards arrow `↓`
56 | - `.arrowLeft`: leftwards arrow `←`
57 | - `.arrowRight`: rightwards arrow `→`
58 | - `.arrowUp`: upwards arrow `↑`
59 | - `.caretDown`: down caret `⌄`
60 | - `.caretLeft`: left caret `‹`
61 | - `.caretRight`: left caret `›`
62 | - `.caretUp`: up caret: `⌃`
63 | - `.checkMark`: check mark `✓`
64 | - `.circleClose`: close symbol surrounded by a circle
65 | - `.circlePlus`: plus symbol surrounded by a circle
66 | - `.close`: close symbol `X`
67 | - `.dot`: dot symbol `.`
68 | - `.download`: downwards triangle-headed arrow to bar `⤓`
69 | - `.fastForward`: fast forward `≫`
70 | - `.hamburger`: hamburger button `≡`
71 | - `.horizontalLine`: horizontal line `―`
72 | - `.horizontalMoreOptions`: horizontal more options `…`
73 | - `.none`: no style
74 | - `.pause`: pause symbol `‖`
75 | - `.play`: play symbol `►`
76 | - `.plus`: plus symbol `+`
77 | - `.stop`: stop symbol `◼`
78 | - `.reload`: reload symbol `↻`
79 | - `.rewind`: rewind `≪`
80 | - `.verticalLine`: vertical line `|`
81 | - `.verticalMoreOptions`: vertical more options `⋮`
82 | - `.location`: location symbol
83 |
84 | ### Custom symbols
85 |
86 | To create your own symbols you have to create an object (or struct) that conforms to the `DynamicButtonBuildableStyle` protocol:
87 |
88 | ```swift
89 | /// Diagonal line style: \
90 | struct MyCustomLine: DynamicButtonBuildableStyle {
91 | let pathVector: DynamicButtonPathVector
92 |
93 | init(center: CGPoint, size: CGFloat, offset: CGPoint, lineWidth: CGFloat) {
94 | let r = size / 2
95 | let c = cos(CGFloat.pi * 0.3)
96 | let s = sin(CGFloat.pi * 0.3)
97 |
98 | let p1 = CGMutablePath()
99 | p1.move(to: CGPoint(x: center.x + r * c, y: center.y + r * s))
100 | p1.addLine(to: CGPoint(x: center.x - r * c, y: center.y - r * s))
101 |
102 | pathVector = DynamicButtonPathVector(p1: p1, p2: p1, p3: p1, p4: p1)
103 | }
104 |
105 | /// "MyCustomLine" style.
106 | static var styleName: String {
107 | return "MyCustomLine"
108 | }
109 | }
110 |
111 | myButton.style = .custom(MyCustomLine.self)
112 | ```
113 |
114 | Note that a symbol can not have more than 4 paths.
115 |
116 | ### And many more...
117 |
118 | To go further, take a look at the example project.
119 |
120 | ## Installation
121 |
122 | #### CocoaPods
123 |
124 | Install CocoaPods if not already available:
125 |
126 | ``` bash
127 | $ [sudo] gem install cocoapods
128 | $ pod setup
129 | ```
130 | Go to the directory of your Xcode project, and Create and Edit your Podfile and add _DynamicButton_:
131 |
132 | ``` bash
133 | $ cd /path/to/MyProject
134 | $ touch Podfile
135 | $ edit Podfile
136 | source 'https://github.com/CocoaPods/Specs.git'
137 | platform :ios, '8.0'
138 |
139 | use_frameworks!
140 | pod 'DynamicButton', '~> 6.2.1'
141 | ```
142 |
143 | Install into your project:
144 |
145 | ``` bash
146 | $ pod install
147 | ```
148 |
149 | Open your project in Xcode from the .xcworkspace file (not the usual project file):
150 |
151 | ``` bash
152 | $ open MyProject.xcworkspace
153 | ```
154 |
155 | You can now `import DynamicButton` framework into your files.
156 |
157 | #### Carthage
158 |
159 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that automates the process of adding frameworks to your Cocoa application.
160 |
161 | You can install Carthage with [Homebrew](http://brew.sh/) using the following command:
162 |
163 | ```bash
164 | $ brew update
165 | $ brew install carthage
166 | ```
167 |
168 | To integrate `DynamicButton` into your Xcode project using Carthage, specify it in your `Cartfile` file:
169 |
170 | ```ogdl
171 | github "yannickl/DynamicButton" >= 6.2.1
172 | ```
173 |
174 | #### Swift Package Manager
175 |
176 | You can use [The Swift Package Manager](https://swift.org/package-manager) to install `DynamicButton` by adding the proper description to your `Package.swift` file:
177 |
178 | ```swift
179 | import PackageDescription
180 |
181 | let package = Package(
182 | name: "YOUR_PROJECT_NAME",
183 | dependencies: [
184 | .package(url: "https://github.com/yannickl/DynamicButton.git", from: "6.2.1")
185 | ],
186 | // ...
187 | )
188 | ```
189 |
190 | Note that the [Swift Package Manager](https://swift.org/package-manager) is still in early design and development, for more information checkout its [GitHub Page](https://github.com/apple/swift-package-manager).
191 |
192 | #### Manually
193 |
194 | [Download](https://github.com/YannickL/DynamicButton/archive/master.zip) the project and copy the `DynamicButton` folder into your project to use it in.
195 |
196 | ## Contribution
197 |
198 | Contributions are welcomed and encouraged *♡*.
199 |
200 | ## Contact
201 |
202 | Yannick Loriot
203 | - [https://21.co/yannickl/](https://21.co/yannickl/)
204 | - [https://twitter.com/yannickloriot](https://twitter.com/yannickloriot)
205 |
206 | ## License (MIT)
207 |
208 | Copyright (c) 2015-present - Yannick Loriot
209 |
210 | Permission is hereby granted, free of charge, to any person obtaining a copy
211 | of this software and associated documentation files (the "Software"), to deal
212 | in the Software without restriction, including without limitation the rights
213 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
214 | copies of the Software, and to permit persons to whom the Software is
215 | furnished to do so, subject to the following conditions:
216 |
217 | The above copyright notice and this permission notice shall be included in
218 | all copies or substantial portions of the Software.
219 |
220 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
221 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
222 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
223 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
224 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
225 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
226 | THE SOFTWARE.
227 |
--------------------------------------------------------------------------------
/Example/TVExample/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/Sources/DynamicButton.swift:
--------------------------------------------------------------------------------
1 | /*
2 | * DynamicButton
3 | *
4 | * Copyright 2015-present Yannick Loriot.
5 | * http://yannickloriot.com
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 | * THE SOFTWARE.
24 | *
25 | */
26 |
27 | import UIKit
28 |
29 | /**
30 | Flat design button compounded by several lines to create several symbols like
31 | *arrows*, *checkmark*, *hamburger button*, etc. with animated transitions
32 | between each style changes.
33 | */
34 | @IBDesignable final public class DynamicButton: UIButton {
35 | let line1Layer = CAShapeLayer()
36 | let line2Layer = CAShapeLayer()
37 | let line3Layer = CAShapeLayer()
38 | let line4Layer = CAShapeLayer()
39 |
40 | private var _style: Style = .hamburger
41 |
42 | lazy var allLayers: [CAShapeLayer] = {
43 | return [self.line1Layer, self.line2Layer, self.line3Layer, self.line4Layer]
44 | }()
45 |
46 | /**
47 | Boolean indicates whether the button can bounce when is touched.
48 |
49 | By default the value is set to true.
50 | */
51 | public var bounceButtonOnTouch: Bool = true
52 |
53 | /**
54 | Initializes and returns a dynamic button with the specified style.
55 |
56 | You have to think to define its frame because the default one is set to {0, 0, 50, 50}.
57 |
58 | - parameter style: The style of the button.
59 | - returns: An initialized view object or nil if the object couldn't be created.
60 | */
61 | required public init(style: Style) {
62 | super.init(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
63 |
64 | _style = style
65 |
66 | setup()
67 | }
68 |
69 | /**
70 | Initializes and returns a newly allocated view object with the specified frame rectangle and with the Hamburger style by default.
71 |
72 | - parameter frame: The frame rectangle for the view, measured in points. The origin of the frame is relative to the superview in which you plan to add it. This method uses the frame rectangle to set the center and bounds properties accordingly.
73 | - returns: An initialized view object or nil if the object couldn't be created.
74 | */
75 | override public init(frame: CGRect) {
76 | super.init(frame: frame)
77 |
78 | setup()
79 | }
80 |
81 | required public init?(coder aDecoder: NSCoder) {
82 | super.init(coder: aDecoder)
83 |
84 | setup()
85 | }
86 |
87 | public override func layoutSubviews() {
88 | super.layoutSubviews()
89 |
90 | let width = bounds.width - (contentEdgeInsets.left + contentEdgeInsets.right)
91 | let height = bounds.height - (contentEdgeInsets.top + contentEdgeInsets.bottom)
92 |
93 | intrinsicSize = min(width, height)
94 | intrinsicOffset = CGPoint(x: contentEdgeInsets.left + (width - intrinsicSize) / 2, y: contentEdgeInsets.top + (height - intrinsicSize) / 2)
95 |
96 | setStyle(_style, animated: false)
97 | }
98 |
99 | public override func setTitle(_ title: String?, for state: UIControl.State) {
100 | super.setTitle("", for: state)
101 | }
102 |
103 | // MARK: - Managing the Button Setup
104 |
105 | /// Intrinsic square size
106 | var intrinsicSize = CGFloat(0)
107 | /// Intrinsic square offset
108 | var intrinsicOffset = CGPoint.zero
109 |
110 | func setup() {
111 | setTitle("", for: .normal)
112 |
113 | clipsToBounds = true
114 |
115 | addTarget(self, action: #selector(highlightAction), for: .touchDown)
116 | addTarget(self, action: #selector(highlightAction), for: .touchDragEnter)
117 | addTarget(self, action: #selector(unhighlightAction), for: .touchDragExit)
118 | addTarget(self, action: #selector(unhighlightAction), for: .touchUpInside)
119 | addTarget(self, action: #selector(unhighlightAction), for: .touchCancel)
120 |
121 | for sublayer in allLayers {
122 | layer.addSublayer(sublayer)
123 | }
124 |
125 | setupLayerPaths()
126 | }
127 |
128 | func setupLayerPaths() {
129 | for sublayer in allLayers {
130 | sublayer.fillColor = UIColor.clear.cgColor
131 | sublayer.anchorPoint = CGPoint(x: 0, y: 0)
132 | sublayer.lineJoin = CAShapeLayerLineJoin.round
133 | sublayer.lineCap = CAShapeLayerLineCap.round
134 | sublayer.contentsScale = layer.contentsScale
135 | sublayer.path = UIBezierPath().cgPath
136 | sublayer.lineWidth = lineWidth
137 | sublayer.strokeColor = strokeColor.cgColor
138 | }
139 |
140 | setStyle(_style, animated: false)
141 | }
142 |
143 | // MARK: - Configuring Buttons
144 |
145 | /// The button style. The setter is equivalent to the setStyle(, animated:) method with animated value to false. Defaults to Hamburger.
146 | public var style: Style {
147 | get { return _style }
148 | set (newValue) { setStyle(newValue, animated: false) }
149 | }
150 |
151 | /**
152 | Set the style of the button and animate the change if needed.
153 |
154 | - parameter style: The style of the button.
155 | - parameter animated: If true the transition between the old style and the new one is animated.
156 | */
157 | public func setStyle(_ style: Style, animated: Bool) {
158 | _style = style
159 |
160 | let center = CGPoint(x: intrinsicOffset.x + intrinsicSize / 2, y: intrinsicOffset.y + intrinsicSize / 2)
161 | let buildable = style.build(center: center, size: intrinsicSize, offset: intrinsicOffset, lineWidth: lineWidth)
162 |
163 | applyButtonBuildable(buildable, animated: animated)
164 | }
165 |
166 | func applyButtonBuildable(_ buildable: DynamicButtonBuildableStyle, animated: Bool) {
167 | accessibilityValue = buildable.description
168 |
169 | for config in buildable.animationConfigurations(line1Layer, layer2: line2Layer, layer3: line3Layer, layer4: line4Layer) {
170 | if animated {
171 | let anim = animationWithKeyPath(config.keyPath, damping: 10)
172 | anim.fromValue = config.layer.path
173 | anim.toValue = config.newValue
174 |
175 | config.layer.add(anim, forKey: config.key)
176 | }
177 | else {
178 | config.layer.removeAllAnimations()
179 | }
180 |
181 | config.layer.path = config.newValue
182 | }
183 | }
184 |
185 | /// Specifies the line width used when stroking the button paths. Defaults to two.
186 | @IBInspectable public var lineWidth: CGFloat = 2 {
187 | didSet { setupLayerPaths() }
188 | }
189 |
190 | /// Specifies the color to fill the path's stroked outlines, or nil for no stroking. Defaults to black.
191 | @IBInspectable public var strokeColor: UIColor = .black {
192 | didSet { setupLayerPaths() }
193 | }
194 |
195 | /// Specifies the color to fill the path's stroked outlines when the button is highlighted, or nil to use the strokeColor. Defaults to nil.
196 | @IBInspectable public var highlightStokeColor: UIColor? = nil
197 |
198 | /// Specifies the color to fill the background when the button is highlighted, or nil to use the backgroundColor. Defaults to nil.
199 | @IBInspectable public var highlightBackgroundColor: UIColor? = nil
200 |
201 | // MARK: - Animating Buttons
202 |
203 | func animationWithKeyPath(_ keyPath: String, damping: CGFloat = 10, initialVelocity: CGFloat = 0, stiffness: CGFloat = 100) -> CABasicAnimation {
204 | guard #available(iOS 9, *) else {
205 | let basic = CABasicAnimation(keyPath: keyPath)
206 | basic.duration = 0.16
207 | basic.fillMode = CAMediaTimingFillMode.forwards
208 | basic.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.default)
209 |
210 | return basic
211 | }
212 |
213 | let spring = CASpringAnimation(keyPath: keyPath)
214 | spring.duration = spring.settlingDuration
215 | spring.damping = damping
216 | spring.initialVelocity = initialVelocity
217 | spring.stiffness = stiffness
218 | spring.fillMode = CAMediaTimingFillMode.forwards
219 | spring.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.default)
220 |
221 | return spring
222 | }
223 |
224 | // MARK: - Action Methods
225 |
226 | // Store the background color color variable
227 | var defaultBackgroundColor: UIColor = .clear
228 |
229 | @objc func highlightAction() {
230 | defaultBackgroundColor = backgroundColor ?? .clear
231 | backgroundColor = highlightBackgroundColor ?? defaultBackgroundColor
232 |
233 | for sublayer in allLayers {
234 | sublayer.strokeColor = (highlightStokeColor ?? strokeColor).cgColor
235 | }
236 |
237 | if bounceButtonOnTouch {
238 | let anim = animationWithKeyPath("transform.scale", damping: 20, stiffness: 1000)
239 | anim.isRemovedOnCompletion = false
240 | anim.toValue = 1.2
241 |
242 | layer.add(anim, forKey: "scaleup")
243 | }
244 | }
245 |
246 | @objc func unhighlightAction() {
247 | backgroundColor = defaultBackgroundColor
248 |
249 | for sublayer in allLayers {
250 | sublayer.strokeColor = strokeColor.cgColor
251 | }
252 |
253 | let anim = animationWithKeyPath("transform.scale", damping: 100, initialVelocity: 20)
254 | anim.isRemovedOnCompletion = false
255 | anim.toValue = 1
256 |
257 | layer.add(anim, forKey: "scaledown")
258 | }
259 | }
260 |
--------------------------------------------------------------------------------
/Example/DynamicButtonExample/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------