├── preview1.gif
├── preview2.gif
├── TabBarInteraction
├── Assets.xcassets
│ ├── Contents.json
│ ├── first.imageset
│ │ ├── first.pdf
│ │ └── Contents.json
│ ├── second.imageset
│ │ ├── second.pdf
│ │ └── Contents.json
│ ├── clock.imageset
│ │ ├── clock@2x.png
│ │ ├── clock@3x.png
│ │ └── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── SecondViewController.swift
├── Info.plist
├── AppDelegate.swift
├── FirstViewController.swift
├── TabbarInteractable.swift
├── ClockView.swift
└── WheelView.swift
├── README.md
├── TabBarInteraction.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── project.pbxproj
├── LICENSE
└── .gitignore
/preview1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/potato04/TabBarInteraction/HEAD/preview1.gif
--------------------------------------------------------------------------------
/preview2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/potato04/TabBarInteraction/HEAD/preview2.gif
--------------------------------------------------------------------------------
/TabBarInteraction/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/TabBarInteraction/Assets.xcassets/first.imageset/first.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/potato04/TabBarInteraction/HEAD/TabBarInteraction/Assets.xcassets/first.imageset/first.pdf
--------------------------------------------------------------------------------
/TabBarInteraction/Assets.xcassets/second.imageset/second.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/potato04/TabBarInteraction/HEAD/TabBarInteraction/Assets.xcassets/second.imageset/second.pdf
--------------------------------------------------------------------------------
/TabBarInteraction/Assets.xcassets/clock.imageset/clock@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/potato04/TabBarInteraction/HEAD/TabBarInteraction/Assets.xcassets/clock.imageset/clock@2x.png
--------------------------------------------------------------------------------
/TabBarInteraction/Assets.xcassets/clock.imageset/clock@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/potato04/TabBarInteraction/HEAD/TabBarInteraction/Assets.xcassets/clock.imageset/clock@3x.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 让 TabBar 根据用户交互而动画的一种尝试
2 |
3 | 实现详见博客: [Tab Bar 的图标原来还可以这样玩](https://juejin.im/post/5c9fa72d6fb9a05e6835c8a6)
4 |
5 |
6 |

7 |
8 |
--------------------------------------------------------------------------------
/TabBarInteraction.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/TabBarInteraction/Assets.xcassets/first.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "first.pdf"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/TabBarInteraction/Assets.xcassets/second.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "second.pdf"
6 | }
7 | ],
8 | "info" : {
9 | "version" : 1,
10 | "author" : "xcode"
11 | }
12 | }
--------------------------------------------------------------------------------
/TabBarInteraction.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/TabBarInteraction/Assets.xcassets/clock.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "clock@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "clock@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Potato04
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 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | # Swift Package Manager
36 | #
37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38 | # Packages/
39 | # Package.pins
40 | # Package.resolved
41 | .build/
42 |
43 | # CocoaPods
44 | #
45 | # We recommend against adding the Pods directory to your .gitignore. However
46 | # you should judge for yourself, the pros and cons are mentioned at:
47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
48 | #
49 | # Pods/
50 |
51 | # Carthage
52 | #
53 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
54 | # Carthage/Checkouts
55 |
56 | Carthage/Build
57 |
58 | # fastlane
59 | #
60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
61 | # screenshots whenever they are needed.
62 | # For more information about the recommended setup visit:
63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
64 |
65 | fastlane/report.xml
66 | fastlane/Preview.html
67 | fastlane/screenshots/**/*.png
68 | fastlane/test_output
69 |
--------------------------------------------------------------------------------
/TabBarInteraction/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 |
--------------------------------------------------------------------------------
/TabBarInteraction/SecondViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SecondViewController.swift
3 | // TabBarInteraction
4 | //
5 | // Created by potato04 on 2019/3/27.
6 | // Copyright © 2019 potato04. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SecondViewController: UIViewController, TabbarInteractable {
12 |
13 | private var clockView: ClockIconView!
14 | @IBOutlet weak var datePicker: UIDatePicker!
15 | let calendar = Calendar.current
16 | lazy var defaultDate: Date = {
17 | var component = DateComponents()
18 | component.hour = 9
19 | component.minute = 0
20 | return calendar.date(from: component)!
21 | }()
22 |
23 | override func viewDidLoad() {
24 | super.viewDidLoad()
25 | // Do any additional setup after loading the view.
26 |
27 | clockView = ClockIconView(frame: CGRect.zero)
28 | clockView.lineWidth = 2
29 | clockView.tintColor = UIColor.white
30 | clockView.layer.cornerRadius = 12.5
31 |
32 | datePicker.addTarget(self, action: #selector(datePickerValueChanged(_:)), for: .valueChanged)
33 | datePicker.date = defaultDate
34 |
35 | replaceSwappableImageViews(with: clockView, and: CGSize(width: 25, height: 25))
36 | }
37 |
38 |
39 | override func viewDidAppear(_ animated: Bool) {
40 | clockView.backgroundColor = view.tintColor
41 | }
42 | override func viewDidDisappear(_ animated: Bool) {
43 | clockView.backgroundColor = UIColor.gray
44 | }
45 |
46 | @objc func datePickerValueChanged(_ sender: UIDatePicker) {
47 | let selectedDate = sender.date
48 | let hour = calendar.component(.hour, from: selectedDate)
49 | let minute = calendar.component(.minute, from: selectedDate)
50 | clockView.hour = hour
51 | clockView.minute = minute
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/TabBarInteraction/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 | UIStatusBarTintParameters
32 |
33 | UINavigationBar
34 |
35 | Style
36 | UIBarStyleDefault
37 | Translucent
38 |
39 |
40 |
41 | UISupportedInterfaceOrientations
42 |
43 | UIInterfaceOrientationPortrait
44 | UIInterfaceOrientationLandscapeLeft
45 | UIInterfaceOrientationLandscapeRight
46 |
47 | UISupportedInterfaceOrientations~ipad
48 |
49 | UIInterfaceOrientationPortrait
50 | UIInterfaceOrientationPortraitUpsideDown
51 | UIInterfaceOrientationLandscapeLeft
52 | UIInterfaceOrientationLandscapeRight
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/TabBarInteraction/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 | }
--------------------------------------------------------------------------------
/TabBarInteraction/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // TabBarInteraction
4 | //
5 | // Created by potato04 on 2019/3/27.
6 | // Copyright © 2019 potato04. 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 |
--------------------------------------------------------------------------------
/TabBarInteraction/FirstViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FirstViewController.swift
3 | // TabBarInteraction
4 | //
5 | // Created by potato04 on 2019/3/27.
6 | // Copyright © 2019 potato04. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class FirstViewController: UIViewController, TabbarInteractable {
12 |
13 | private var wheelView: WheelView!
14 |
15 | @IBOutlet weak var tableView: UITableView!
16 | let data = Array(1...100).map { "\($0)" }
17 |
18 | override func viewDidLoad() {
19 | super.viewDidLoad()
20 | // Do any additional setup after loading the view.
21 |
22 | tableView.delegate = self
23 | tableView.dataSource = self
24 | tableView.register(UITableViewCell.self, forCellReuseIdentifier: "DefaultCell")
25 | tableView.rowHeight = 44
26 |
27 | wheelView = WheelView(frame: CGRect.zero)
28 | wheelView.font = UIFont.systemFont(ofSize: 15, weight: .bold)
29 | wheelView.borderWidth = 1
30 | wheelView.backgroundColor = UIColor.white
31 | wheelView.contents = data
32 |
33 | replaceSwappableImageViews(with: wheelView, and: CGSize(width: 25, height: 25))
34 | }
35 |
36 | override func viewDidAppear(_ animated: Bool) {
37 | scrollViewDidScroll(self.tableView)
38 | wheelView.tintColor = view.tintColor
39 | }
40 | override func viewDidDisappear(_ animated: Bool) {
41 | wheelView.tintColor = UIColor.gray
42 | }
43 | }
44 |
45 | // MARK: UITableViewDataSource
46 | extension FirstViewController: UITableViewDataSource {
47 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
48 | return data.count
49 | }
50 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
51 | let cell = tableView.dequeueReusableCell(withIdentifier: "DefaultCell")
52 | cell?.textLabel?.text = data[indexPath.row]
53 | return cell!
54 | }
55 | }
56 |
57 | // MARK: UITableViewDelegate
58 | extension FirstViewController: UITableViewDelegate {
59 | func scrollViewDidScroll(_ scrollView: UIScrollView) {
60 | // progress 怎么计算取决于你需求,这里的是为了把`tableview`当前可见区域最底部的2个数字给显示出来。
61 | let progress = Float((scrollView.contentOffset.y + tableView.bounds.height - tableView.rowHeight) / scrollView.contentSize.height)
62 | wheelView.progress = progress
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/TabBarInteraction/TabbarInteractable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TabbarInteractable.swift
3 | // TabBarInteraction
4 | //
5 | // Created by potato04 on 2019/4/3.
6 | // Copyright © 2019 potato04. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | protocol TabbarInteractable {
12 | func replaceSwappableImageViews(with newView: UIView, and viewSize: CGSize)
13 | }
14 |
15 | extension TabbarInteractable where Self: UIViewController {
16 | func replaceSwappableImageViews(with newView: UIView, and viewSize: CGSize) {
17 | newView.isUserInteractionEnabled = false
18 |
19 | //find ViewController's nearest UITabBarController
20 | var parentController = self.parent
21 | while !(parentController is UITabBarController) {
22 | if parentController?.parent == nil { return }
23 | parentController = parentController?.parent
24 | }
25 | let tabbarController = parentController as! UITabBarController
26 |
27 | // using bfs to find ViewController's index in UITabBarController
28 | var controllerIndex = -1
29 | findControllerIndexLoop: for (i, child) in tabbarController.children.enumerated() {
30 | var queue = [child]
31 | while queue.count > 0 {
32 | let controller = queue.removeFirst()
33 | if controller is Self {
34 | controllerIndex = i
35 | break findControllerIndexLoop
36 | }
37 | for vc in controller.children {
38 | queue.append(vc)
39 | }
40 | }
41 | }
42 | if controllerIndex == -1 { return }
43 |
44 | var tabBarButtons = tabbarController.tabBar.subviews.filter({
45 | type(of: $0).description().isEqual("UITabBarButton")
46 | })
47 |
48 | guard !tabBarButtons.isEmpty else { return }
49 |
50 | let tabBarButton = tabBarButtons[controllerIndex]
51 | let swappableImageViews = tabBarButton.subviews.filter({
52 | type(of: $0).description().isEqual("UITabBarSwappableImageView")
53 | })
54 | guard !swappableImageViews.isEmpty else { return }
55 | let swappableImageView = swappableImageViews.first!
56 | tabBarButton.addSubview(newView)
57 | swappableImageView.isHidden = true
58 | NSLayoutConstraint.activate([
59 | newView.widthAnchor.constraint(equalToConstant: viewSize.width),
60 | newView.heightAnchor.constraint(equalToConstant: viewSize.height),
61 | newView.centerXAnchor.constraint(equalTo: swappableImageView.centerXAnchor),
62 | newView.centerYAnchor.constraint(equalTo: swappableImageView.centerYAnchor)
63 | ])
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/TabBarInteraction/ClockView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ClockView.swift
3 | // TabBarInteraction
4 | //
5 | // Created by potato04 on 2019/3/31.
6 | // Copyright © 2019 potato04. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ClockIconView: UIView {
12 |
13 | var hour: Int = 9 {
14 | didSet {
15 | hour = min(24, max(0, hour))
16 | updateHourHandLayerPath()
17 | }
18 | }
19 |
20 | var minute: Int = 0 {
21 | didSet {
22 | minute = min(60, max(0, minute))
23 | updateMinuteHandLayerPath()
24 | }
25 | }
26 |
27 | private let hourHandLayer = CAShapeLayer()
28 | private let minuteHandLayer = CAShapeLayer()
29 |
30 | var hourHandLength: CGFloat = 8 {
31 | didSet {
32 | updateHourHandLayerPath()
33 | }
34 | }
35 | var minuteHandLength: CGFloat = 10 {
36 | didSet {
37 | updateMinuteHandLayerPath()
38 | }
39 | }
40 |
41 | var lineWidth: CGFloat {
42 | get {
43 | return hourHandLayer.lineWidth
44 | }
45 | set {
46 | hourHandLayer.lineWidth = newValue
47 | minuteHandLayer.lineWidth = newValue
48 | updateHourHandLayerPath()
49 | updateMinuteHandLayerPath()
50 | }
51 | }
52 |
53 | override var tintColor: UIColor! {
54 | get {
55 | return UIColor(cgColor: hourHandLayer.strokeColor!)
56 | }
57 | set {
58 | hourHandLayer.strokeColor = newValue.cgColor
59 | minuteHandLayer.strokeColor = newValue.cgColor
60 | updateHourHandLayerPath()
61 | updateMinuteHandLayerPath()
62 | }
63 | }
64 |
65 | required init?(coder aDecoder: NSCoder) {
66 | super.init(coder: aDecoder)
67 | setupView()
68 | }
69 | override init(frame: CGRect) {
70 | super.init(frame: frame)
71 | setupView()
72 | }
73 |
74 | private func setupView() {
75 | translatesAutoresizingMaskIntoConstraints = false
76 | //updateBounds(bounds)
77 |
78 | hourHandLayer.lineCap = .round
79 | minuteHandLayer.lineCap = .round
80 | layer.addSublayer(hourHandLayer)
81 | layer.addSublayer(minuteHandLayer)
82 | }
83 |
84 |
85 | override func layoutSubviews() {
86 | updateBounds(bounds)
87 | }
88 |
89 | private func updateBounds(_ bounds: CGRect) {
90 | [hourHandLayer, minuteHandLayer].forEach {
91 | $0.bounds = bounds
92 | $0.position = CGPoint(x: bounds.midX, y: bounds.midY)
93 | }
94 | updateHourHandLayerPath()
95 | updateMinuteHandLayerPath()
96 | }
97 |
98 | private func updateHourHandLayerPath() {
99 | let hourHandLayerPath = UIBezierPath()
100 | let center = CGPoint(x: bounds.midX, y: bounds.midY)
101 | hourHandLayerPath.move(to: center)
102 | hourHandLayerPath.addLine(to: CGPoint(x: center.x, y: center.y - hourHandLength))
103 | hourHandLayer.path = hourHandLayerPath.cgPath
104 |
105 | ///需要考虑分钟引起的时针偏移
106 | var radian = (2 * CGFloat.pi * CGFloat(hour)) / 12
107 | radian += (CGFloat(minute) / 60) * (2 * CGFloat.pi) / 12
108 | hourHandLayer.transform = CATransform3DMakeRotation(radian, 0, 0, 1)
109 | }
110 |
111 | private func updateMinuteHandLayerPath() {
112 | let minuteHandLayerPath = UIBezierPath()
113 | let center = CGPoint(x: bounds.midX, y: bounds.midY)
114 | //minuteHandLayerPath.move(to: center.applying(CGAffineTransform(translationX: 0, y: lineWidth / 2)))
115 | minuteHandLayerPath.move(to: center)
116 | minuteHandLayerPath.addLine(to: CGPoint(x: center.x, y: center.y - minuteHandLength))
117 | minuteHandLayer.path = minuteHandLayerPath.cgPath
118 | minuteHandLayer.transform = CATransform3DMakeRotation((2 * CGFloat.pi * CGFloat(minute)) / 60 , 0, 0, 1)
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/TabBarInteraction/WheelView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WheelView.swift
3 | // TabBarInteraction
4 | //
5 | // Created by potato04 on 2019/3/28.
6 | // Copyright © 2019 potato04. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class WheelView: UIView {
12 |
13 | var progress: Float = 0.0 {
14 | didSet {
15 | progress = min(max(progress, 0.0), 1.0)
16 | guard !contents.isEmpty else { return }
17 |
18 | /** 根据 progress 和 contents 计算出上下两个 label 显示的内容以及 label 的压缩程度和位置
19 | *
20 | * Example:
21 | * progress = 0.4, contents = ["A","B","C","D"]
22 | *
23 | * 1)计算两个label显示的内容
24 | * topIndex = 4 * 0.4 = 1.6, topLabel.text = contents[1] = "B"
25 | * bottomIndex = 1.6 + 1 = 2.6, bottomLabel.text = contents[2] = "C"
26 | *
27 | * 2) 计算两个label如何压缩和位置调整,这是实现滚轮效果的原理
28 | * indexOffset = 1.6 % 1 = 0.6
29 | * halfHeight = bounds.height / 2
30 | *
31 | * ┌─────────────┐ ┌─────────────┐
32 | * |┌───────────┐| scaleY | |
33 | * || || 1-0.6=0.4 | | translationY
34 | * || topLabel || ----------> |┌─ topLabel─┐| ------------------┐
35 | * || || |└───────────┘| -halfHeight * 0.6 | ┌─────────────┐
36 | * |└───────────┘| | | | |┌─ toplabel─┐|
37 | * └─────────────┘ └─────────────┘ | |└───────────┘|
38 | * |--> |┌───────────┐|
39 | * ┌─────────────┐ ┌─────────────┐ | ||bottomLabel||
40 | * |┌───────────┐| scaleY | | | |└───────────┘|
41 | * || || 0.6 |┌───────────┐| translationY | └─────────────┘
42 | * ||bottomLabel|| ----------> ||bottomLabel|| ------------------┘
43 | * || || |└───────────┘| halfHeight * 0.4
44 | * |└───────────┘| | |
45 | * └─────────────┘ └─────────────┘
46 | *
47 | * 可以想象出,当 indexOffset 从 0..<1 过程中,
48 | * topLabel 从满视图越缩越小至0,而 bottomLabel 越放越大至满视图,即形成一次完整的滚动
49 | */
50 |
51 | let topIndex = min(max(0.0, Float(contents.count) * progress), Float(contents.count - 1))
52 | let bottomIndex = min(topIndex + 1, Float(contents.count - 1))
53 | let indexOffset = topIndex.truncatingRemainder(dividingBy: 1)
54 |
55 | toplabel.text = contents[Int(topIndex)]
56 | toplabel.transform = CGAffineTransform(scaleX: 1.0, y: CGFloat(1 - indexOffset))
57 | .concatenating(CGAffineTransform(translationX: 0, y: -(toplabel.bounds.height / 2) * CGFloat(indexOffset)))
58 |
59 | bottomLabel.text = contents[Int(bottomIndex)]
60 | bottomLabel.transform = CGAffineTransform(scaleX: 1.0, y: CGFloat(indexOffset))
61 | .concatenating(CGAffineTransform(translationX: 0, y: (bottomLabel.bounds.height / 2) * (1 - CGFloat(indexOffset))))
62 | }
63 | }
64 |
65 | var contents = [String]()
66 |
67 | private lazy var toplabel: UILabel = {
68 | return createDefaultLabel()
69 | }()
70 |
71 | private lazy var bottomLabel: UILabel = {
72 | return createDefaultLabel()
73 | }()
74 |
75 | private func createDefaultLabel() -> UILabel {
76 | let label = UILabel()
77 | label.textAlignment = NSTextAlignment.center
78 | label.adjustsFontSizeToFitWidth = true
79 | label.translatesAutoresizingMaskIntoConstraints = false
80 | return label
81 | }
82 |
83 | required init?(coder aDecoder: NSCoder) {
84 | super.init(coder: aDecoder)
85 | setupView()
86 | }
87 | override init(frame: CGRect) {
88 | super.init(frame: frame)
89 | setupView()
90 | }
91 |
92 | private func setupView() {
93 | translatesAutoresizingMaskIntoConstraints = false
94 |
95 | [toplabel, bottomLabel].forEach {
96 | addSubview($0)
97 | NSLayoutConstraint.activate([
98 | $0.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
99 | $0.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor),
100 | $0.leftAnchor.constraint(equalTo: layoutMarginsGuide.leftAnchor),
101 | $0.rightAnchor.constraint(equalTo: layoutMarginsGuide.rightAnchor)
102 | ])
103 | }
104 | }
105 | }
106 |
107 |
108 | extension WheelView {
109 |
110 | override func tintColorDidChange() {
111 | [toplabel, bottomLabel].forEach {
112 | $0.textColor = tintColor
113 | }
114 | layer.borderColor = tintColor.cgColor
115 | }
116 |
117 | override var backgroundColor: UIColor? {
118 | get {
119 | return toplabel.backgroundColor
120 | }
121 | set {
122 | [toplabel, bottomLabel].forEach {
123 | $0.backgroundColor = newValue
124 | }
125 | }
126 | }
127 |
128 | var borderWidth: CGFloat {
129 | get {
130 | return layer.borderWidth
131 | }
132 | set {
133 | layoutMargins = UIEdgeInsets(top: newValue, left: newValue, bottom: newValue, right: newValue)
134 | layer.borderWidth = newValue
135 | }
136 | }
137 |
138 | var font: UIFont {
139 | get {
140 | return toplabel.font
141 | }
142 | set {
143 | [toplabel, bottomLabel].forEach {
144 | $0.font = newValue
145 | }
146 | }
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/TabBarInteraction/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 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/TabBarInteraction.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 4BC6C127225118EE00D7FCF0 /* ClockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC6C126225118EE00D7FCF0 /* ClockView.swift */; };
11 | 4BC6C1292252585C00D7FCF0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4BC6C1282252585B00D7FCF0 /* Assets.xcassets */; };
12 | 4BCCAEB022546E4A003BF7B0 /* TabbarInteractable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BCCAEAF22546E4A003BF7B0 /* TabbarInteractable.swift */; };
13 | 4BEA4951224BD393006C211C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA4950224BD393006C211C /* AppDelegate.swift */; };
14 | 4BEA4953224BD393006C211C /* FirstViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA4952224BD393006C211C /* FirstViewController.swift */; };
15 | 4BEA4955224BD393006C211C /* SecondViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA4954224BD393006C211C /* SecondViewController.swift */; };
16 | 4BEA4958224BD393006C211C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4BEA4956224BD393006C211C /* Main.storyboard */; };
17 | 4BEA495D224BD394006C211C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4BEA495B224BD394006C211C /* LaunchScreen.storyboard */; };
18 | 4BEA4967224CE21B006C211C /* WheelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BEA4966224CE21B006C211C /* WheelView.swift */; };
19 | /* End PBXBuildFile section */
20 |
21 | /* Begin PBXFileReference section */
22 | 4BC6C126225118EE00D7FCF0 /* ClockView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClockView.swift; sourceTree = ""; };
23 | 4BC6C1282252585B00D7FCF0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
24 | 4BCCAEAF22546E4A003BF7B0 /* TabbarInteractable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabbarInteractable.swift; sourceTree = ""; };
25 | 4BEA494D224BD393006C211C /* TabBarInteraction.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TabBarInteraction.app; sourceTree = BUILT_PRODUCTS_DIR; };
26 | 4BEA4950224BD393006C211C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
27 | 4BEA4952224BD393006C211C /* FirstViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstViewController.swift; sourceTree = ""; };
28 | 4BEA4954224BD393006C211C /* SecondViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondViewController.swift; sourceTree = ""; };
29 | 4BEA4957224BD393006C211C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
30 | 4BEA495C224BD394006C211C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
31 | 4BEA495E224BD394006C211C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
32 | 4BEA4966224CE21B006C211C /* WheelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WheelView.swift; sourceTree = ""; };
33 | /* End PBXFileReference section */
34 |
35 | /* Begin PBXFrameworksBuildPhase section */
36 | 4BEA494A224BD393006C211C /* Frameworks */ = {
37 | isa = PBXFrameworksBuildPhase;
38 | buildActionMask = 2147483647;
39 | files = (
40 | );
41 | runOnlyForDeploymentPostprocessing = 0;
42 | };
43 | /* End PBXFrameworksBuildPhase section */
44 |
45 | /* Begin PBXGroup section */
46 | 4BCCAEB122547A00003BF7B0 /* Supporting Files */ = {
47 | isa = PBXGroup;
48 | children = (
49 | 4BC6C1282252585B00D7FCF0 /* Assets.xcassets */,
50 | 4BEA4950224BD393006C211C /* AppDelegate.swift */,
51 | 4BEA4956224BD393006C211C /* Main.storyboard */,
52 | 4BEA495B224BD394006C211C /* LaunchScreen.storyboard */,
53 | 4BEA495E224BD394006C211C /* Info.plist */,
54 | );
55 | name = "Supporting Files";
56 | sourceTree = "";
57 | };
58 | 4BEA4944224BD393006C211C = {
59 | isa = PBXGroup;
60 | children = (
61 | 4BEA494F224BD393006C211C /* TabBarInteraction */,
62 | 4BEA494E224BD393006C211C /* Products */,
63 | );
64 | sourceTree = "";
65 | };
66 | 4BEA494E224BD393006C211C /* Products */ = {
67 | isa = PBXGroup;
68 | children = (
69 | 4BEA494D224BD393006C211C /* TabBarInteraction.app */,
70 | );
71 | name = Products;
72 | sourceTree = "";
73 | };
74 | 4BEA494F224BD393006C211C /* TabBarInteraction */ = {
75 | isa = PBXGroup;
76 | children = (
77 | 4BCCAEB122547A00003BF7B0 /* Supporting Files */,
78 | 4BEA4952224BD393006C211C /* FirstViewController.swift */,
79 | 4BEA4954224BD393006C211C /* SecondViewController.swift */,
80 | 4BEA4966224CE21B006C211C /* WheelView.swift */,
81 | 4BC6C126225118EE00D7FCF0 /* ClockView.swift */,
82 | 4BCCAEAF22546E4A003BF7B0 /* TabbarInteractable.swift */,
83 | );
84 | path = TabBarInteraction;
85 | sourceTree = "";
86 | };
87 | /* End PBXGroup section */
88 |
89 | /* Begin PBXNativeTarget section */
90 | 4BEA494C224BD393006C211C /* TabBarInteraction */ = {
91 | isa = PBXNativeTarget;
92 | buildConfigurationList = 4BEA4961224BD394006C211C /* Build configuration list for PBXNativeTarget "TabBarInteraction" */;
93 | buildPhases = (
94 | 4BEA4949224BD393006C211C /* Sources */,
95 | 4BEA494A224BD393006C211C /* Frameworks */,
96 | 4BEA494B224BD393006C211C /* Resources */,
97 | );
98 | buildRules = (
99 | );
100 | dependencies = (
101 | );
102 | name = TabBarInteraction;
103 | productName = TabBarIconInteraction;
104 | productReference = 4BEA494D224BD393006C211C /* TabBarInteraction.app */;
105 | productType = "com.apple.product-type.application";
106 | };
107 | /* End PBXNativeTarget section */
108 |
109 | /* Begin PBXProject section */
110 | 4BEA4945224BD393006C211C /* Project object */ = {
111 | isa = PBXProject;
112 | attributes = {
113 | LastSwiftUpdateCheck = 1020;
114 | LastUpgradeCheck = 1020;
115 | ORGANIZATIONNAME = potato04;
116 | TargetAttributes = {
117 | 4BEA494C224BD393006C211C = {
118 | CreatedOnToolsVersion = 10.2;
119 | };
120 | };
121 | };
122 | buildConfigurationList = 4BEA4948224BD393006C211C /* Build configuration list for PBXProject "TabBarInteraction" */;
123 | compatibilityVersion = "Xcode 9.3";
124 | developmentRegion = en;
125 | hasScannedForEncodings = 0;
126 | knownRegions = (
127 | en,
128 | Base,
129 | );
130 | mainGroup = 4BEA4944224BD393006C211C;
131 | productRefGroup = 4BEA494E224BD393006C211C /* Products */;
132 | projectDirPath = "";
133 | projectRoot = "";
134 | targets = (
135 | 4BEA494C224BD393006C211C /* TabBarInteraction */,
136 | );
137 | };
138 | /* End PBXProject section */
139 |
140 | /* Begin PBXResourcesBuildPhase section */
141 | 4BEA494B224BD393006C211C /* Resources */ = {
142 | isa = PBXResourcesBuildPhase;
143 | buildActionMask = 2147483647;
144 | files = (
145 | 4BEA495D224BD394006C211C /* LaunchScreen.storyboard in Resources */,
146 | 4BC6C1292252585C00D7FCF0 /* Assets.xcassets in Resources */,
147 | 4BEA4958224BD393006C211C /* Main.storyboard in Resources */,
148 | );
149 | runOnlyForDeploymentPostprocessing = 0;
150 | };
151 | /* End PBXResourcesBuildPhase section */
152 |
153 | /* Begin PBXSourcesBuildPhase section */
154 | 4BEA4949224BD393006C211C /* Sources */ = {
155 | isa = PBXSourcesBuildPhase;
156 | buildActionMask = 2147483647;
157 | files = (
158 | 4BEA4955224BD393006C211C /* SecondViewController.swift in Sources */,
159 | 4BEA4951224BD393006C211C /* AppDelegate.swift in Sources */,
160 | 4BC6C127225118EE00D7FCF0 /* ClockView.swift in Sources */,
161 | 4BCCAEB022546E4A003BF7B0 /* TabbarInteractable.swift in Sources */,
162 | 4BEA4967224CE21B006C211C /* WheelView.swift in Sources */,
163 | 4BEA4953224BD393006C211C /* FirstViewController.swift in Sources */,
164 | );
165 | runOnlyForDeploymentPostprocessing = 0;
166 | };
167 | /* End PBXSourcesBuildPhase section */
168 |
169 | /* Begin PBXVariantGroup section */
170 | 4BEA4956224BD393006C211C /* Main.storyboard */ = {
171 | isa = PBXVariantGroup;
172 | children = (
173 | 4BEA4957224BD393006C211C /* Base */,
174 | );
175 | name = Main.storyboard;
176 | sourceTree = "";
177 | };
178 | 4BEA495B224BD394006C211C /* LaunchScreen.storyboard */ = {
179 | isa = PBXVariantGroup;
180 | children = (
181 | 4BEA495C224BD394006C211C /* Base */,
182 | );
183 | name = LaunchScreen.storyboard;
184 | sourceTree = "";
185 | };
186 | /* End PBXVariantGroup section */
187 |
188 | /* Begin XCBuildConfiguration section */
189 | 4BEA495F224BD394006C211C /* Debug */ = {
190 | isa = XCBuildConfiguration;
191 | buildSettings = {
192 | ALWAYS_SEARCH_USER_PATHS = NO;
193 | CLANG_ANALYZER_NONNULL = YES;
194 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
195 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
196 | CLANG_CXX_LIBRARY = "libc++";
197 | CLANG_ENABLE_MODULES = YES;
198 | CLANG_ENABLE_OBJC_ARC = YES;
199 | CLANG_ENABLE_OBJC_WEAK = YES;
200 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
201 | CLANG_WARN_BOOL_CONVERSION = YES;
202 | CLANG_WARN_COMMA = YES;
203 | CLANG_WARN_CONSTANT_CONVERSION = YES;
204 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
205 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
206 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
207 | CLANG_WARN_EMPTY_BODY = YES;
208 | CLANG_WARN_ENUM_CONVERSION = YES;
209 | CLANG_WARN_INFINITE_RECURSION = YES;
210 | CLANG_WARN_INT_CONVERSION = YES;
211 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
212 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
213 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
214 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
215 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
216 | CLANG_WARN_STRICT_PROTOTYPES = YES;
217 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
218 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
219 | CLANG_WARN_UNREACHABLE_CODE = YES;
220 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
221 | CODE_SIGN_IDENTITY = "iPhone Developer";
222 | COPY_PHASE_STRIP = NO;
223 | DEBUG_INFORMATION_FORMAT = dwarf;
224 | ENABLE_STRICT_OBJC_MSGSEND = YES;
225 | ENABLE_TESTABILITY = YES;
226 | GCC_C_LANGUAGE_STANDARD = gnu11;
227 | GCC_DYNAMIC_NO_PIC = NO;
228 | GCC_NO_COMMON_BLOCKS = YES;
229 | GCC_OPTIMIZATION_LEVEL = 0;
230 | GCC_PREPROCESSOR_DEFINITIONS = (
231 | "DEBUG=1",
232 | "$(inherited)",
233 | );
234 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
235 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
236 | GCC_WARN_UNDECLARED_SELECTOR = YES;
237 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
238 | GCC_WARN_UNUSED_FUNCTION = YES;
239 | GCC_WARN_UNUSED_VARIABLE = YES;
240 | IPHONEOS_DEPLOYMENT_TARGET = 12.2;
241 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
242 | MTL_FAST_MATH = YES;
243 | ONLY_ACTIVE_ARCH = YES;
244 | SDKROOT = iphoneos;
245 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
246 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
247 | };
248 | name = Debug;
249 | };
250 | 4BEA4960224BD394006C211C /* Release */ = {
251 | isa = XCBuildConfiguration;
252 | buildSettings = {
253 | ALWAYS_SEARCH_USER_PATHS = NO;
254 | CLANG_ANALYZER_NONNULL = YES;
255 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
256 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
257 | CLANG_CXX_LIBRARY = "libc++";
258 | CLANG_ENABLE_MODULES = YES;
259 | CLANG_ENABLE_OBJC_ARC = YES;
260 | CLANG_ENABLE_OBJC_WEAK = YES;
261 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
262 | CLANG_WARN_BOOL_CONVERSION = YES;
263 | CLANG_WARN_COMMA = YES;
264 | CLANG_WARN_CONSTANT_CONVERSION = YES;
265 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
266 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
267 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
268 | CLANG_WARN_EMPTY_BODY = YES;
269 | CLANG_WARN_ENUM_CONVERSION = YES;
270 | CLANG_WARN_INFINITE_RECURSION = YES;
271 | CLANG_WARN_INT_CONVERSION = YES;
272 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
273 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
274 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
275 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
276 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
277 | CLANG_WARN_STRICT_PROTOTYPES = YES;
278 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
279 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
280 | CLANG_WARN_UNREACHABLE_CODE = YES;
281 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
282 | CODE_SIGN_IDENTITY = "iPhone Developer";
283 | COPY_PHASE_STRIP = NO;
284 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
285 | ENABLE_NS_ASSERTIONS = NO;
286 | ENABLE_STRICT_OBJC_MSGSEND = YES;
287 | GCC_C_LANGUAGE_STANDARD = gnu11;
288 | GCC_NO_COMMON_BLOCKS = YES;
289 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
290 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
291 | GCC_WARN_UNDECLARED_SELECTOR = YES;
292 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
293 | GCC_WARN_UNUSED_FUNCTION = YES;
294 | GCC_WARN_UNUSED_VARIABLE = YES;
295 | IPHONEOS_DEPLOYMENT_TARGET = 12.2;
296 | MTL_ENABLE_DEBUG_INFO = NO;
297 | MTL_FAST_MATH = YES;
298 | SDKROOT = iphoneos;
299 | SWIFT_COMPILATION_MODE = wholemodule;
300 | SWIFT_OPTIMIZATION_LEVEL = "-O";
301 | VALIDATE_PRODUCT = YES;
302 | };
303 | name = Release;
304 | };
305 | 4BEA4962224BD394006C211C /* Debug */ = {
306 | isa = XCBuildConfiguration;
307 | buildSettings = {
308 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
309 | CODE_SIGN_IDENTITY = "iPhone Developer";
310 | CODE_SIGN_STYLE = Automatic;
311 | DEVELOPMENT_TEAM = "";
312 | INFOPLIST_FILE = "$(SRCROOT)/TabBarInteraction/Info.plist";
313 | LD_RUNPATH_SEARCH_PATHS = (
314 | "$(inherited)",
315 | "@executable_path/Frameworks",
316 | );
317 | PRODUCT_BUNDLE_IDENTIFIER = com.potato04.TabBarInteraction;
318 | PRODUCT_NAME = "$(TARGET_NAME)";
319 | PROVISIONING_PROFILE_SPECIFIER = "";
320 | SWIFT_VERSION = 5.0;
321 | TARGETED_DEVICE_FAMILY = "1,2";
322 | };
323 | name = Debug;
324 | };
325 | 4BEA4963224BD394006C211C /* Release */ = {
326 | isa = XCBuildConfiguration;
327 | buildSettings = {
328 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
329 | CODE_SIGN_IDENTITY = "iPhone Developer";
330 | CODE_SIGN_STYLE = Automatic;
331 | DEVELOPMENT_TEAM = "";
332 | INFOPLIST_FILE = "$(SRCROOT)/TabBarInteraction/Info.plist";
333 | LD_RUNPATH_SEARCH_PATHS = (
334 | "$(inherited)",
335 | "@executable_path/Frameworks",
336 | );
337 | PRODUCT_BUNDLE_IDENTIFIER = com.potato04.TabBarInteraction;
338 | PRODUCT_NAME = "$(TARGET_NAME)";
339 | PROVISIONING_PROFILE_SPECIFIER = "";
340 | SWIFT_VERSION = 5.0;
341 | TARGETED_DEVICE_FAMILY = "1,2";
342 | };
343 | name = Release;
344 | };
345 | /* End XCBuildConfiguration section */
346 |
347 | /* Begin XCConfigurationList section */
348 | 4BEA4948224BD393006C211C /* Build configuration list for PBXProject "TabBarInteraction" */ = {
349 | isa = XCConfigurationList;
350 | buildConfigurations = (
351 | 4BEA495F224BD394006C211C /* Debug */,
352 | 4BEA4960224BD394006C211C /* Release */,
353 | );
354 | defaultConfigurationIsVisible = 0;
355 | defaultConfigurationName = Release;
356 | };
357 | 4BEA4961224BD394006C211C /* Build configuration list for PBXNativeTarget "TabBarInteraction" */ = {
358 | isa = XCConfigurationList;
359 | buildConfigurations = (
360 | 4BEA4962224BD394006C211C /* Debug */,
361 | 4BEA4963224BD394006C211C /* Release */,
362 | );
363 | defaultConfigurationIsVisible = 0;
364 | defaultConfigurationName = Release;
365 | };
366 | /* End XCConfigurationList section */
367 | };
368 | rootObject = 4BEA4945224BD393006C211C /* Project object */;
369 | }
370 |
--------------------------------------------------------------------------------