├── .swift-version
├── Contents
├── up.gif
├── down.gif
├── left.gif
├── main.gif
├── right.gif
├── example1.gif
├── example2.gif
├── example3.gif
├── example4.gif
├── example5.gif
└── example6.gif
├── ExpandableButtonExample
├── Assets.xcassets
│ ├── Contents.json
│ ├── edit.imageset
│ │ ├── Image.png
│ │ └── Contents.json
│ ├── like.imageset
│ │ ├── Image.png
│ │ └── Contents.json
│ ├── delete.imageset
│ │ ├── Image.png
│ │ └── Contents.json
│ ├── share.imageset
│ │ ├── Image.png
│ │ └── Contents.json
│ ├── photo.imageset
│ │ ├── 1496710261194222281.jpg
│ │ └── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Info.plist
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── AppDelegate.swift
└── ViewController.swift
├── ExpandableButtonExample.xcodeproj
├── project.xcworkspace
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── project.pbxproj
├── ExpandableButtonExampleTests
├── ExpandableButtonExampleTests.swift
└── Info.plist
├── ExpandableButton.podspec
├── ExpandableButtonExampleUITests
├── Info.plist
└── ExpandableButtonExampleUITests.swift
├── LICENSE
├── ExpandableButton
├── ActionButton.swift
├── ExpandableButtonItem.swift
├── ArrowButton.swift
└── ExpandableButtonView.swift
└── README.md
/.swift-version:
--------------------------------------------------------------------------------
1 | 4.2
2 |
--------------------------------------------------------------------------------
/Contents/up.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DimaMishchenko/ExpandableButton/HEAD/Contents/up.gif
--------------------------------------------------------------------------------
/Contents/down.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DimaMishchenko/ExpandableButton/HEAD/Contents/down.gif
--------------------------------------------------------------------------------
/Contents/left.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DimaMishchenko/ExpandableButton/HEAD/Contents/left.gif
--------------------------------------------------------------------------------
/Contents/main.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DimaMishchenko/ExpandableButton/HEAD/Contents/main.gif
--------------------------------------------------------------------------------
/Contents/right.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DimaMishchenko/ExpandableButton/HEAD/Contents/right.gif
--------------------------------------------------------------------------------
/Contents/example1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DimaMishchenko/ExpandableButton/HEAD/Contents/example1.gif
--------------------------------------------------------------------------------
/Contents/example2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DimaMishchenko/ExpandableButton/HEAD/Contents/example2.gif
--------------------------------------------------------------------------------
/Contents/example3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DimaMishchenko/ExpandableButton/HEAD/Contents/example3.gif
--------------------------------------------------------------------------------
/Contents/example4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DimaMishchenko/ExpandableButton/HEAD/Contents/example4.gif
--------------------------------------------------------------------------------
/Contents/example5.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DimaMishchenko/ExpandableButton/HEAD/Contents/example5.gif
--------------------------------------------------------------------------------
/Contents/example6.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DimaMishchenko/ExpandableButton/HEAD/Contents/example6.gif
--------------------------------------------------------------------------------
/ExpandableButtonExample/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/ExpandableButtonExample/Assets.xcassets/edit.imageset/Image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DimaMishchenko/ExpandableButton/HEAD/ExpandableButtonExample/Assets.xcassets/edit.imageset/Image.png
--------------------------------------------------------------------------------
/ExpandableButtonExample/Assets.xcassets/like.imageset/Image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DimaMishchenko/ExpandableButton/HEAD/ExpandableButtonExample/Assets.xcassets/like.imageset/Image.png
--------------------------------------------------------------------------------
/ExpandableButtonExample/Assets.xcassets/delete.imageset/Image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DimaMishchenko/ExpandableButton/HEAD/ExpandableButtonExample/Assets.xcassets/delete.imageset/Image.png
--------------------------------------------------------------------------------
/ExpandableButtonExample/Assets.xcassets/share.imageset/Image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DimaMishchenko/ExpandableButton/HEAD/ExpandableButtonExample/Assets.xcassets/share.imageset/Image.png
--------------------------------------------------------------------------------
/ExpandableButtonExample/Assets.xcassets/photo.imageset/1496710261194222281.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DimaMishchenko/ExpandableButton/HEAD/ExpandableButtonExample/Assets.xcassets/photo.imageset/1496710261194222281.jpg
--------------------------------------------------------------------------------
/ExpandableButtonExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ExpandableButtonExampleTests/ExpandableButtonExampleTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ExpandableButtonExampleTests.swift
3 | // ExpandableButtonExampleTests
4 | //
5 | // Created by Dima Mishchenko on 20.07.2018.
6 | // Copyright © 2018 Dima. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import ExpandableButtonExample
11 |
12 | class ExpandableButtonExampleTests: XCTestCase {
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/ExpandableButtonExample/Assets.xcassets/delete.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Image.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/ExpandableButtonExample/Assets.xcassets/edit.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Image.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/ExpandableButtonExample/Assets.xcassets/like.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Image.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/ExpandableButtonExample/Assets.xcassets/share.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Image.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/ExpandableButtonExample/Assets.xcassets/photo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "1496710261194222281.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/ExpandableButton.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'ExpandableButton'
3 | s.version = '1.1.0'
4 | s.summary = 'ExpandableButton'
5 |
6 | s.description = <<-DESC
7 | Customizable and easy to use expandable button in Swift.
8 | DESC
9 |
10 | s.homepage = 'https://github.com/DimaMishchenko/ExpandableButton'
11 | s.license = { :type => 'MIT', :file => 'LICENSE' }
12 | s.author = { 'Dima Mishchenko' => 'narmdv5@gmail.com' }
13 | s.source = { :git => 'https://github.com/DimaMishchenko/ExpandableButton.git', :tag => s.version.to_s }
14 |
15 | s.ios.deployment_target = '9.0'
16 | s.source_files = 'ExpandableButton/*'
17 | s.exclude_files = "ExpandableButton/*.plist"
18 | s.swift_version = '4.2'
19 |
20 | end
--------------------------------------------------------------------------------
/ExpandableButtonExampleTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ExpandableButtonExampleUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 DimaMishchenko
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, 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,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/ExpandableButtonExampleUITests/ExpandableButtonExampleUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ExpandableButtonExampleUITests.swift
3 | // ExpandableButtonExampleUITests
4 | //
5 | // Created by Dima Mishchenko on 31.07.2018.
6 | // Copyright © 2018 Dima. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class ExpandableButtonExampleUITests: XCTestCase {
12 |
13 | var app: XCUIApplication!
14 |
15 | override func setUp() {
16 | super.setUp()
17 |
18 | continueAfterFailure = false
19 | app = XCUIApplication()
20 | }
21 |
22 | func testOpenOnTap() {
23 |
24 | app.launch()
25 |
26 | let button = app.otherElements["expandableButton"].children(matching: .button).element(boundBy: 0)
27 |
28 | XCTAssert(button.exists)
29 | XCTAssert(app.buttons.count == 1)
30 |
31 | button.tap()
32 |
33 | XCTAssert(app.buttons.count == 5)
34 | }
35 |
36 | func testCloseOnTap() {
37 |
38 | app.launch()
39 |
40 | let button = app.otherElements["expandableButton"].children(matching: .button).element(boundBy: 0)
41 |
42 | XCTAssert(app.buttons.count == 1)
43 |
44 | button.tap()
45 | XCTAssert(app.buttons.count != 1)
46 |
47 |
48 | button.tap()
49 | XCTAssert(app.buttons.count == 1)
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/ExpandableButtonExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/ExpandableButtonExample/Base.lproj/LaunchScreen.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 |
--------------------------------------------------------------------------------
/ExpandableButton/ActionButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MIT License
3 | //
4 | // Copyright (c) 2018 DimaMishchenko
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 |
25 | import UIKit
26 |
27 | public class ActionButton: UIButton {
28 |
29 | public typealias ButtonActionBlock = () -> Void
30 |
31 | // MARK: - Public properties
32 |
33 | public var actionBlock: ButtonActionBlock?
34 |
35 | // MARK: - Init
36 |
37 | override public init(frame: CGRect) {
38 |
39 | super.init(frame: frame)
40 | addTarget(self, action: #selector(buttonSelected), for: .touchUpInside)
41 | }
42 |
43 | required public init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
44 |
45 | // MARK: - Actions
46 |
47 | @objc private func buttonSelected() { actionBlock?() }
48 | }
49 |
--------------------------------------------------------------------------------
/ExpandableButtonExample/Assets.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 | }
--------------------------------------------------------------------------------
/ExpandableButtonExample/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // ExpandableButtonExample
4 | //
5 | // Created by Dima Mishchenko on 20.07.2018.
6 | // Copyright © 2018 Dima. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(_ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/ExpandableButtonExample/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // ExpandableButtonExample
4 | //
5 | // Created by Dima Mishchenko on 20.07.2018.
6 | // Copyright © 2018 Dima. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController: UIViewController {
12 |
13 | var buttonView: ExpandableButtonView!
14 |
15 | override func viewDidLoad() {
16 | super.viewDidLoad()
17 | view.backgroundColor = .black
18 |
19 | let insets = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
20 |
21 | let items = [
22 | ExpandableButtonItem(
23 | image: #imageLiteral(resourceName: "delete"),
24 | highlightedImage: #imageLiteral(resourceName: "delete").alpha(0.5),
25 | imageEdgeInsets: insets,
26 | identifier: "delete",
27 | action: {_ in}
28 | ),
29 | ExpandableButtonItem(
30 | image: #imageLiteral(resourceName: "edit"),
31 | highlightedImage: #imageLiteral(resourceName: "edit").alpha(0.5),
32 | imageEdgeInsets: insets,
33 | identifier: "edit",
34 | action: {_ in}
35 | ),
36 | ExpandableButtonItem(
37 | image: #imageLiteral(resourceName: "share"),
38 | highlightedImage: #imageLiteral(resourceName: "share").alpha(0.5),
39 | imageEdgeInsets: insets,
40 | identifier: "share",
41 | action: { _ in}
42 | ),
43 | ExpandableButtonItem(
44 | image: #imageLiteral(resourceName: "like"),
45 | highlightedImage: #imageLiteral(resourceName: "like").alpha(0.5),
46 | imageEdgeInsets: insets,
47 | identifier: "like",
48 | action: { _ in}
49 | )
50 | ]
51 |
52 | buttonView = ExpandableButtonView(direction: .right, items: items)
53 | buttonView.backgroundColor = .white
54 | buttonView.arrowWidth = 2
55 | buttonView.separatorWidth = 2
56 | buttonView.separatorInset = 12
57 | buttonView.layer.cornerRadius = 30
58 | buttonView.accessibilityIdentifier = "expandableButton"
59 | view.addSubview(buttonView)
60 | setupFrame()
61 | }
62 |
63 | override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
64 | coordinator.animate(alongsideTransition: { (_) in
65 | self.setupFrame()
66 | }, completion: nil)
67 | }
68 |
69 | private func setupFrame() {
70 | buttonView.frame = CGRect(x: 24, y: UIScreen.main.bounds.size.height - 24 - 80, width: 60, height: 60)
71 | }
72 | }
73 |
74 | extension UIImage {
75 |
76 | func alpha(_ value: CGFloat) -> UIImage {
77 |
78 | UIGraphicsBeginImageContextWithOptions(size, false, scale)
79 | draw(at: CGPoint.zero, blendMode: .normal, alpha: value)
80 | let newImage = UIGraphicsGetImageFromCurrentImageContext()
81 | UIGraphicsEndImageContext()
82 | return newImage!
83 | }
84 | }
85 |
86 |
--------------------------------------------------------------------------------
/ExpandableButton/ExpandableButtonItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MIT License
3 | //
4 | // Copyright (c) 2018 DimaMishchenko
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 |
25 | import UIKit
26 |
27 | public class ExpandableButtonItem {
28 |
29 | public typealias ActionBlock = (ExpandableButtonItem) -> Void
30 |
31 | // MARK: - Properties
32 |
33 | // image
34 | public var image: UIImage?
35 | public var highlightedImage: UIImage?
36 |
37 | // title
38 | public var attributedTitle: NSAttributedString?
39 | public var highlightedAttributedTitle: NSAttributedString?
40 |
41 | // insets
42 | public var contentEdgeInsets: UIEdgeInsets = .zero
43 | public var titleEdgeInsets: UIEdgeInsets = .zero
44 | public var imageEdgeInsets: UIEdgeInsets = .zero
45 |
46 | // width
47 | public var size: CGSize?
48 |
49 | // alignment
50 | public var titleAlignment: NSTextAlignment = .center
51 |
52 | // content mode
53 | public var imageContentMode: UIView.ContentMode = .scaleAspectFit
54 |
55 | // action
56 | public var action: ActionBlock = {_ in}
57 |
58 | // identifier
59 | public var identifier: String = ""
60 |
61 | // MARK: - Init
62 |
63 | public init(image: UIImage? = nil,
64 | highlightedImage: UIImage? = nil,
65 | attributedTitle: NSAttributedString? = nil,
66 | highlightedAttributedTitle: NSAttributedString? = nil,
67 | contentEdgeInsets: UIEdgeInsets = .zero,
68 | titleEdgeInsets: UIEdgeInsets = .zero,
69 | imageEdgeInsets: UIEdgeInsets = .zero,
70 | size: CGSize? = nil,
71 | titleAlignment: NSTextAlignment = .center,
72 | imageContentMode: UIView.ContentMode = .scaleAspectFit,
73 | identifier: String = "",
74 | action: @escaping ActionBlock = {_ in}) {
75 |
76 | self.image = image
77 | self.highlightedImage = highlightedImage
78 | self.attributedTitle = attributedTitle
79 | self.highlightedAttributedTitle = highlightedAttributedTitle
80 | self.contentEdgeInsets = contentEdgeInsets
81 | self.titleEdgeInsets = titleEdgeInsets
82 | self.imageEdgeInsets = imageEdgeInsets
83 | self.size = size
84 | self.titleAlignment = titleAlignment
85 | self.imageContentMode = imageContentMode
86 | self.identifier = identifier
87 | self.action = action
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/ExpandableButtonExample/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 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/ExpandableButton/ArrowButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MIT License
3 | //
4 | // Copyright (c) 2018 DimaMishchenko
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 |
25 | import UIKit
26 |
27 | public class ArrowButton: ActionButton {
28 |
29 | private typealias TopBottomPaths = (top: CGPath, bottom: CGPath)
30 |
31 | // MARK: - Public properties
32 |
33 | public var animationDuration: TimeInterval = 0.2
34 | public var arrowInsets = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)
35 |
36 | public var arrowWidth: CGFloat = 1 {
37 | didSet {
38 | topLineLayer.lineWidth = arrowWidth
39 | bottomLineLayer.lineWidth = arrowWidth
40 | }
41 | }
42 |
43 | public var arrowColor: UIColor = .black {
44 | didSet {
45 | topLineLayer.strokeColor = arrowColor.cgColor
46 | bottomLineLayer.strokeColor = arrowColor.cgColor
47 | }
48 | }
49 |
50 | public var isArrowsHidden = false {
51 | didSet {
52 | topLineLayer.isHidden = isArrowsHidden
53 | bottomLineLayer.isHidden = isArrowsHidden
54 | }
55 | }
56 |
57 | // MARK: - Private properties
58 |
59 | private lazy var topLineLayer: CAShapeLayer = layer()
60 | private lazy var bottomLineLayer: CAShapeLayer = layer()
61 |
62 | // MARK: - Public
63 |
64 | public func showUpArrow() { animateArrow(with: upArrowPaths()) }
65 | public func showDownArrow() { animateArrow(with: downArrowPaths()) }
66 | public func showLeftArrow() { animateArrow(with: leftArrowPaths()) }
67 | public func showRightArrow() { animateArrow(with: rightArrowPaths()) }
68 |
69 | // MARK: - Private
70 |
71 | private func animateArrow(with paths: TopBottomPaths) {
72 |
73 | let keyPath = "path"
74 |
75 | let topLineAnimation = aimation(
76 | keyPath: keyPath,
77 | duration: animationDuration,
78 | fromValue: topLineLayer.path,
79 | toValue: paths.top
80 | )
81 |
82 | let bottomLineAnimation = aimation(
83 | keyPath: keyPath,
84 | duration: animationDuration,
85 | fromValue: bottomLineLayer.path,
86 | toValue: paths.bottom
87 | )
88 | topLineLayer.path = paths.top
89 | topLineLayer.add(topLineAnimation, forKey: keyPath)
90 |
91 | bottomLineLayer.path = paths.bottom
92 | bottomLineLayer.add(bottomLineAnimation, forKey: keyPath)
93 | }
94 |
95 | private func aimation(keyPath: String, duration: TimeInterval, fromValue: Any?, toValue: Any?) -> CAAnimation {
96 |
97 | let animation = CABasicAnimation(keyPath: keyPath)
98 | animation.duration = duration
99 | animation.fromValue = fromValue
100 | animation.toValue = toValue
101 |
102 | return animation
103 | }
104 |
105 | private func upArrowPaths() -> TopBottomPaths {
106 |
107 | let verticalInset = bounds.size.height / 3
108 | let horizontalInset = bounds.size.width / 2.5
109 |
110 | let firstPoint = CGPoint(
111 | x: center.x - horizontalInset + arrowInsets.left,
112 | y: center.y + verticalInset - arrowInsets.bottom
113 | )
114 | let secondPoint = CGPoint(
115 | x: center.x + horizontalInset - arrowInsets.right,
116 | y: center.y + verticalInset - arrowInsets.bottom
117 | )
118 | let centerPoint = CGPoint(
119 | x: center.x,
120 | y: center.y - verticalInset + arrowInsets.top
121 | )
122 |
123 | return arrowPaths(firstPoint: firstPoint, secondPoint: secondPoint, centerPoint: centerPoint)
124 | }
125 |
126 | private func downArrowPaths() -> TopBottomPaths {
127 |
128 | let verticalInset = bounds.size.height / 3
129 | let horizontalInset = bounds.size.width / 2.5
130 |
131 | let firstPoint = CGPoint(
132 | x: center.x - horizontalInset + arrowInsets.left,
133 | y: center.y - verticalInset + arrowInsets.top
134 | )
135 | let secondPoint = CGPoint(
136 | x: center.x + horizontalInset - arrowInsets.right,
137 | y: center.y - verticalInset + arrowInsets.top
138 | )
139 | let centerPoint = CGPoint(
140 | x: center.x,
141 | y: center.y + verticalInset - arrowInsets.bottom
142 | )
143 |
144 | return arrowPaths(firstPoint: firstPoint, secondPoint: secondPoint, centerPoint: centerPoint)
145 | }
146 |
147 | private func leftArrowPaths() -> TopBottomPaths {
148 |
149 | let verticalInset = bounds.size.height / 2.5
150 | let horizontalInset = bounds.size.width / 3
151 |
152 | let firstPoint = CGPoint(
153 | x: center.x + horizontalInset - arrowInsets.right,
154 | y: center.y - verticalInset + arrowInsets.top
155 | )
156 | let secondPoint = CGPoint(
157 | x: center.x + horizontalInset - arrowInsets.right,
158 | y: center.y + verticalInset - arrowInsets.bottom
159 | )
160 | let centerPoint = CGPoint(
161 | x: center.x - horizontalInset + arrowInsets.left,
162 | y: center.y
163 | )
164 |
165 | return arrowPaths(firstPoint: firstPoint, secondPoint: secondPoint, centerPoint: centerPoint)
166 | }
167 |
168 | private func rightArrowPaths() -> TopBottomPaths {
169 |
170 | let verticalInset = bounds.size.height / 2.5
171 | let horizontalInset = bounds.size.width / 3
172 |
173 | let firstPoint = CGPoint(
174 | x: center.x - horizontalInset + arrowInsets.left,
175 | y: center.y - verticalInset + arrowInsets.top
176 | )
177 | let secondPoint = CGPoint(
178 | x: center.x - horizontalInset + arrowInsets.left,
179 | y: center.y + verticalInset - arrowInsets.bottom
180 | )
181 | let centerPoint = CGPoint(
182 | x: center.x + horizontalInset - arrowInsets.right,
183 | y: center.y
184 | )
185 |
186 | return arrowPaths(firstPoint: firstPoint, secondPoint: secondPoint, centerPoint: centerPoint)
187 | }
188 |
189 | private func arrowPaths(firstPoint top: CGPoint, secondPoint: CGPoint, centerPoint: CGPoint) -> TopBottomPaths {
190 |
191 | let gravityCenter = CGPoint(x: (top.x + secondPoint.x + centerPoint.x) / 3,
192 | y: (top.y + secondPoint.y + centerPoint.y) / 3)
193 | let offsetFromCenter = CGPoint(x: center.x - gravityCenter.x, y: center.y - gravityCenter.y)
194 |
195 | let topLinePath = line(from: top, to: centerPoint, offset: offsetFromCenter)
196 | let bottomLinePath = line(from: secondPoint, to: centerPoint, offset: offsetFromCenter)
197 |
198 | return (topLinePath, bottomLinePath)
199 | }
200 |
201 | private func line(from startPoint: CGPoint, to endPoint: CGPoint, offset: CGPoint) -> CGPath {
202 |
203 | let path = CGMutablePath()
204 |
205 | path.move(to: CGPoint(x: offset.x + startPoint.x, y: offset.y + startPoint.y))
206 | path.addLine(to: CGPoint(x: offset.x + endPoint.x, y: offset.y + endPoint.y))
207 |
208 | return path
209 | }
210 |
211 | private func layer() -> CAShapeLayer {
212 |
213 | let layer = CAShapeLayer()
214 | layer.strokeColor = arrowColor.cgColor
215 | layer.lineWidth = arrowWidth
216 | layer.lineJoin = CAShapeLayerLineJoin.round
217 | layer.lineCap = CAShapeLayerLineCap.round
218 | self.layer.addSublayer(layer)
219 | return layer
220 | }
221 | }
222 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ExpandableButton
2 | [](https://developer.apple.com/swift/)
3 | [](https://cocoapods.org/pods/ExpandableButton)
4 | [](LICENSE)
5 |
6 | 
7 |
8 | ## Requirements
9 | - iOS 9.0+
10 |
11 | ## Installation
12 | ### [CocoaPods](http://www.cocoapods.org):
13 | - Add the following line to your [`Podfile`](http://guides.cocoapods.org/using/the-podfile.html):
14 | ``` ruby
15 | pod 'ExpandableButton'
16 |
17 | #for swift less than 4.2 use:
18 | pod 'ExpandableButton', '~> 1.0.0'
19 | ```
20 | - Add `use_frameworks!` to your [`Podfile`](http://guides.cocoapods.org/using/the-podfile.html).
21 | - Run `pod install`.
22 | - Add to files:
23 | ``` swift
24 | import ExpandableButton
25 | ```
26 |
27 | ## Usage
28 | You can init **ExpandableButton** with `frame` (default is `.zero`), `direction` (default is `.right`) and items (each item will be button). `direction` is opening direction. `items` is `[ExpandableButtonItem]` whiches contain information about future buttons.
29 | Diretions example:
30 | ``` swift
31 | let rightButton = ExpandableButtonView(frame: frame, direction: .right, items: items)
32 | let leftButton = ExpandableButtonView(frame: frame, direction: .left, items: items)
33 | let upButton = ExpandableButtonView(frame: frame, direction: .up, items: items)
34 | let downButton = ExpandableButtonView(frame: frame, direction: .down, items: items)
35 | ```
36 | 
37 | 
38 | 
39 | 
40 |
41 | Items with `image` and `action`:
42 | ``` swift
43 | // create items with images and actions
44 | let items = [
45 | ExpandableButtonItem(
46 | image: UIImage(named: "delete"),
47 | action: {_ in
48 | print("delete")
49 | }
50 | ),
51 | ExpandableButtonItem(
52 | image: UIImage(named: "edit"),
53 | action: {_ in
54 | print("edit")
55 | }
56 | ),
57 | ExpandableButtonItem(
58 | image: UIImage(named: "share"),
59 | action: { _ in
60 | print("share")
61 | }
62 | )
63 | ]
64 |
65 | // create ExpandableButton
66 | let buttonView = ExpandableButtonView(items: items)
67 | buttonView.frame = CGRect(x: 0, y: 0, width: 60, height: 60)
68 | buttonView.backgroundColor = .white
69 | view.addSubview(buttonView)
70 | ```
71 | 
72 |
73 | With `image`, `highlightedImage`, `imageEdgeInsets`:
74 | ``` swift
75 | let insets = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
76 |
77 | // create items with image, highlighted image, image insets.
78 | let items = [
79 | ExpandableButtonItem(
80 | image: UIImage(named: "delete"),
81 | highlightedImage: UIImage(named: "highlightedDelete"),
82 | imageEdgeInsets: insets,
83 | action: {_ in
84 | print("delete")
85 | }
86 | )
87 | ...
88 | ]
89 | ```
90 | 
91 |
92 | `arrowWidth`, `separatorWidth` and `cornerRadius`:
93 | ``` swift
94 | buttonView.arrowWidth = 2
95 | buttonView.separatorWidth = 2
96 | buttonView.layer.cornerRadius = 30
97 | ```
98 | 
99 |
100 | Custom icons for `open` and `close` actions, `closeOpenImagesInsets`:
101 | ``` swift
102 | // custom open and close images
103 | buttonView.openImage = UIImage(named: "open")
104 | buttonView.closeImage = UIImage(named: "close")
105 | buttonView.closeOpenImagesInsets = insets
106 | ```
107 | 
108 |
109 | With `attributedTitle`, `highlightedAttributedTitle` and custom item `size`:
110 | ``` swift
111 | // with attributed string, highlighted attributed string, custom size.
112 | let items = [
113 | ExpandableButtonItem(
114 | attributedTitle: NSAttributedString(
115 | string: "Attributed Text",
116 | attributes: [.foregroundColor: UIColor.red]
117 | ),
118 | highlightedAttributedTitle: NSAttributedString(
119 | string: "Attributed Text",
120 | attributes: [.foregroundColor: UIColor.green]
121 | ),
122 | size: CGSize(width: 160, height: 60)
123 | )
124 | ]
125 | ```
126 | 
127 |
128 | With `attributedTitle` under `image` (using `contentEdgeInsets`, `titleEdgeInsets`, `imageEdgeInsets`, `titleAlignment`, `imageContentMode`, `size`):
129 | ``` swift
130 | let attributedTitle = NSAttributedString(
131 | string: "Share",
132 | attributes: [.foregroundColor: UIColor.black,
133 | .font: UIFont.systemFont(ofSize: 12)]
134 | )
135 |
136 | let highlightedAttributedTitle = NSAttributedString(
137 | string: "Share",
138 | attributes: [.foregroundColor: UIColor.lightGray,
139 | .font: UIFont.systemFont(ofSize: 12)]
140 | )
141 |
142 | let items = [
143 | ExpandableButtonItem(
144 | image: UIImage(named: "share"),
145 | highlightedImage: UIImage(named: "haglightedShare"),
146 | attributedTitle: attributedTitle,
147 | highlightedAttributedTitle: highlightedAttributedTitle,
148 | contentEdgeInsets: UIEdgeInsets(top: 6, left: 6, bottom: 6, right: 6),
149 | titleEdgeInsets: UIEdgeInsets(top: 24, left: -200, bottom: 0, right: 0),
150 | imageEdgeInsets: UIEdgeInsets(top: 6, left: 0, bottom: 24, right: 0),
151 | size: CGSize(width: 80, height: 60),
152 | titleAlignment: .center,
153 | imageContentMode: .scaleAspectFit
154 | )
155 | ]
156 | ```
157 | 
158 |
159 | You can also `open()` and `close()`:
160 | ``` swift
161 | let buttonView = ExpandableButtonView(items: items)
162 |
163 | buttonView.open()
164 | buttonView.close()
165 | ```
166 |
167 | ## All options
168 | ### [`ExpandableButtonView`](ExpandableButton/ExpandableButtonView.swift)
169 | | Name | Type | Default value | Description |
170 | | :--- | :--- | :--- | :--- |
171 | | `direction` | `Direction` | `.right` | Opening direction. Could be `.left`, `.right`, `.up`, `.down`. Set only on `init(frame:direction:items:)`.
172 | | `state` | `State` | `.closed` | Current state. Could be `.opened`, `.closed` or `.animating`. |
173 | | `animationDuration` | `TimeInterval` | `0.2` | Opening, closing and arrow animation duration. |
174 | | `closeOnAction` | `Bool` | `false` | If `true` call `close()` after any item action. |
175 | | `isHapticFeedback` | `Bool` | `true` | Turn on haptic feedback (Taptic engine) |
176 | | `arrowInsets` | `UIEdgeInsets` | `top: 12 left: 12 bottom: 12 right: 12` | Arrow insets. |
177 | | `arrowWidth` | `CGFloat` | `1` | Arrow line width. |
178 | | `arrowColor` | `UIColor` | `UIColor.black` | Arrow color. |
179 | | `closeOpenImagesInsets` | `UIEdgeInsets` | `.zero` | Insets for custom close and open images. |
180 | | `closeImage` | `UIImage?` | `nil` | Custom close image. |
181 | | `openImage` | `UIImage?` | `nil` | Custom open image. |
182 | | `isSeparatorHidden` | `Bool` | `false` | If `true` hide separator view. |
183 | | `separatorColor` | `UIColor` | `UIColor.black` | Separator color. |
184 | | `separatorInset` | `CGFloat` | `8` | Separator inset from top, bottom for `.left`, `.right` directions and from left, right for `up`, `down`. |
185 | | `separatorWidth` | `CGFloat` | `1` | Separator view width. |
186 |
187 | ### [`ExpandableButtonItem`](ExpandableButton/ExpandableButtonItem.swift)
188 | | Name | Type | Default value | Description |
189 | | :--- | :--- | :--- | :--- |
190 | | `image` | `UIImage?` | `nil` | Image for `.normal` state. |
191 | | `highlightedImage` | `UIImage?` | `nil` | Image for `.highlighted` state. |
192 | | `attributedTitle` | `NSAttributedString?` | `nil` | Attributed string for `.normal` state. |
193 | | `highlightedAttributedTitle` | `NSAttributedString?` | `nil` | Attributed string for `.highlighted` state. |
194 | | `contentEdgeInsets` | `UIEdgeInsets` | `.zero` | `contentEdgeInsets` for `UIButton` |
195 | | `titleEdgeInsets` | `UIEdgeInsets` | `.zero` | `titleEdgeInsets` for `UIButton`. |
196 | | `imageEdgeInsets` | `UIEdgeInsets` | `.zero` | `imageEdgeInsets` for `UIButton`. |
197 | | `size` | `CGSize?` | `nil` | `UIButton` size for current item. If `nil` will be equal to arrow button size. |
198 | | `titleAlignment` | `NSTextAlignment` | `.center` | `titleAlignment` for `titleLabel` in `UIButton`. |
199 | | `imageContentMode` | `UIViewContentMode` | `.scaleAspectFit` | `imageContentMode` for `imageView` in `UIButton`.|
200 | | `action` | `(ExpandableButtonItem) -> Void` | `{_ in}` | Action closure. Calls on `.touchUpInside` |
201 | | `identifier` | `String` | `""` | Identifier for `ExpandableButtonItem`. |
202 |
203 |
204 | You can also use [`ArrowButton`](ExpandableButton/ArrowButton.swift) (button which can drow left, right, up and down arrows using core graphics, just call `showLeftArrow()`, `showRightArrow()`, `showUpArrow()` or `showDownArrow()`) and [`ActionButton`](ExpandableButton/ActionButton.swift) (simple `UIButton` but with `actionBlock` propertie which calls on `.touchUpInside`) in your projects.
205 |
206 | ## License
207 | **ExpandableButton** is under MIT license. See the [LICENSE](LICENSE) file for more info.
208 |
--------------------------------------------------------------------------------
/ExpandableButton/ExpandableButtonView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MIT License
3 | //
4 | // Copyright (c) 2018 DimaMishchenko
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 |
25 | import UIKit
26 |
27 | public class ExpandableButtonView: UIView {
28 |
29 | public enum Direction {
30 | case up
31 | case down
32 | case left
33 | case right
34 | }
35 |
36 | public enum State {
37 | case opened
38 | case closed
39 | case animating
40 | }
41 |
42 | // MARK: - UI properties
43 |
44 | private var arrowButton: ArrowButton!
45 | private var separatorView: UIView!
46 | private var itemsButtons: [ActionButton] = []
47 |
48 | // MARK: - Public properties
49 |
50 | public private(set) var direction: Direction
51 |
52 | public private(set) var state: State = .closed
53 |
54 | public var animationDuration: TimeInterval = 0.2 {
55 | didSet { arrowButton.animationDuration = animationDuration }
56 | }
57 |
58 | public var closeOnAction: Bool = false
59 | public var isHapticFeedback = true
60 |
61 | // arrow
62 |
63 | public var arrowInsets: UIEdgeInsets {
64 | get { return arrowButton.arrowInsets }
65 | set { arrowButton.arrowInsets = newValue }
66 | }
67 |
68 | public var arrowWidth: CGFloat {
69 | get { return arrowButton.arrowWidth }
70 | set { arrowButton.arrowWidth = newValue }
71 | }
72 |
73 | public var arrowColor: UIColor {
74 | get { return arrowButton.arrowColor }
75 | set { arrowButton.arrowColor = newValue }
76 | }
77 |
78 | public var closeOpenImagesInsets: UIEdgeInsets {
79 | get { return arrowButton.imageEdgeInsets }
80 | set { arrowButton.imageEdgeInsets = newValue }
81 | }
82 | public var closeImage: UIImage?
83 | public var openImage: UIImage?
84 |
85 | // separator
86 |
87 | public var isSeparatorHidden: Bool = false { didSet { separatorView.isHidden = isSeparatorHidden } }
88 | public var separatorColor: UIColor = .black { didSet { separatorView.backgroundColor = separatorColor } }
89 | public var separatorInset: CGFloat = 8 { didSet { reloadSeparatorFrame() } }
90 | public var separatorWidth: CGFloat = 1 { didSet { reloadSeparatorFrame() } }
91 |
92 | // MARK: - Private properties
93 |
94 | private var firstLayout = true
95 |
96 | // MARK: - Init
97 |
98 | public init(frame: CGRect = .zero, direction: Direction = .right, items: [ExpandableButtonItem]) {
99 |
100 | self.direction = direction
101 | super.init(frame: frame)
102 | setupUI()
103 | setupButtons(with: items)
104 | }
105 |
106 | required public init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
107 |
108 | // MARK: - Overrides
109 |
110 | public override var frame: CGRect { didSet { setupFrames() } }
111 | public override var backgroundColor: UIColor? { didSet { arrowButton.backgroundColor = backgroundColor } }
112 |
113 | override public func layoutSubviews() {
114 |
115 | super.layoutSubviews()
116 |
117 | if firstLayout {
118 |
119 | setupFrames()
120 | showCloseArrow()
121 |
122 | firstLayout = false
123 | }
124 | }
125 |
126 | // MARK: - Public
127 |
128 | public func open() {
129 |
130 | guard state == .closed else { return }
131 |
132 | state = .animating
133 | showOpenArrow()
134 |
135 | itemsButtons.forEach { $0.isHidden = false }
136 |
137 | UIView.animate(withDuration: animationDuration, animations: {
138 | self.itemsButtons.forEach { $0.alpha = 0; $0.alpha = 1 }
139 | self.open(with: self.direction)
140 | }) {
141 | if $0 {
142 | self.state = .opened
143 | self.impactHapticFeedback()
144 | }
145 | }
146 | }
147 |
148 | public func close() {
149 |
150 | guard state == .opened else { return }
151 |
152 | state = .animating
153 |
154 | // because of CABasicAnimation in ArrowButton.
155 | if direction == .up || direction == .left { self.close(with: self.direction) }
156 |
157 | showCloseArrow()
158 |
159 | // because of CABasicAnimation in ArrowButton.
160 | if direction == .up || direction == .left { self.open(with: self.direction) }
161 |
162 | UIView.animate(withDuration: animationDuration, animations: {
163 | self.itemsButtons.forEach { $0.alpha = 1; $0.alpha = 0 }
164 | self.close(with: self.direction)
165 | }) {
166 | if $0 {
167 | self.itemsButtons.forEach { $0.isHidden = true }
168 | self.state = .closed
169 | self.impactHapticFeedback()
170 | }
171 | }
172 |
173 | }
174 |
175 | // MARK: - Private
176 |
177 | private func setupUI() {
178 |
179 | clipsToBounds = true
180 |
181 | // arrow button
182 |
183 | arrowButton = ArrowButton()
184 | arrowButton.actionBlock = { [weak self] in
185 |
186 | guard let state = self?.state else { return }
187 |
188 | switch state {
189 | case .opened: self?.close()
190 | case .closed: self?.open()
191 | case .animating: break
192 | }
193 | }
194 | arrowButton.backgroundColor = backgroundColor
195 | addSubview(arrowButton)
196 |
197 | // separator
198 |
199 | separatorView = UIView()
200 | separatorView.backgroundColor = separatorColor
201 | insertSubview(separatorView, belowSubview: arrowButton)
202 | }
203 |
204 | private func setupButtons(with items: [ExpandableButtonItem]) {
205 |
206 | items.forEach { item in
207 |
208 | let button = ActionButton()
209 | insertSubview(button, belowSubview: arrowButton)
210 |
211 | button.setImage(item.image, for: .normal)
212 | button.setImage(item.highlightedImage, for: .highlighted)
213 |
214 | button.setAttributedTitle(item.attributedTitle, for: .normal)
215 | button.setAttributedTitle(item.highlightedAttributedTitle, for: .highlighted)
216 |
217 | button.contentEdgeInsets = item.contentEdgeInsets
218 | button.titleEdgeInsets = item.titleEdgeInsets
219 | button.imageEdgeInsets = item.imageEdgeInsets
220 |
221 | button.titleLabel?.textAlignment = item.titleAlignment
222 | button.imageView?.contentMode = item.imageContentMode
223 |
224 | if let size = item.size { button.frame = CGRect(origin: .zero, size: size) }
225 |
226 | button.actionBlock = { [weak self] in
227 | item.action(item)
228 | if let closeOnAction = self?.closeOnAction, closeOnAction { self?.close() }
229 | }
230 |
231 | itemsButtons.append(button)
232 | }
233 | }
234 |
235 | // MARK: - Layout
236 |
237 | private func setupFrames() {
238 |
239 | guard arrowButton != nil, separatorView != nil else { return }
240 |
241 | // arrow button
242 |
243 | arrowButton.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
244 |
245 | // separator
246 |
247 | reloadSeparatorFrame()
248 |
249 | // items buttons
250 |
251 | setupItemButtonsFrames()
252 | itemsButtons.forEach { $0.isHidden = true }
253 |
254 | // self
255 |
256 | switch state {
257 | case .closed:
258 | showCloseArrow()
259 | case .opened:
260 | open(with: direction)
261 | showOpenArrow()
262 | default: break
263 | }
264 | }
265 |
266 | private func reloadSeparatorFrame() {
267 |
268 | switch direction {
269 | case .up:
270 | let y = itemsButtons.reduce(0, { $0 + $1.frame.height })
271 | let width = frame.width - separatorInset * 2
272 | separatorView.frame = CGRect(x: separatorInset, y: y, width: width, height: separatorWidth)
273 | case .down:
274 | let width = frame.width - separatorInset * 2
275 | separatorView.frame = CGRect(x: separatorInset, y: frame.height, width: width, height: separatorWidth)
276 | case .left:
277 | let x = itemsButtons.reduce(0, { $0 + $1.frame.width })
278 | let height = frame.height - separatorInset * 2
279 | separatorView.frame = CGRect(x: x, y: separatorInset, width: separatorWidth, height: height)
280 | case .right:
281 | let height = frame.height - separatorInset * 2
282 | separatorView.frame = CGRect(x: frame.width, y: separatorInset, width: separatorWidth, height: height)
283 | }
284 | }
285 |
286 | private func setupItemButtonsFrames() {
287 |
288 | var previousButton: UIButton?
289 |
290 | itemsButtons.forEach {
291 |
292 | let width = $0.frame.width == 0 ? arrowButton.frame.width : $0.frame.width
293 | let height = $0.frame.height == 0 ? arrowButton.frame.height : $0.frame.height
294 |
295 | var x: CGFloat = 0
296 | var y: CGFloat = 0
297 |
298 | switch direction {
299 | case .up:
300 | y = previousButton != nil ?
301 | previousButton!.frame.origin.y + previousButton!.frame.height :
302 | arrowButton.frame.origin.y
303 | case .down:
304 | y = previousButton != nil ?
305 | previousButton!.frame.origin.y + previousButton!.frame.height :
306 | arrowButton.frame.origin.y + arrowButton.frame.height
307 | case .left:
308 | x = previousButton != nil ?
309 | previousButton!.frame.origin.x + previousButton!.frame.width :
310 | arrowButton.frame.origin.x
311 | case .right:
312 | x = previousButton != nil ?
313 | previousButton!.frame.origin.x + previousButton!.frame.width :
314 | arrowButton.frame.origin.x + arrowButton.frame.width
315 | }
316 | $0.frame = CGRect(x: x, y: y, width: width, height: height)
317 |
318 | previousButton = $0
319 | }
320 | }
321 |
322 | // MARK: - Arrows
323 |
324 | private func showOpenArrow() {
325 |
326 | arrowButton.setImage(openImage, for: .normal)
327 | arrowButton.isArrowsHidden = openImage != nil
328 |
329 | if openImage == nil {
330 |
331 | if closeImage == nil {
332 |
333 | switch direction {
334 | case .up: arrowButton.showDownArrow()
335 | case .down: arrowButton.showUpArrow()
336 | case .left: arrowButton.showRightArrow()
337 | case .right: arrowButton.showLeftArrow()
338 | }
339 | }
340 | }
341 | }
342 |
343 | private func showCloseArrow() {
344 |
345 | arrowButton.setImage(closeImage, for: .normal)
346 | arrowButton.isArrowsHidden = closeImage != nil
347 |
348 | if closeImage == nil {
349 |
350 | switch direction {
351 | case .up: arrowButton.showUpArrow()
352 | case .down: arrowButton.showDownArrow()
353 | case .left: arrowButton.showLeftArrow()
354 | case .right: arrowButton.showRightArrow()
355 | }
356 | }
357 | }
358 |
359 | // MARK: - Haptic Feedback
360 |
361 | private func impactHapticFeedback() {
362 |
363 | if #available(iOS 10.0, *), isHapticFeedback {
364 |
365 | let generator = UISelectionFeedbackGenerator()
366 | generator.selectionChanged()
367 | }
368 | }
369 |
370 | // MARK: - Open close
371 |
372 | private func open(with direction: Direction) {
373 |
374 | switch direction {
375 | case .up:
376 | let itemsHeight = itemsButtons.reduce(0, { $0 + $1.frame.height })
377 | let y = frame.origin.y - itemsHeight
378 | let height = frame.size.height + itemsHeight
379 |
380 | super.frame = CGRect(x: frame.origin.x, y: y, width: frame.size.width, height: height)
381 |
382 | let arrY = super.frame.height - arrowButton.frame.height
383 | arrowButton.frame = CGRect(x: 0, y: arrY, width: arrowButton.frame.width, height: arrowButton.frame.height)
384 | case .down:
385 | let height = frame.size.height + itemsButtons.reduce(0, { $0 + $1.frame.height })
386 | super.frame = CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.size.width, height: height)
387 | case .left:
388 | let itemsWidth = itemsButtons.reduce(0, { $0 + $1.frame.width })
389 | let x = frame.origin.x - itemsWidth
390 | let width = frame.size.width + itemsWidth
391 | super.frame = CGRect(x: x, y: frame.origin.y, width: width, height: frame.size.height)
392 |
393 | let arrX = super.frame.width - arrowButton.frame.width
394 | arrowButton.frame = CGRect(x: arrX, y: 0, width: arrowButton.frame.width, height: arrowButton.frame.height)
395 | case .right:
396 | let width = frame.size.width + itemsButtons.reduce(0, { $0 + $1.frame.width })
397 | super.frame = CGRect(x: frame.origin.x, y: frame.origin.y, width: width, height: frame.size.height)
398 | }
399 | }
400 |
401 | private func close(with direction: Direction) {
402 |
403 | switch direction {
404 | case .up:
405 | let itemsHeight = itemsButtons.reduce(0, { $0 + $1.frame.height })
406 | let y = frame.origin.y + itemsHeight
407 | let height = frame.size.height - itemsHeight
408 | super.frame = CGRect(x: frame.origin.x, y: y, width: frame.size.width, height: height)
409 |
410 | arrowButton.frame = CGRect(x: 0, y: 0, width: arrowButton.frame.width, height: arrowButton.frame.height)
411 | case .down:
412 | let height = frame.size.height - itemsButtons.reduce(0, { $0 + $1.frame.height })
413 | super.frame = CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.size.width, height: height)
414 | case .left:
415 | let itemsWidth = itemsButtons.reduce(0, { $0 + $1.frame.width })
416 | let x = frame.origin.x + itemsWidth
417 | let width = frame.size.width - itemsWidth
418 | super.frame = CGRect(x: x, y: frame.origin.y, width: width, height: frame.size.height)
419 |
420 | arrowButton.frame = CGRect(x: 0, y: 0, width: arrowButton.frame.width, height: arrowButton.frame.height)
421 | case .right:
422 | let width = frame.size.width - itemsButtons.reduce(0, { $0 + $1.frame.width })
423 | super.frame = CGRect(x: frame.origin.x, y: frame.origin.y, width: width, height: frame.size.height)
424 | }
425 | }
426 | }
427 |
--------------------------------------------------------------------------------
/ExpandableButtonExample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 963F695321021DA9007C5ACA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 963F695221021DA9007C5ACA /* AppDelegate.swift */; };
11 | 963F695521021DA9007C5ACA /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 963F695421021DA9007C5ACA /* ViewController.swift */; };
12 | 963F695821021DAA007C5ACA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 963F695621021DAA007C5ACA /* Main.storyboard */; };
13 | 963F695A21021DB6007C5ACA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 963F695921021DB6007C5ACA /* Assets.xcassets */; };
14 | 963F695D21021DB6007C5ACA /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 963F695B21021DB6007C5ACA /* LaunchScreen.storyboard */; };
15 | 963F696821021DB6007C5ACA /* ExpandableButtonExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 963F696721021DB6007C5ACA /* ExpandableButtonExampleTests.swift */; };
16 | 963F697421021E3D007C5ACA /* ExpandableButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 963F697321021E3D007C5ACA /* ExpandableButtonView.swift */; };
17 | 963F6976210224CB007C5ACA /* ActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 963F6975210224CB007C5ACA /* ActionButton.swift */; };
18 | 965C34E121109E400081C003 /* ExpandableButtonExampleUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 965C34E021109E400081C003 /* ExpandableButtonExampleUITests.swift */; };
19 | 96BA3FFF21062BCC004F4EB0 /* ArrowButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BA3FFE21062BCC004F4EB0 /* ArrowButton.swift */; };
20 | 96FF4BC8210238EA00938FE3 /* ExpandableButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96FF4BC7210238EA00938FE3 /* ExpandableButtonItem.swift */; };
21 | /* End PBXBuildFile section */
22 |
23 | /* Begin PBXContainerItemProxy section */
24 | 963F696421021DB6007C5ACA /* PBXContainerItemProxy */ = {
25 | isa = PBXContainerItemProxy;
26 | containerPortal = 963F694721021DA9007C5ACA /* Project object */;
27 | proxyType = 1;
28 | remoteGlobalIDString = 963F694E21021DA9007C5ACA;
29 | remoteInfo = ExpandableButtonExample;
30 | };
31 | 965C34E321109E400081C003 /* PBXContainerItemProxy */ = {
32 | isa = PBXContainerItemProxy;
33 | containerPortal = 963F694721021DA9007C5ACA /* Project object */;
34 | proxyType = 1;
35 | remoteGlobalIDString = 963F694E21021DA9007C5ACA;
36 | remoteInfo = ExpandableButtonExample;
37 | };
38 | /* End PBXContainerItemProxy section */
39 |
40 | /* Begin PBXFileReference section */
41 | 963F694F21021DA9007C5ACA /* ExpandableButtonExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExpandableButtonExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
42 | 963F695221021DA9007C5ACA /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
43 | 963F695421021DA9007C5ACA /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
44 | 963F695721021DAA007C5ACA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
45 | 963F695921021DB6007C5ACA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
46 | 963F695C21021DB6007C5ACA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
47 | 963F695E21021DB6007C5ACA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
48 | 963F696321021DB6007C5ACA /* ExpandableButtonExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExpandableButtonExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
49 | 963F696721021DB6007C5ACA /* ExpandableButtonExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpandableButtonExampleTests.swift; sourceTree = ""; };
50 | 963F696921021DB6007C5ACA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
51 | 963F697321021E3D007C5ACA /* ExpandableButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpandableButtonView.swift; sourceTree = ""; };
52 | 963F6975210224CB007C5ACA /* ActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionButton.swift; sourceTree = ""; };
53 | 965C34DE21109E400081C003 /* ExpandableButtonExampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExpandableButtonExampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
54 | 965C34E021109E400081C003 /* ExpandableButtonExampleUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpandableButtonExampleUITests.swift; sourceTree = ""; };
55 | 965C34E221109E400081C003 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
56 | 96BA3FFE21062BCC004F4EB0 /* ArrowButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrowButton.swift; sourceTree = ""; };
57 | 96FF4BC7210238EA00938FE3 /* ExpandableButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpandableButtonItem.swift; sourceTree = ""; };
58 | /* End PBXFileReference section */
59 |
60 | /* Begin PBXFrameworksBuildPhase section */
61 | 963F694C21021DA9007C5ACA /* Frameworks */ = {
62 | isa = PBXFrameworksBuildPhase;
63 | buildActionMask = 2147483647;
64 | files = (
65 | );
66 | runOnlyForDeploymentPostprocessing = 0;
67 | };
68 | 963F696021021DB6007C5ACA /* Frameworks */ = {
69 | isa = PBXFrameworksBuildPhase;
70 | buildActionMask = 2147483647;
71 | files = (
72 | );
73 | runOnlyForDeploymentPostprocessing = 0;
74 | };
75 | 965C34DB21109E400081C003 /* Frameworks */ = {
76 | isa = PBXFrameworksBuildPhase;
77 | buildActionMask = 2147483647;
78 | files = (
79 | );
80 | runOnlyForDeploymentPostprocessing = 0;
81 | };
82 | /* End PBXFrameworksBuildPhase section */
83 |
84 | /* Begin PBXGroup section */
85 | 963F694621021DA9007C5ACA = {
86 | isa = PBXGroup;
87 | children = (
88 | 963F697221021E08007C5ACA /* ExpandableButton */,
89 | 963F695121021DA9007C5ACA /* ExpandableButtonExample */,
90 | 963F696621021DB6007C5ACA /* ExpandableButtonExampleTests */,
91 | 965C34DF21109E400081C003 /* ExpandableButtonExampleUITests */,
92 | 963F695021021DA9007C5ACA /* Products */,
93 | );
94 | sourceTree = "";
95 | };
96 | 963F695021021DA9007C5ACA /* Products */ = {
97 | isa = PBXGroup;
98 | children = (
99 | 963F694F21021DA9007C5ACA /* ExpandableButtonExample.app */,
100 | 963F696321021DB6007C5ACA /* ExpandableButtonExampleTests.xctest */,
101 | 965C34DE21109E400081C003 /* ExpandableButtonExampleUITests.xctest */,
102 | );
103 | name = Products;
104 | sourceTree = "";
105 | };
106 | 963F695121021DA9007C5ACA /* ExpandableButtonExample */ = {
107 | isa = PBXGroup;
108 | children = (
109 | 963F695221021DA9007C5ACA /* AppDelegate.swift */,
110 | 963F695421021DA9007C5ACA /* ViewController.swift */,
111 | 963F695621021DAA007C5ACA /* Main.storyboard */,
112 | 963F695921021DB6007C5ACA /* Assets.xcassets */,
113 | 963F695B21021DB6007C5ACA /* LaunchScreen.storyboard */,
114 | 963F695E21021DB6007C5ACA /* Info.plist */,
115 | );
116 | path = ExpandableButtonExample;
117 | sourceTree = "";
118 | };
119 | 963F696621021DB6007C5ACA /* ExpandableButtonExampleTests */ = {
120 | isa = PBXGroup;
121 | children = (
122 | 963F696721021DB6007C5ACA /* ExpandableButtonExampleTests.swift */,
123 | 963F696921021DB6007C5ACA /* Info.plist */,
124 | );
125 | path = ExpandableButtonExampleTests;
126 | sourceTree = "";
127 | };
128 | 963F697221021E08007C5ACA /* ExpandableButton */ = {
129 | isa = PBXGroup;
130 | children = (
131 | 963F697321021E3D007C5ACA /* ExpandableButtonView.swift */,
132 | 96FF4BC7210238EA00938FE3 /* ExpandableButtonItem.swift */,
133 | 96BA3FFE21062BCC004F4EB0 /* ArrowButton.swift */,
134 | 963F6975210224CB007C5ACA /* ActionButton.swift */,
135 | );
136 | path = ExpandableButton;
137 | sourceTree = "";
138 | };
139 | 965C34DF21109E400081C003 /* ExpandableButtonExampleUITests */ = {
140 | isa = PBXGroup;
141 | children = (
142 | 965C34E021109E400081C003 /* ExpandableButtonExampleUITests.swift */,
143 | 965C34E221109E400081C003 /* Info.plist */,
144 | );
145 | path = ExpandableButtonExampleUITests;
146 | sourceTree = "";
147 | };
148 | /* End PBXGroup section */
149 |
150 | /* Begin PBXNativeTarget section */
151 | 963F694E21021DA9007C5ACA /* ExpandableButtonExample */ = {
152 | isa = PBXNativeTarget;
153 | buildConfigurationList = 963F696C21021DB6007C5ACA /* Build configuration list for PBXNativeTarget "ExpandableButtonExample" */;
154 | buildPhases = (
155 | 963F694B21021DA9007C5ACA /* Sources */,
156 | 963F694C21021DA9007C5ACA /* Frameworks */,
157 | 963F694D21021DA9007C5ACA /* Resources */,
158 | );
159 | buildRules = (
160 | );
161 | dependencies = (
162 | );
163 | name = ExpandableButtonExample;
164 | productName = ExpandableButtonExample;
165 | productReference = 963F694F21021DA9007C5ACA /* ExpandableButtonExample.app */;
166 | productType = "com.apple.product-type.application";
167 | };
168 | 963F696221021DB6007C5ACA /* ExpandableButtonExampleTests */ = {
169 | isa = PBXNativeTarget;
170 | buildConfigurationList = 963F696F21021DB6007C5ACA /* Build configuration list for PBXNativeTarget "ExpandableButtonExampleTests" */;
171 | buildPhases = (
172 | 963F695F21021DB6007C5ACA /* Sources */,
173 | 963F696021021DB6007C5ACA /* Frameworks */,
174 | 963F696121021DB6007C5ACA /* Resources */,
175 | );
176 | buildRules = (
177 | );
178 | dependencies = (
179 | 963F696521021DB6007C5ACA /* PBXTargetDependency */,
180 | );
181 | name = ExpandableButtonExampleTests;
182 | productName = ExpandableButtonExampleTests;
183 | productReference = 963F696321021DB6007C5ACA /* ExpandableButtonExampleTests.xctest */;
184 | productType = "com.apple.product-type.bundle.unit-test";
185 | };
186 | 965C34DD21109E400081C003 /* ExpandableButtonExampleUITests */ = {
187 | isa = PBXNativeTarget;
188 | buildConfigurationList = 965C34E721109E400081C003 /* Build configuration list for PBXNativeTarget "ExpandableButtonExampleUITests" */;
189 | buildPhases = (
190 | 965C34DA21109E400081C003 /* Sources */,
191 | 965C34DB21109E400081C003 /* Frameworks */,
192 | 965C34DC21109E400081C003 /* Resources */,
193 | );
194 | buildRules = (
195 | );
196 | dependencies = (
197 | 965C34E421109E400081C003 /* PBXTargetDependency */,
198 | );
199 | name = ExpandableButtonExampleUITests;
200 | productName = ExpandableButtonExampleUITests;
201 | productReference = 965C34DE21109E400081C003 /* ExpandableButtonExampleUITests.xctest */;
202 | productType = "com.apple.product-type.bundle.ui-testing";
203 | };
204 | /* End PBXNativeTarget section */
205 |
206 | /* Begin PBXProject section */
207 | 963F694721021DA9007C5ACA /* Project object */ = {
208 | isa = PBXProject;
209 | attributes = {
210 | LastSwiftUpdateCheck = 0940;
211 | LastUpgradeCheck = 0940;
212 | ORGANIZATIONNAME = Dima;
213 | TargetAttributes = {
214 | 963F694E21021DA9007C5ACA = {
215 | CreatedOnToolsVersion = 9.4.1;
216 | };
217 | 963F696221021DB6007C5ACA = {
218 | CreatedOnToolsVersion = 9.4.1;
219 | TestTargetID = 963F694E21021DA9007C5ACA;
220 | };
221 | 965C34DD21109E400081C003 = {
222 | CreatedOnToolsVersion = 9.4.1;
223 | TestTargetID = 963F694E21021DA9007C5ACA;
224 | };
225 | };
226 | };
227 | buildConfigurationList = 963F694A21021DA9007C5ACA /* Build configuration list for PBXProject "ExpandableButtonExample" */;
228 | compatibilityVersion = "Xcode 9.3";
229 | developmentRegion = en;
230 | hasScannedForEncodings = 0;
231 | knownRegions = (
232 | en,
233 | Base,
234 | );
235 | mainGroup = 963F694621021DA9007C5ACA;
236 | productRefGroup = 963F695021021DA9007C5ACA /* Products */;
237 | projectDirPath = "";
238 | projectRoot = "";
239 | targets = (
240 | 963F694E21021DA9007C5ACA /* ExpandableButtonExample */,
241 | 963F696221021DB6007C5ACA /* ExpandableButtonExampleTests */,
242 | 965C34DD21109E400081C003 /* ExpandableButtonExampleUITests */,
243 | );
244 | };
245 | /* End PBXProject section */
246 |
247 | /* Begin PBXResourcesBuildPhase section */
248 | 963F694D21021DA9007C5ACA /* Resources */ = {
249 | isa = PBXResourcesBuildPhase;
250 | buildActionMask = 2147483647;
251 | files = (
252 | 963F695D21021DB6007C5ACA /* LaunchScreen.storyboard in Resources */,
253 | 963F695A21021DB6007C5ACA /* Assets.xcassets in Resources */,
254 | 963F695821021DAA007C5ACA /* Main.storyboard in Resources */,
255 | );
256 | runOnlyForDeploymentPostprocessing = 0;
257 | };
258 | 963F696121021DB6007C5ACA /* Resources */ = {
259 | isa = PBXResourcesBuildPhase;
260 | buildActionMask = 2147483647;
261 | files = (
262 | );
263 | runOnlyForDeploymentPostprocessing = 0;
264 | };
265 | 965C34DC21109E400081C003 /* Resources */ = {
266 | isa = PBXResourcesBuildPhase;
267 | buildActionMask = 2147483647;
268 | files = (
269 | );
270 | runOnlyForDeploymentPostprocessing = 0;
271 | };
272 | /* End PBXResourcesBuildPhase section */
273 |
274 | /* Begin PBXSourcesBuildPhase section */
275 | 963F694B21021DA9007C5ACA /* Sources */ = {
276 | isa = PBXSourcesBuildPhase;
277 | buildActionMask = 2147483647;
278 | files = (
279 | 963F697421021E3D007C5ACA /* ExpandableButtonView.swift in Sources */,
280 | 96BA3FFF21062BCC004F4EB0 /* ArrowButton.swift in Sources */,
281 | 963F695521021DA9007C5ACA /* ViewController.swift in Sources */,
282 | 963F695321021DA9007C5ACA /* AppDelegate.swift in Sources */,
283 | 963F6976210224CB007C5ACA /* ActionButton.swift in Sources */,
284 | 96FF4BC8210238EA00938FE3 /* ExpandableButtonItem.swift in Sources */,
285 | );
286 | runOnlyForDeploymentPostprocessing = 0;
287 | };
288 | 963F695F21021DB6007C5ACA /* Sources */ = {
289 | isa = PBXSourcesBuildPhase;
290 | buildActionMask = 2147483647;
291 | files = (
292 | 963F696821021DB6007C5ACA /* ExpandableButtonExampleTests.swift in Sources */,
293 | );
294 | runOnlyForDeploymentPostprocessing = 0;
295 | };
296 | 965C34DA21109E400081C003 /* Sources */ = {
297 | isa = PBXSourcesBuildPhase;
298 | buildActionMask = 2147483647;
299 | files = (
300 | 965C34E121109E400081C003 /* ExpandableButtonExampleUITests.swift in Sources */,
301 | );
302 | runOnlyForDeploymentPostprocessing = 0;
303 | };
304 | /* End PBXSourcesBuildPhase section */
305 |
306 | /* Begin PBXTargetDependency section */
307 | 963F696521021DB6007C5ACA /* PBXTargetDependency */ = {
308 | isa = PBXTargetDependency;
309 | target = 963F694E21021DA9007C5ACA /* ExpandableButtonExample */;
310 | targetProxy = 963F696421021DB6007C5ACA /* PBXContainerItemProxy */;
311 | };
312 | 965C34E421109E400081C003 /* PBXTargetDependency */ = {
313 | isa = PBXTargetDependency;
314 | target = 963F694E21021DA9007C5ACA /* ExpandableButtonExample */;
315 | targetProxy = 965C34E321109E400081C003 /* PBXContainerItemProxy */;
316 | };
317 | /* End PBXTargetDependency section */
318 |
319 | /* Begin PBXVariantGroup section */
320 | 963F695621021DAA007C5ACA /* Main.storyboard */ = {
321 | isa = PBXVariantGroup;
322 | children = (
323 | 963F695721021DAA007C5ACA /* Base */,
324 | );
325 | name = Main.storyboard;
326 | sourceTree = "";
327 | };
328 | 963F695B21021DB6007C5ACA /* LaunchScreen.storyboard */ = {
329 | isa = PBXVariantGroup;
330 | children = (
331 | 963F695C21021DB6007C5ACA /* Base */,
332 | );
333 | name = LaunchScreen.storyboard;
334 | sourceTree = "";
335 | };
336 | /* End PBXVariantGroup section */
337 |
338 | /* Begin XCBuildConfiguration section */
339 | 963F696A21021DB6007C5ACA /* Debug */ = {
340 | isa = XCBuildConfiguration;
341 | buildSettings = {
342 | ALWAYS_SEARCH_USER_PATHS = NO;
343 | CLANG_ANALYZER_NONNULL = YES;
344 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
345 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
346 | CLANG_CXX_LIBRARY = "libc++";
347 | CLANG_ENABLE_MODULES = YES;
348 | CLANG_ENABLE_OBJC_ARC = YES;
349 | CLANG_ENABLE_OBJC_WEAK = YES;
350 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
351 | CLANG_WARN_BOOL_CONVERSION = YES;
352 | CLANG_WARN_COMMA = YES;
353 | CLANG_WARN_CONSTANT_CONVERSION = YES;
354 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
355 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
356 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
357 | CLANG_WARN_EMPTY_BODY = YES;
358 | CLANG_WARN_ENUM_CONVERSION = YES;
359 | CLANG_WARN_INFINITE_RECURSION = YES;
360 | CLANG_WARN_INT_CONVERSION = YES;
361 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
362 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
363 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
364 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
365 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
366 | CLANG_WARN_STRICT_PROTOTYPES = YES;
367 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
368 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
369 | CLANG_WARN_UNREACHABLE_CODE = YES;
370 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
371 | CODE_SIGN_IDENTITY = "iPhone Developer";
372 | COPY_PHASE_STRIP = NO;
373 | DEBUG_INFORMATION_FORMAT = dwarf;
374 | ENABLE_STRICT_OBJC_MSGSEND = YES;
375 | ENABLE_TESTABILITY = YES;
376 | GCC_C_LANGUAGE_STANDARD = gnu11;
377 | GCC_DYNAMIC_NO_PIC = NO;
378 | GCC_NO_COMMON_BLOCKS = YES;
379 | GCC_OPTIMIZATION_LEVEL = 0;
380 | GCC_PREPROCESSOR_DEFINITIONS = (
381 | "DEBUG=1",
382 | "$(inherited)",
383 | );
384 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
385 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
386 | GCC_WARN_UNDECLARED_SELECTOR = YES;
387 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
388 | GCC_WARN_UNUSED_FUNCTION = YES;
389 | GCC_WARN_UNUSED_VARIABLE = YES;
390 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
391 | MTL_ENABLE_DEBUG_INFO = YES;
392 | ONLY_ACTIVE_ARCH = YES;
393 | SDKROOT = iphoneos;
394 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
395 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
396 | SWIFT_VERSION = 4.2;
397 | };
398 | name = Debug;
399 | };
400 | 963F696B21021DB6007C5ACA /* Release */ = {
401 | isa = XCBuildConfiguration;
402 | buildSettings = {
403 | ALWAYS_SEARCH_USER_PATHS = NO;
404 | CLANG_ANALYZER_NONNULL = YES;
405 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
406 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
407 | CLANG_CXX_LIBRARY = "libc++";
408 | CLANG_ENABLE_MODULES = YES;
409 | CLANG_ENABLE_OBJC_ARC = YES;
410 | CLANG_ENABLE_OBJC_WEAK = YES;
411 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
412 | CLANG_WARN_BOOL_CONVERSION = YES;
413 | CLANG_WARN_COMMA = YES;
414 | CLANG_WARN_CONSTANT_CONVERSION = YES;
415 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
416 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
417 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
418 | CLANG_WARN_EMPTY_BODY = YES;
419 | CLANG_WARN_ENUM_CONVERSION = YES;
420 | CLANG_WARN_INFINITE_RECURSION = YES;
421 | CLANG_WARN_INT_CONVERSION = YES;
422 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
423 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
424 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
425 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
426 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
427 | CLANG_WARN_STRICT_PROTOTYPES = YES;
428 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
429 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
430 | CLANG_WARN_UNREACHABLE_CODE = YES;
431 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
432 | CODE_SIGN_IDENTITY = "iPhone Developer";
433 | COPY_PHASE_STRIP = NO;
434 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
435 | ENABLE_NS_ASSERTIONS = NO;
436 | ENABLE_STRICT_OBJC_MSGSEND = YES;
437 | GCC_C_LANGUAGE_STANDARD = gnu11;
438 | GCC_NO_COMMON_BLOCKS = YES;
439 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
440 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
441 | GCC_WARN_UNDECLARED_SELECTOR = YES;
442 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
443 | GCC_WARN_UNUSED_FUNCTION = YES;
444 | GCC_WARN_UNUSED_VARIABLE = YES;
445 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
446 | MTL_ENABLE_DEBUG_INFO = NO;
447 | SDKROOT = iphoneos;
448 | SWIFT_COMPILATION_MODE = wholemodule;
449 | SWIFT_OPTIMIZATION_LEVEL = "-O";
450 | SWIFT_VERSION = 4.2;
451 | VALIDATE_PRODUCT = YES;
452 | };
453 | name = Release;
454 | };
455 | 963F696D21021DB6007C5ACA /* Debug */ = {
456 | isa = XCBuildConfiguration;
457 | buildSettings = {
458 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
459 | CODE_SIGN_STYLE = Automatic;
460 | DEVELOPMENT_TEAM = 77X75EH6F4;
461 | INFOPLIST_FILE = ExpandableButtonExample/Info.plist;
462 | LD_RUNPATH_SEARCH_PATHS = (
463 | "$(inherited)",
464 | "@executable_path/Frameworks",
465 | );
466 | PRODUCT_BUNDLE_IDENTIFIER = com.dima.mishchenko.ExpandableButtonExample;
467 | PRODUCT_NAME = "$(TARGET_NAME)";
468 | SWIFT_VERSION = 4.2;
469 | TARGETED_DEVICE_FAMILY = "1,2";
470 | };
471 | name = Debug;
472 | };
473 | 963F696E21021DB6007C5ACA /* Release */ = {
474 | isa = XCBuildConfiguration;
475 | buildSettings = {
476 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
477 | CODE_SIGN_STYLE = Automatic;
478 | DEVELOPMENT_TEAM = 77X75EH6F4;
479 | INFOPLIST_FILE = ExpandableButtonExample/Info.plist;
480 | LD_RUNPATH_SEARCH_PATHS = (
481 | "$(inherited)",
482 | "@executable_path/Frameworks",
483 | );
484 | PRODUCT_BUNDLE_IDENTIFIER = com.dima.mishchenko.ExpandableButtonExample;
485 | PRODUCT_NAME = "$(TARGET_NAME)";
486 | SWIFT_VERSION = 4.2;
487 | TARGETED_DEVICE_FAMILY = "1,2";
488 | };
489 | name = Release;
490 | };
491 | 963F697021021DB6007C5ACA /* Debug */ = {
492 | isa = XCBuildConfiguration;
493 | buildSettings = {
494 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
495 | BUNDLE_LOADER = "$(TEST_HOST)";
496 | CODE_SIGN_STYLE = Automatic;
497 | DEVELOPMENT_TEAM = 77X75EH6F4;
498 | INFOPLIST_FILE = ExpandableButtonExampleTests/Info.plist;
499 | LD_RUNPATH_SEARCH_PATHS = (
500 | "$(inherited)",
501 | "@executable_path/Frameworks",
502 | "@loader_path/Frameworks",
503 | );
504 | PRODUCT_BUNDLE_IDENTIFIER = com.dima.mishchenko.ExpandableButtonExampleTests;
505 | PRODUCT_NAME = "$(TARGET_NAME)";
506 | SWIFT_VERSION = 4.2;
507 | TARGETED_DEVICE_FAMILY = "1,2";
508 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ExpandableButtonExample.app/ExpandableButtonExample";
509 | };
510 | name = Debug;
511 | };
512 | 963F697121021DB6007C5ACA /* Release */ = {
513 | isa = XCBuildConfiguration;
514 | buildSettings = {
515 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
516 | BUNDLE_LOADER = "$(TEST_HOST)";
517 | CODE_SIGN_STYLE = Automatic;
518 | DEVELOPMENT_TEAM = 77X75EH6F4;
519 | INFOPLIST_FILE = ExpandableButtonExampleTests/Info.plist;
520 | LD_RUNPATH_SEARCH_PATHS = (
521 | "$(inherited)",
522 | "@executable_path/Frameworks",
523 | "@loader_path/Frameworks",
524 | );
525 | PRODUCT_BUNDLE_IDENTIFIER = com.dima.mishchenko.ExpandableButtonExampleTests;
526 | PRODUCT_NAME = "$(TARGET_NAME)";
527 | SWIFT_VERSION = 4.2;
528 | TARGETED_DEVICE_FAMILY = "1,2";
529 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ExpandableButtonExample.app/ExpandableButtonExample";
530 | };
531 | name = Release;
532 | };
533 | 965C34E521109E400081C003 /* Debug */ = {
534 | isa = XCBuildConfiguration;
535 | buildSettings = {
536 | CODE_SIGN_STYLE = Automatic;
537 | DEVELOPMENT_TEAM = 77X75EH6F4;
538 | INFOPLIST_FILE = ExpandableButtonExampleUITests/Info.plist;
539 | IPHONEOS_DEPLOYMENT_TARGET = 11.4;
540 | LD_RUNPATH_SEARCH_PATHS = (
541 | "$(inherited)",
542 | "@executable_path/Frameworks",
543 | "@loader_path/Frameworks",
544 | );
545 | PRODUCT_BUNDLE_IDENTIFIER = com.dima.mishchenko.ExpandableButtonExampleUITests;
546 | PRODUCT_NAME = "$(TARGET_NAME)";
547 | SWIFT_VERSION = 4.2;
548 | TARGETED_DEVICE_FAMILY = "1,2";
549 | TEST_TARGET_NAME = ExpandableButtonExample;
550 | };
551 | name = Debug;
552 | };
553 | 965C34E621109E400081C003 /* Release */ = {
554 | isa = XCBuildConfiguration;
555 | buildSettings = {
556 | CODE_SIGN_STYLE = Automatic;
557 | DEVELOPMENT_TEAM = 77X75EH6F4;
558 | INFOPLIST_FILE = ExpandableButtonExampleUITests/Info.plist;
559 | IPHONEOS_DEPLOYMENT_TARGET = 11.4;
560 | LD_RUNPATH_SEARCH_PATHS = (
561 | "$(inherited)",
562 | "@executable_path/Frameworks",
563 | "@loader_path/Frameworks",
564 | );
565 | PRODUCT_BUNDLE_IDENTIFIER = com.dima.mishchenko.ExpandableButtonExampleUITests;
566 | PRODUCT_NAME = "$(TARGET_NAME)";
567 | SWIFT_VERSION = 4.2;
568 | TARGETED_DEVICE_FAMILY = "1,2";
569 | TEST_TARGET_NAME = ExpandableButtonExample;
570 | };
571 | name = Release;
572 | };
573 | /* End XCBuildConfiguration section */
574 |
575 | /* Begin XCConfigurationList section */
576 | 963F694A21021DA9007C5ACA /* Build configuration list for PBXProject "ExpandableButtonExample" */ = {
577 | isa = XCConfigurationList;
578 | buildConfigurations = (
579 | 963F696A21021DB6007C5ACA /* Debug */,
580 | 963F696B21021DB6007C5ACA /* Release */,
581 | );
582 | defaultConfigurationIsVisible = 0;
583 | defaultConfigurationName = Release;
584 | };
585 | 963F696C21021DB6007C5ACA /* Build configuration list for PBXNativeTarget "ExpandableButtonExample" */ = {
586 | isa = XCConfigurationList;
587 | buildConfigurations = (
588 | 963F696D21021DB6007C5ACA /* Debug */,
589 | 963F696E21021DB6007C5ACA /* Release */,
590 | );
591 | defaultConfigurationIsVisible = 0;
592 | defaultConfigurationName = Release;
593 | };
594 | 963F696F21021DB6007C5ACA /* Build configuration list for PBXNativeTarget "ExpandableButtonExampleTests" */ = {
595 | isa = XCConfigurationList;
596 | buildConfigurations = (
597 | 963F697021021DB6007C5ACA /* Debug */,
598 | 963F697121021DB6007C5ACA /* Release */,
599 | );
600 | defaultConfigurationIsVisible = 0;
601 | defaultConfigurationName = Release;
602 | };
603 | 965C34E721109E400081C003 /* Build configuration list for PBXNativeTarget "ExpandableButtonExampleUITests" */ = {
604 | isa = XCConfigurationList;
605 | buildConfigurations = (
606 | 965C34E521109E400081C003 /* Debug */,
607 | 965C34E621109E400081C003 /* Release */,
608 | );
609 | defaultConfigurationIsVisible = 0;
610 | defaultConfigurationName = Release;
611 | };
612 | /* End XCConfigurationList section */
613 | };
614 | rootObject = 963F694721021DA9007C5ACA /* Project object */;
615 | }
616 |
--------------------------------------------------------------------------------