Repository: jasudev/AxisSegmentedView Branch: main Commit: 44e42f5766fe Files: 89 Total size: 251.4 KB Directory structure: gitextract_kiml9m07/ ├── .gitignore ├── AxisSegmentedViewExample/ │ ├── AxisSegmentedViewExample (tvOS)/ │ │ ├── Assets.xcassets/ │ │ │ ├── AccentColor.colorset/ │ │ │ │ └── Contents.json │ │ │ ├── App Icon & Top Shelf Image.brandassets/ │ │ │ │ ├── App Icon - App Store.imagestack/ │ │ │ │ │ ├── Back.imagestacklayer/ │ │ │ │ │ │ ├── Content.imageset/ │ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ └── Contents.json │ │ │ │ │ ├── Contents.json │ │ │ │ │ ├── Front.imagestacklayer/ │ │ │ │ │ │ ├── Content.imageset/ │ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ └── Contents.json │ │ │ │ │ └── Middle.imagestacklayer/ │ │ │ │ │ ├── Content.imageset/ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ └── Contents.json │ │ │ │ ├── App Icon.imagestack/ │ │ │ │ │ ├── Back.imagestacklayer/ │ │ │ │ │ │ ├── Content.imageset/ │ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ └── Contents.json │ │ │ │ │ ├── Contents.json │ │ │ │ │ ├── Front.imagestacklayer/ │ │ │ │ │ │ ├── Content.imageset/ │ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ │ └── Contents.json │ │ │ │ │ └── Middle.imagestacklayer/ │ │ │ │ │ ├── Content.imageset/ │ │ │ │ │ │ └── Contents.json │ │ │ │ │ └── Contents.json │ │ │ │ ├── Contents.json │ │ │ │ ├── Top Shelf Image Wide.imageset/ │ │ │ │ │ └── Contents.json │ │ │ │ └── Top Shelf Image.imageset/ │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── AxisSegmentedViewExample__tvOS_App.swift │ │ ├── ContentView.swift │ │ ├── CustomStyle.swift │ │ ├── CustomStyleView.swift │ │ ├── Preview Content/ │ │ │ └── Preview Assets.xcassets/ │ │ │ └── Contents.json │ │ ├── SegmentedListView.swift │ │ ├── SegmentedViewWithControl.swift │ │ ├── SelectionItemView.swift │ │ ├── ViewModel/ │ │ │ ├── BacisValue.swift │ │ │ ├── CapsuleValue.swift │ │ │ ├── JellyValue.swift │ │ │ ├── LineValue.swift │ │ │ ├── NeumorphismValue.swift │ │ │ ├── NormalValue.swift │ │ │ ├── ScaleValue.swift │ │ │ └── ViscosityValue.swift │ │ └── WithoutStyleView.swift │ ├── AxisSegmentedViewExample.xcodeproj/ │ │ ├── project.pbxproj │ │ ├── project.xcworkspace/ │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata/ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ └── AxisSegmentedView.xcscheme │ ├── Shared/ │ │ ├── Assets.xcassets/ │ │ │ ├── AccentColor.colorset/ │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ └── image.imageset/ │ │ │ └── Contents.json │ │ ├── AxisSegmentedViewExampleApp.swift │ │ ├── Color+Extensions.swift │ │ ├── ContentView.swift │ │ ├── CustomStyle.swift │ │ ├── CustomStyleView.swift │ │ ├── SegmentedListView.swift │ │ ├── SegmentedViewWithControl.swift │ │ ├── SelectionItemView.swift │ │ ├── ViewModel/ │ │ │ ├── BacisValue.swift │ │ │ ├── CapsuleValue.swift │ │ │ ├── JellyValue.swift │ │ │ ├── LineValue.swift │ │ │ ├── NeumorphismValue.swift │ │ │ ├── NormalValue.swift │ │ │ ├── ScaleValue.swift │ │ │ └── ViscosityValue.swift │ │ └── WithoutStyleView.swift │ └── macOS/ │ └── macOS.entitlements ├── LICENSE ├── Package.swift ├── README.md ├── Sources/ │ └── AxisSegmentedView/ │ ├── AxisSegmentedView.swift │ ├── Private/ │ │ ├── Model/ │ │ │ └── ASItem.swift │ │ ├── Modifiers/ │ │ │ └── ASItemModifier.swift │ │ └── ViewModel/ │ │ ├── ASPositionValue.swift │ │ └── ASSelectionValue.swift │ └── Public/ │ ├── Constants/ │ │ ├── ASConstant.swift │ │ └── ASLineConstant.swift │ ├── Extensions/ │ │ ├── Animation+Extensions.swift │ │ └── View+Extensions.swift │ ├── Modifiers/ │ │ ├── NeumorphismInnerModifier.swift │ │ └── NeumorphismOuterModifier.swift │ ├── Previews/ │ │ └── SegmentedViewPreview.swift │ ├── Shapes/ │ │ └── ASCurveShape.swift │ ├── Styles/ │ │ ├── ASBasicStyle.swift │ │ ├── ASCapsuleStyle.swift │ │ ├── ASJellyStyle.swift │ │ ├── ASLineStyle.swift │ │ ├── ASNeumorphismStyle.swift │ │ ├── ASScaleStyle.swift │ │ └── Movement/ │ │ ├── ASNormalStyle.swift │ │ └── ASViscosityStyle.swift │ └── ViewModel/ │ └── ASStateValue.swift └── Tests/ └── AxisSegmentedViewTests/ └── AxisSegmentedViewTests.swift ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore ## User settings xcuserdata/ ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) *.xcscmblueprint *.xccheckout ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) build/ DerivedData/ *.moved-aside *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 ## Obj-C/Swift specific *.hmap ## App packaging *.ipa *.dSYM.zip *.dSYM ## Playgrounds timeline.xctimeline playground.xcworkspace # Swift Package Manager # # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. # Packages/ # Package.pins # Package.resolved # *.xcodeproj # # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata # hence it is not needed unless you have added a package configuration file to your project # .swiftpm .build/ # CocoaPods # # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control # # Pods/ # # Add this line if you want to avoid checking in source code from the Xcode workspace # *.xcworkspace # Carthage # # Add this line if you want to avoid checking in source code from Carthage dependencies. # Carthage/Checkouts Carthage/Build/ # Accio dependency management Dependencies/ .accio/ # fastlane # # It is recommended to not store the screenshots in the git repo. # Instead, use fastlane to re-generate the screenshots whenever they are needed. # For more information about the recommended setup visit: # https://docs.fastlane.tools/best-practices/source-control/#source-control fastlane/report.xml fastlane/Preview.html fastlane/screenshots/**/*.png fastlane/test_output # Code Injection # # After new code Injection tools there's a generated folder /iOSInjectionProject # https://github.com/johnno1962/injectionforxcode iOSInjectionProject/ ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/Assets.xcassets/AccentColor.colorset/Contents.json ================================================ { "colors" : [ { "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "tv" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 }, "layers" : [ { "filename" : "Front.imagestacklayer" }, { "filename" : "Middle.imagestacklayer" }, { "filename" : "Back.imagestacklayer" } ] } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "tv" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "tv" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "tv", "scale" : "1x" }, { "idiom" : "tv", "scale" : "2x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 }, "layers" : [ { "filename" : "Front.imagestacklayer" }, { "filename" : "Middle.imagestacklayer" }, { "filename" : "Back.imagestacklayer" } ] } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "tv", "scale" : "1x" }, { "idiom" : "tv", "scale" : "2x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "tv", "scale" : "1x" }, { "idiom" : "tv", "scale" : "2x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json ================================================ { "assets" : [ { "filename" : "App Icon - App Store.imagestack", "idiom" : "tv", "role" : "primary-app-icon", "size" : "1280x768" }, { "filename" : "App Icon.imagestack", "idiom" : "tv", "role" : "primary-app-icon", "size" : "400x240" }, { "filename" : "Top Shelf Image Wide.imageset", "idiom" : "tv", "role" : "top-shelf-image-wide", "size" : "2320x720" }, { "filename" : "Top Shelf Image.imageset", "idiom" : "tv", "role" : "top-shelf-image", "size" : "1920x720" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "tv", "scale" : "1x" }, { "idiom" : "tv", "scale" : "2x" }, { "idiom" : "tv-marketing", "scale" : "1x" }, { "idiom" : "tv-marketing", "scale" : "2x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json ================================================ { "images" : [ { "idiom" : "tv", "scale" : "1x" }, { "idiom" : "tv", "scale" : "2x" }, { "idiom" : "tv-marketing", "scale" : "1x" }, { "idiom" : "tv-marketing", "scale" : "2x" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/Assets.xcassets/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/AxisSegmentedViewExample__tvOS_App.swift ================================================ // // AxisSegmentedViewExample__tvOS_App.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/27. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI @main struct AxisSegmentedViewExample__tvOS_App: App { var body: some Scene { WindowGroup { ContentView() } } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/ContentView.swift ================================================ // // ContentView.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/27. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView struct ContentView: View { @State private var tabViewSelection: Int = 0 var body: some View { TabView(selection: $tabViewSelection) { SegmentedListView(axisMode: .horizontal) .tag(0) .tabItem { Image(systemName: "rectangle.arrowtriangle.2.inward") Text("Horizontal") } SegmentedListView(axisMode: .vertical) .tag(1) .tabItem { Image(systemName: "rectangle.portrait.arrowtriangle.2.inward") Text("Vertical") } WithoutStyleView() .tag(2) .tabItem { Image(systemName: "cpu") Text("Without style") } CustomStyleView() .tag(3) .tabItem { Image(systemName: "skew") Text("Custom Style") } .padding() } .navigationTitle(Text("AxisSegmentedView")) .padding() } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/CustomStyle.swift ================================================ // // CustomStyle.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/25. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView public struct CustomStyle: View { @EnvironmentObject var stateValue: ASStateValue let color: Color public init(color: Color = .purple) { self.color = color } private var selectionView: some View { RoundedRectangle(cornerRadius: 5) .fill(Color.yellow) } public var body: some View { let selectionFrame = stateValue.selectionFrame ZStack(alignment: .topLeading) { Color.clear RoundedRectangle(cornerRadius: 5) .stroke() .fill(color) .frame(width: selectionFrame.width, height: selectionFrame.height) .offset(x: selectionFrame.origin.x, y: selectionFrame.origin.y) } .animation(.easeInOut, value: stateValue.selectionIndex) } } struct CustomStyle_Previews: PreviewProvider { static var previews: some View { SegmentedViewPreview(constant: .init()) { CustomStyle() .preferredColorScheme(.dark) } .frame(height: 44) .padding(.horizontal, 16) } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/CustomStyleView.swift ================================================ // // CustomStyleView.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/26. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView struct CustomStyleView: View { @State private var selection: Int = 0 var body: some View { AxisSegmentedView(selection: $selection, constant: .init()) { Image(systemName: "align.horizontal.left") .itemTag(0, selectArea: 0) { SelectionItemView("align.horizontal.left.fill") } Image(systemName: "align.horizontal.right") .itemTag(1, selectArea: 560) { SelectionItemView("align.horizontal.right.fill") } Image(systemName: "align.vertical.top") .itemTag(2, selectArea: 0) { SelectionItemView("align.vertical.top.fill") } Image(systemName: "align.vertical.bottom") .itemTag(3, selectArea: 560) { SelectionItemView("align.vertical.bottom.fill") } } style: { CustomStyle(color: .red) } .frame(height: 80) } } struct CustomStyleView_Previews: PreviewProvider { static var previews: some View { CustomStyleView() } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/Preview Content/Preview Assets.xcassets/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/SegmentedListView.swift ================================================ // // SegmentedListView.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/23. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView enum StyleType: String { case ASNormalStyle case ASViscosityStyle case ASBasicStyle case ASCapsuleStyle case ASJellyStyle case ASLineStyle case ASNeumorphismStyle case ASScaleStyle } struct SegmentedListView: View { let axisMode: ASAxisMode @StateObject private var normalValue: NormalValue = .init() @StateObject private var viscosityValue: ViscosityValue = .init() @StateObject private var basicValue: BasicValue = .init() @StateObject private var capsuleValue: CapsuleValue = .init() @StateObject private var jellyValue: JellyValue = .init() @StateObject private var lineValue: LineValue = .init() @StateObject private var neumorphismValue: NeumorphismValue = .init() @StateObject private var scaleValue: ScaleValue = .init() var content: some View { Group { SegmentedViewWithControl(title: "ABNormalStyle", styleType: .ASNormalStyle, axisMode: axisMode, constant: $normalValue.constant, tabs: { Group { Text("Clear") .font(.callout) .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .foregroundColor(Color.white.opacity(0.5)) .itemTag(0, selectArea: normalValue.selectArea0) { HStack { Image(systemName: "checkmark.circle") Text("Clear") } .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .font(.callout) .foregroundColor(Color.white) } Text("Confusing") .font(.callout) .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .foregroundColor(Color.white.opacity(0.5)) .itemTag(1, selectArea: normalValue.selectArea1) { HStack { Image(systemName: "checkmark.circle") Text("Confusing") } .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .font(.callout) .foregroundColor(Color.white) } } }, style: { ASNormalStyle { _ in RoundedRectangle(cornerRadius: 5) .fill(Color(hex: 0x191919)) .overlay( RoundedRectangle(cornerRadius: 5) .stroke() .fill(Color(hex: 0x282828)) ) .padding(3.5) } .background(Color(hex: 0x0B0C10)) .clipShape(RoundedRectangle(cornerRadius: 5)) }) SegmentedViewWithControl(title: "ASViscosityStyle", styleType: .ASViscosityStyle, axisMode: axisMode, constant: $viscosityValue.constant, tabs: { Group { Text("Store") .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .font(.callout) .foregroundColor(Color.white.opacity(0.5)) .itemTag(0, selectArea: viscosityValue.selectArea0) { Text("Store") .font(.callout) .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .foregroundColor(Color.white) } Text("Library") .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .font(.callout) .foregroundColor(Color.white.opacity(0.5)) .itemTag(1, selectArea: viscosityValue.selectArea1) { Text("Library") .font(.callout) .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .foregroundColor(Color.white) } Text("Downloads") .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .font(.callout) .foregroundColor(Color.white.opacity(0.5)) .itemTag(2, selectArea: viscosityValue.selectArea2) { Text("Downloads") .font(.callout) .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .foregroundColor(Color.white) } } }, style: { ASViscosityStyle { _ in Capsule() .fill(LinearGradient(colors: [Color(hex: 0x222222), Color(hex: 0x111111)], startPoint: axisMode == .horizontal ? UnitPoint.top : UnitPoint.leading, endPoint: axisMode == .horizontal ? UnitPoint.bottom : UnitPoint.trailing)) .overlay( Capsule() .stroke() .fill(Color.black) ) .padding(2) } .background(Color.black.opacity(0.2)) .clipShape(Capsule()) .innerShadow(Capsule(), radius: 1, opacity: 0.5, isDark: true) }) SegmentedViewWithControl(title: "ASBasicStyle", styleType: .ASBasicStyle, axisMode: axisMode, constant: $basicValue.constant, tabs: { Group { Text("Male") .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .font(.callout) .foregroundColor(Color.white.opacity(0.5)) .itemTag(0, selectArea: basicValue.selectArea0) { Text("Male") .font(.callout) .fixedSize() .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .foregroundColor(Color.white) } Text("Female") .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .font(.callout) .foregroundColor(Color.white.opacity(0.5)) .itemTag(1, selectArea: basicValue.selectArea1) { Text("Female") .font(.callout) .fixedSize() .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .foregroundColor(Color.white) } Text("Other") .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .font(.callout) .foregroundColor(Color.white.opacity(0.5)) .itemTag(2, selectArea: basicValue.selectArea2) { Text("Other") .font(.callout) .fixedSize() .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .foregroundColor(Color.white) } } }, style: { ASBasicStyle(backgroundColor: basicValue.backgroundColor, foregroundColor: basicValue.foregroundColor, cornerRadius: basicValue.cornerRadius, padding: basicValue.padding, isApplySelectionCornerRadius: basicValue.isApplySelectionCornerRadius, movementMode: basicValue.movementMode) }) SegmentedViewWithControl(title: "ASJellyStyle", styleType: .ASJellyStyle, axisMode: axisMode, constant: $jellyValue.constant, tabs: { Group { Image(systemName: "align.horizontal.left") .itemTag(0, selectArea: jellyValue.selectArea0) { SelectionItemView("align.horizontal.left.fill") } Image(systemName: "align.horizontal.right") .itemTag(1, selectArea: jellyValue.selectArea1) { SelectionItemView("align.horizontal.right.fill") } Image(systemName: "align.vertical.top") .itemTag(2, selectArea: jellyValue.selectArea2) { SelectionItemView("align.vertical.top.fill") } Image(systemName: "align.vertical.bottom") .itemTag(3, selectArea: jellyValue.selectArea3) { SelectionItemView("align.vertical.bottom.fill") } } }, style: { ASJellyStyle(backgroundColor: jellyValue.backgroundColor, foregroundColor: jellyValue.foregroundColor, jellyRadius: jellyValue.jellyRadius, jellyDepth: jellyValue.jellyDepth, jellyEdge: jellyValue.jellyEdge) // .overlay( // RoundedRectangle(cornerRadius: 11) // .stroke(.purple, lineWidth: 1) // .padding(1) // ) // .clipShape(RoundedRectangle(cornerRadius: 11)) }) SegmentedViewWithControl(title: "ASLineStyle", styleType: .ASLineStyle, axisMode: axisMode, constant: $lineValue.constant, tabs: { Group { Image(systemName: "align.horizontal.left") .itemTag(0, selectArea: lineValue.selectArea0) { SelectionItemView("align.horizontal.left.fill") } Image(systemName: "align.horizontal.right") .itemTag(1, selectArea: lineValue.selectArea1) { SelectionItemView("align.horizontal.right.fill") } Image(systemName: "align.vertical.top") .itemTag(2, selectArea: lineValue.selectArea2) { SelectionItemView("align.vertical.top.fill") } Image(systemName: "align.vertical.bottom") .itemTag(3, selectArea: lineValue.selectArea3) { SelectionItemView("align.vertical.bottom.fill") } } }, style: { ASLineStyle(lineColor: lineValue.lineColor, lineSmallWidth: lineValue.lineSmallWidth, lineLargeScale: lineValue.lineLargeScale, lineEdge: lineValue.lineEdge, movementMode: lineValue.movementMode) .overlay( Rectangle() .stroke() .fill(Color(hex: 0x303030)) ) }) SegmentedViewWithControl(title: "ASCapsuleStyle", styleType: .ASCapsuleStyle, axisMode: axisMode, constant: $capsuleValue.constant, tabs: { Group { Image(systemName: "align.horizontal.left") .itemTag(0, selectArea: capsuleValue.selectArea0) { SelectionItemView("align.horizontal.left.fill") } Image(systemName: "align.horizontal.right") .itemTag(1, selectArea: capsuleValue.selectArea1) { SelectionItemView("align.horizontal.right.fill") } Image(systemName: "align.vertical.top") .itemTag(2, selectArea: capsuleValue.selectArea2) { SelectionItemView("align.vertical.top.fill") } Image(systemName: "align.vertical.bottom") .itemTag(3, selectArea: capsuleValue.selectArea3) { SelectionItemView("align.vertical.bottom.fill") } } }, style: { ASCapsuleStyle(backgroundColor: capsuleValue.backgroundColor, foregroundColor: capsuleValue.foregroundColor, movementMode: capsuleValue.movementMode) }) SegmentedViewWithControl(title: "ASNeumorphismStyle", styleType: .ASNeumorphismStyle, axisMode: axisMode, constant: $neumorphismValue.constant, area: 120, tabs: { Group { Image(systemName: "align.horizontal.left") .itemTag(0, selectArea: neumorphismValue.selectArea0) { SelectionItemView("align.horizontal.left.fill") } Image(systemName: "align.horizontal.right") .itemTag(1, selectArea: neumorphismValue.selectArea1) { SelectionItemView("align.horizontal.right.fill") } Image(systemName: "align.vertical.top") .itemTag(2, selectArea: neumorphismValue.selectArea2) { SelectionItemView("align.vertical.top.fill") } Image(systemName: "align.vertical.bottom") .itemTag(3, selectArea: neumorphismValue.selectArea3) { SelectionItemView("align.vertical.bottom.fill") } } }, style: { ASNeumorphismStyle(backgroundColor: neumorphismValue.backgroundColor, foregroundColor: neumorphismValue.foregroundColor, cornerRadius: neumorphismValue.cornerRadius, padding: neumorphismValue.padding, shadowRadius: neumorphismValue.shadowRadius, shadowOpacity: neumorphismValue.shadowOpacity, isInner: neumorphismValue.isInner, movementMode: neumorphismValue.movementMode) }) SegmentedViewWithControl(title: "ASScaleStyle", styleType: .ASScaleStyle, axisMode: axisMode, constant: $scaleValue.constant, tabs: { Group { Image(systemName: "align.horizontal.left") .itemTag(0, selectArea: scaleValue.selectArea0) { SelectionItemView("align.horizontal.left.fill") } Image(systemName: "align.horizontal.right") .itemTag(1, selectArea: scaleValue.selectArea1) { SelectionItemView("align.horizontal.right.fill") } Image(systemName: "align.vertical.top") .itemTag(2, selectArea: scaleValue.selectArea2) { SelectionItemView("align.vertical.top.fill") } Image(systemName: "align.vertical.bottom") .itemTag(3, selectArea: scaleValue.selectArea3) { SelectionItemView("align.vertical.bottom.fill") } } }, style: { ASScaleStyle(backgroundColor: scaleValue.backgroundColor, foregroundColor: scaleValue.foregroundColor, cornerRadius: scaleValue.cornerRadius, minimumScale: scaleValue.minimumScale) }) } } var body: some View { ZStack { if axisMode == .horizontal { ScrollView { VStack(spacing: 20) { content } .padding(.horizontal, 5) } }else { ScrollView(.horizontal) { HStack(spacing: 20) { content } .padding(.vertical, 5) } } } .environmentObject(normalValue) .environmentObject(viscosityValue) .environmentObject(basicValue) .environmentObject(capsuleValue) .environmentObject(jellyValue) .environmentObject(lineValue) .environmentObject(neumorphismValue) .environmentObject(scaleValue) } } struct SegmentedListView_Previews: PreviewProvider { static var previews: some View { SegmentedListView(axisMode: .horizontal) .padding() .preferredColorScheme(.dark) } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/SegmentedViewWithControl.swift ================================================ // // SegmentedViewWithControl.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/23. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView struct SegmentedViewWithControl : View { @State private var isShowControlView: Bool = false @State private var selection: Int = 0 @State private var maxSelectArea: CGFloat = 0 @EnvironmentObject private var normalValue: NormalValue @EnvironmentObject private var viscosityValue: ViscosityValue @EnvironmentObject private var basicValue: BasicValue @EnvironmentObject private var capsuleValue: CapsuleValue @EnvironmentObject private var jellyValue: JellyValue @EnvironmentObject private var lineValue: LineValue @EnvironmentObject private var neumorphismValue: NeumorphismValue @EnvironmentObject private var scaleValue: ScaleValue let title: String let styleType: StyleType let axisMode: ASAxisMode @Binding var constant: ASConstant let area: CGFloat var tabs: () -> Tabs var style: () -> Style init(title: String, styleType: StyleType, axisMode: ASAxisMode = .horizontal, constant: Binding, area: CGFloat = 80, @ViewBuilder tabs: @escaping () -> Tabs, @ViewBuilder style: @escaping () -> Style) { self.title = title self.styleType = styleType self.axisMode = axisMode _constant = constant self.area = area self.tabs = tabs self.style = style } private var segmentedView: some View { AxisSegmentedView(selection: $selection, constant: constant, { tabs() }, style: { style() }) .font(.system(size: 20)) } private var controlView: some View { ZStack { Color(hex: 0x030303) .cornerRadius(8) ScrollView { VStack { getStyleControlView() VStack(alignment: .leading, spacing: 8) { Text("● Divide Line").opacity(0.5).font(.caption) HStack { Text("isShowSelectionLine") Spacer() Toggle(isOn: $constant.divideLine.isShowSelectionLine) {} } } .padding() VStack(alignment: .leading, spacing: 8) { Text("● Active").opacity(0.5).font(.caption) HStack { Text("ActivatedGeometryEffect") Spacer() Toggle(isOn: $constant.isActivatedGeometryEffect) {} } } .padding() } } .font(.footnote) .labelsHidden() } } var body: some View { ZStack { ZStack { if constant.axisMode == .horizontal { segmentedView .frame(height: area) }else { segmentedView .frame(width: area) } } .padding(10) } .background( GeometryReader { proxy in Color(hex: 0x15151A) .cornerRadius(8) .onAppear { self.maxSelectArea = constant.axisMode == .horizontal ? proxy.size.width * 0.5 : proxy.size.height * 0.5 } } ) .onAppear { DispatchQueue.main.async { constant.axisMode = axisMode } } } private func getStyleControlView() -> some View { Group { VStack(alignment: .leading, spacing: 8) { HStack { Text("● " + title).opacity(0.5).font(.caption) Spacer() } switch styleType { case .ASBasicStyle: Group { HStack { Text("isApplySelectionCornerRadius") Spacer() Toggle(isOn: $basicValue.isApplySelectionCornerRadius) {} } HStack { Text("Movement Mode") Spacer() Picker(selection: $basicValue.movementMode) { Text("Normal").tag(ASMovementMode.normal) Text("Viscosity").tag(ASMovementMode.viscosity) } label: {} .pickerStyle(.segmented) } } case .ASCapsuleStyle: Group { HStack { Text("Movement Mode") Spacer() Picker(selection: $capsuleValue.movementMode) { Text("Normal").tag(ASMovementMode.normal) Text("Viscosity").tag(ASMovementMode.viscosity) } label: {} .pickerStyle(.segmented) } } case .ASJellyStyle: Group { HStack { Text("Jelly Edge") Spacer() Picker(selection: $jellyValue.jellyEdge) { Text("Bottom/Trailing").tag(ASEdgeMode.bottomTrailing) Text("Top/Leading").tag(ASEdgeMode.topLeading) } label: {} .pickerStyle(.segmented) } } case .ASLineStyle: Group { HStack { Text("Line Edge") Spacer() Picker(selection: $lineValue.lineEdge) { Text("Bottom/Trailing").tag(ASEdgeMode.bottomTrailing) Text("Top/Leading").tag(ASEdgeMode.topLeading) } label: {} .pickerStyle(.segmented) } HStack { Text("Movement Mode") Spacer() Picker(selection: $lineValue.movementMode) { Text("Normal").tag(ASMovementMode.normal) Text("Viscosity").tag(ASMovementMode.viscosity) } label: {} .pickerStyle(.segmented) } } case .ASNeumorphismStyle: Group { HStack { Text("isInner") Spacer() Toggle(isOn: $neumorphismValue.isInner) {} } HStack { Text("Movement Mode") Spacer() Picker(selection: $neumorphismValue.movementMode) { Text("Normal").tag(ASMovementMode.normal) Text("Viscosity").tag(ASMovementMode.viscosity) } label: {} .pickerStyle(.segmented) } } default: EmptyView() } } .padding() } } } struct SegmentedViewWithControl_Previews: PreviewProvider { static var previews: some View { SegmentedViewWithControl(title: "ABBasicStyle", styleType: .ASBasicStyle, axisMode: .horizontal, constant: .constant(ASConstant.init()), tabs: { Group { Image(systemName: "align.horizontal.left") .itemTag(0, selectArea: 0) { SelectionItemView("align.horizontal.left.fill") } Image(systemName: "align.horizontal.right") .itemTag(1, selectArea: 0) { SelectionItemView("align.horizontal.right.fill") } Image(systemName: "align.vertical.top") .itemTag(2, selectArea: 0) { SelectionItemView("align.vertical.top.fill") } Image(systemName: "align.vertical.bottom") .itemTag(3, selectArea: 0) { SelectionItemView("align.vertical.bottom.fill") } } }, style: { ASBasicStyle() }) .padding() .preferredColorScheme(.dark) } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/SelectionItemView.swift ================================================ // // SelectionItemView.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/23. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView struct SelectionItemView: View { @EnvironmentObject private var stateValue: ASStateValue @State private var scale: CGFloat = 1 let iconName: String init(_ iconName: String) { self.iconName = iconName } var body: some View { Image(systemName: iconName) .foregroundColor(Color.white) .scaleEffect(scale) .onAppear { scale = 1 if !stateValue.isInitialRun { withAnimation(.easeInOut(duration: 0.26)) { scale = 1.2 } withAnimation(.easeInOut(duration: 0.26).delay(0.26)) { scale = 1 } } } } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/ViewModel/BacisValue.swift ================================================ // // BacisValue.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/23. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView class BasicValue: ObservableObject { @Published var constant = ASConstant(divideLine: .init(color: Color.white.opacity(0.2), scale: 0.3)) @Published var backgroundColor: Color = .gray.opacity(0.3) @Published var foregroundColor: Color = .black.opacity(0.7) @Published var cornerRadius: CGFloat = 6 @Published var padding: CGFloat = 3 @Published var isApplySelectionCornerRadius: Bool = true @Published var movementMode: ASMovementMode = .viscosity @Published var selectArea0: CGFloat = 0 @Published var selectArea1: CGFloat = 0 @Published var selectArea2: CGFloat = 0 } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/ViewModel/CapsuleValue.swift ================================================ // // CapsuleValue.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/23. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView class CapsuleValue: ObservableObject { @Published var constant = ASConstant(divideLine: .init(color: Color(hex: 0x444444), scale: 0)) @Published var backgroundColor: Color = .gray.opacity(0.4) @Published var foregroundColor: Color = Color.blue @Published var movementMode: ASMovementMode = .viscosity @Published var selectArea0: CGFloat = 0 @Published var selectArea1: CGFloat = 0 @Published var selectArea2: CGFloat = 0 @Published var selectArea3: CGFloat = 0 } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/ViewModel/JellyValue.swift ================================================ // // JellyValue.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/23. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView class JellyValue: ObservableObject { @Published var constant = ASConstant(divideLine: .init(color: Color(hex: 0x444444), scale: 0)) @Published var backgroundColor: Color = .gray.opacity(0.1) @Published var foregroundColor: Color = .purple @Published var jellyRadius: CGFloat = 110 @Published var jellyDepth: CGFloat = 0.9 @Published var jellyEdge: ASEdgeMode = .bottomTrailing @Published var selectArea0: CGFloat = 0 @Published var selectArea1: CGFloat = 0 @Published var selectArea2: CGFloat = 0 @Published var selectArea3: CGFloat = 0 } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/ViewModel/LineValue.swift ================================================ // // LineValue.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/23. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView class LineValue: ObservableObject { @Published var constant = ASConstant(divideLine: .init(color: Color(hex: 0x202020), scale: 0)) @Published var lineColor: Color = .blue @Published var lineSmallWidth: CGFloat = 2 @Published var lineLargeScale: CGFloat = 1.0 @Published var lineEdge: ASEdgeMode = .bottomTrailing @Published var movementMode: ASMovementMode = .viscosity @Published var selectArea0: CGFloat = 0 @Published var selectArea1: CGFloat = 0 @Published var selectArea2: CGFloat = 0 @Published var selectArea3: CGFloat = 0 } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/ViewModel/NeumorphismValue.swift ================================================ // // NeumorphismValue.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/23. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView class NeumorphismValue: ObservableObject { @Published var constant = ASConstant(divideLine: .init(color: Color(hex: 0x444444), scale: 0)) @Published var backgroundColor: Color = .clear @Published var foregroundColor: Color = .clear @Published var cornerRadius: CGFloat = 11 @Published var padding: CGFloat = 12 @Published var shadowRadius: CGFloat = 5 @Published var shadowOpacity: CGFloat = 0.7 @Published var isInner: Bool = false @Published var movementMode: ASMovementMode = .viscosity @Published var selectArea0: CGFloat = 0 @Published var selectArea1: CGFloat = 0 @Published var selectArea2: CGFloat = 0 @Published var selectArea3: CGFloat = 0 init(backgroundColor: Color = Color(hex: 0x31353A), foregroundColor: Color = Color(hex: 0x31353A), cornerRadius: CGFloat = 11, padding: CGFloat = 12, shadowRadius: CGFloat = 5, shadowOpacity: CGFloat = 0.7, isInner: Bool = false, movementMode: ASMovementMode = .viscosity) { self.backgroundColor = backgroundColor self.foregroundColor = foregroundColor self.cornerRadius = cornerRadius self.padding = padding self.shadowRadius = shadowRadius self.isInner = isInner self.movementMode = movementMode } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/ViewModel/NormalValue.swift ================================================ // // NormalValue.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/24. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView class NormalValue: ObservableObject { @Published var constant = ASConstant(divideLine: .init(color: Color(hex: 0x444444), scale: 0)) @Published var selectArea0: CGFloat = 0 @Published var selectArea1: CGFloat = 0 } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/ViewModel/ScaleValue.swift ================================================ // // ScaleValue.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/23. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView class ScaleValue: ObservableObject { @Published var constant = ASConstant(divideLine: .init(color: Color(hex: 0x444444), scale: 0)) @Published var backgroundColor: Color = .clear @Published var foregroundColor: Color = Color.blue @Published var cornerRadius: CGFloat = 11 @Published var minimumScale: CGFloat = 0.1 @Published var selectArea0: CGFloat = 0 @Published var selectArea1: CGFloat = 0 @Published var selectArea2: CGFloat = 0 @Published var selectArea3: CGFloat = 0 } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/ViewModel/ViscosityValue.swift ================================================ // // ViscosityValue.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/24. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView class ViscosityValue: ObservableObject { @Published var constant = ASConstant(divideLine: .init(color: Color(hex: 0x444444), scale: 0)) @Published var selectArea0: CGFloat = 0 @Published var selectArea1: CGFloat = 0 @Published var selectArea2: CGFloat = 0 } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample (tvOS)/WithoutStyleView.swift ================================================ // // WithoutStyleView.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/23. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView struct WithoutStyleView: View { @State private var selection: Int = 0 @State private var constant = ASConstant(axisMode: .horizontal) var body: some View { AxisSegmentedView(selection: $selection, constant: constant) { TabViews() } } } struct TabViews: View { @State private var maxArea1: CGFloat = 600 @EnvironmentObject private var stateValue: ASStateValue let colors = [Color(hex: 0x295A76), Color(hex: 0x7FACAA), Color(hex: 0xEBF4CC), Color(hex: 0xE79875), Color(hex: 0xBA523C), Color(hex: 0x295A76)] var listView: some View { List(0...100, id: \.self) { index in Button { print("click") } label: { Text("Index \(index)") } }.listStyle(.plain) } var body: some View { Group { Rectangle() .fill(colors[0]) .overlay( Text("0") ) .itemTag(0, selectArea: maxArea1) { Circle() .fill(.red) .overlay( Text("0") ) } Rectangle() .fill(colors[1]) .overlay( Text("1") ) .itemTag(1, selectArea: maxArea1) { listView } Rectangle() .fill(colors[2]) .overlay( Text("2") ) .itemTag(2, selectArea: maxArea1) { listView } Rectangle() .fill(colors[3]) .overlay( Text("3") ) .itemTag(3, selectArea: maxArea1) { listView } } } } struct WithoutStyleView_Previews: PreviewProvider { static var previews: some View { WithoutStyleView() .preferredColorScheme(.dark) } } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 55; objects = { /* Begin PBXBuildFile section */ 4B0EE0EA27E4B84E005B804E /* AxisSegmentedViewExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B0EE0DA27E4B84D005B804E /* AxisSegmentedViewExampleApp.swift */; }; 4B0EE0EB27E4B84E005B804E /* AxisSegmentedViewExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B0EE0DA27E4B84D005B804E /* AxisSegmentedViewExampleApp.swift */; }; 4B0EE0EC27E4B84E005B804E /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B0EE0DB27E4B84D005B804E /* ContentView.swift */; }; 4B0EE0ED27E4B84E005B804E /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B0EE0DB27E4B84D005B804E /* ContentView.swift */; }; 4B0EE0EE27E4B84E005B804E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4B0EE0DC27E4B84E005B804E /* Assets.xcassets */; }; 4B0EE0EF27E4B84E005B804E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4B0EE0DC27E4B84E005B804E /* Assets.xcassets */; }; 4B0EE0FC27E4BE88005B804E /* AxisSegmentedView in Frameworks */ = {isa = PBXBuildFile; productRef = 4B0EE0FB27E4BE88005B804E /* AxisSegmentedView */; }; 4B0EE0FE27E4BE8C005B804E /* AxisSegmentedView in Frameworks */ = {isa = PBXBuildFile; productRef = 4B0EE0FD27E4BE8B005B804E /* AxisSegmentedView */; }; 4B4D2CDA27EFFD6800EE251F /* AxisSegmentedViewExample__tvOS_App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D2CD927EFFD6800EE251F /* AxisSegmentedViewExample__tvOS_App.swift */; }; 4B4D2CDC27EFFD6800EE251F /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D2CDB27EFFD6800EE251F /* ContentView.swift */; }; 4B4D2CDE27EFFD6900EE251F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4B4D2CDD27EFFD6900EE251F /* Assets.xcassets */; }; 4B4D2CE127EFFD6900EE251F /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4B4D2CE027EFFD6900EE251F /* Preview Assets.xcassets */; }; 4B4D2CE627EFFE3700EE251F /* AxisSegmentedView in Frameworks */ = {isa = PBXBuildFile; productRef = 4B4D2CE527EFFE3700EE251F /* AxisSegmentedView */; }; 4B4D2CE727EFFE7200EE251F /* Color+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB6EC27EAFEBB00663DED /* Color+Extensions.swift */; }; 4B4D2D0227F0043F00EE251F /* SegmentedListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D2CFE27F0043F00EE251F /* SegmentedListView.swift */; }; 4B4D2D0327F0043F00EE251F /* CustomStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D2D0027F0043F00EE251F /* CustomStyle.swift */; }; 4B4D2D0427F0043F00EE251F /* WithoutStyleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D2CFF27F0043F00EE251F /* WithoutStyleView.swift */; }; 4B4D2D0527F0043F00EE251F /* SegmentedViewWithControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D2CFD27F0043F00EE251F /* SegmentedViewWithControl.swift */; }; 4B4D2D0627F0043F00EE251F /* SelectionItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D2D0127F0043F00EE251F /* SelectionItemView.swift */; }; 4B4D2D0727F0043F00EE251F /* CustomStyleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D2CFC27F0043F00EE251F /* CustomStyleView.swift */; }; 4B4D2D1127F004B600EE251F /* SelectionItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB6E927EAEF6D00663DED /* SelectionItemView.swift */; }; 4B4D2D1227F004B600EE251F /* SelectionItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB6E927EAEF6D00663DED /* SelectionItemView.swift */; }; 4B4D2D1327F004C000EE251F /* SegmentedListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB6E327EAEECD00663DED /* SegmentedListView.swift */; }; 4B4D2D1427F004C000EE251F /* SegmentedListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB6E327EAEECD00663DED /* SegmentedListView.swift */; }; 4B4D2D1527F004C300EE251F /* SegmentedViewWithControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB6E627EAEF2000663DED /* SegmentedViewWithControl.swift */; }; 4B4D2D1627F004C300EE251F /* SegmentedViewWithControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB6E627EAEF2000663DED /* SegmentedViewWithControl.swift */; }; 4B4D2D1727F004C600EE251F /* WithoutStyleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB70227EB69EA00663DED /* WithoutStyleView.swift */; }; 4B4D2D1827F004C600EE251F /* WithoutStyleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB70227EB69EA00663DED /* WithoutStyleView.swift */; }; 4B4D2D1927F004C900EE251F /* CustomStyleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4A605827EF6052008D69AF /* CustomStyleView.swift */; }; 4B4D2D1A27F004C900EE251F /* CustomStyleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4A605827EF6052008D69AF /* CustomStyleView.swift */; }; 4B4D2D1B27F004CC00EE251F /* CustomStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3D010A27EE014F000A8506 /* CustomStyle.swift */; }; 4B4D2D1C27F004CC00EE251F /* CustomStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3D010A27EE014F000A8506 /* CustomStyle.swift */; }; 4B4D2D1D27F0051900EE251F /* BacisValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D2D0E27F0045200EE251F /* BacisValue.swift */; }; 4B4D2D1E27F0052300EE251F /* NormalValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D2D0A27F0045200EE251F /* NormalValue.swift */; }; 4B4D2D1F27F0052500EE251F /* ViscosityValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D2D0B27F0045200EE251F /* ViscosityValue.swift */; }; 4B4D2D2027F0052800EE251F /* CapsuleValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D2D1027F0045200EE251F /* CapsuleValue.swift */; }; 4B4D2D2127F0052A00EE251F /* JellyValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D2D0F27F0045200EE251F /* JellyValue.swift */; }; 4B4D2D2227F0052C00EE251F /* LineValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D2D0927F0045200EE251F /* LineValue.swift */; }; 4B4D2D2327F0052E00EE251F /* NeumorphismValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D2D0C27F0045200EE251F /* NeumorphismValue.swift */; }; 4B4D2D2427F0053100EE251F /* ScaleValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D2D0D27F0045200EE251F /* ScaleValue.swift */; }; 4BA1E34F27EC28D0002DE9F3 /* NormalValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BA1E34E27EC28D0002DE9F3 /* NormalValue.swift */; }; 4BA1E35027EC28D0002DE9F3 /* NormalValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BA1E34E27EC28D0002DE9F3 /* NormalValue.swift */; }; 4BA1E35227EC2903002DE9F3 /* ViscosityValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BA1E35127EC2903002DE9F3 /* ViscosityValue.swift */; }; 4BA1E35327EC2903002DE9F3 /* ViscosityValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BA1E35127EC2903002DE9F3 /* ViscosityValue.swift */; }; 4BFBB6ED27EAFEBB00663DED /* Color+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB6EC27EAFEBB00663DED /* Color+Extensions.swift */; }; 4BFBB6EE27EAFEBB00663DED /* Color+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB6EC27EAFEBB00663DED /* Color+Extensions.swift */; }; 4BFBB6F127EB4EDA00663DED /* BacisValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB6F027EB4EDA00663DED /* BacisValue.swift */; }; 4BFBB6F227EB4EDA00663DED /* BacisValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB6F027EB4EDA00663DED /* BacisValue.swift */; }; 4BFBB6F427EB4F4300663DED /* CapsuleValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB6F327EB4F4300663DED /* CapsuleValue.swift */; }; 4BFBB6F527EB4F4300663DED /* CapsuleValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB6F327EB4F4300663DED /* CapsuleValue.swift */; }; 4BFBB6F727EB4F8B00663DED /* JellyValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB6F627EB4F8B00663DED /* JellyValue.swift */; }; 4BFBB6F827EB4F8B00663DED /* JellyValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB6F627EB4F8B00663DED /* JellyValue.swift */; }; 4BFBB6FA27EB4FCC00663DED /* LineValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB6F927EB4FCC00663DED /* LineValue.swift */; }; 4BFBB6FB27EB4FCC00663DED /* LineValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB6F927EB4FCC00663DED /* LineValue.swift */; }; 4BFBB6FD27EB501000663DED /* NeumorphismValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB6FC27EB501000663DED /* NeumorphismValue.swift */; }; 4BFBB6FE27EB501000663DED /* NeumorphismValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB6FC27EB501000663DED /* NeumorphismValue.swift */; }; 4BFBB70027EB504300663DED /* ScaleValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB6FF27EB504300663DED /* ScaleValue.swift */; }; 4BFBB70127EB504300663DED /* ScaleValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFBB6FF27EB504300663DED /* ScaleValue.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 4B0EE0DA27E4B84D005B804E /* AxisSegmentedViewExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AxisSegmentedViewExampleApp.swift; sourceTree = ""; }; 4B0EE0DB27E4B84D005B804E /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 4B0EE0DC27E4B84E005B804E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 4B0EE0E127E4B84E005B804E /* AxisSegmentedViewExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AxisSegmentedViewExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 4B0EE0E727E4B84E005B804E /* AxisSegmentedViewExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AxisSegmentedViewExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 4B0EE0E927E4B84E005B804E /* macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = macOS.entitlements; sourceTree = ""; }; 4B0EE0F927E4B875005B804E /* AxisSegmentedView */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = AxisSegmentedView; path = ..; sourceTree = ""; }; 4B3D010A27EE014F000A8506 /* CustomStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomStyle.swift; sourceTree = ""; }; 4B4A605827EF6052008D69AF /* CustomStyleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomStyleView.swift; sourceTree = ""; }; 4B4D2CD727EFFD6800EE251F /* AxisSegmentedViewExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AxisSegmentedViewExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 4B4D2CD927EFFD6800EE251F /* AxisSegmentedViewExample__tvOS_App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AxisSegmentedViewExample__tvOS_App.swift; sourceTree = ""; }; 4B4D2CDB27EFFD6800EE251F /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 4B4D2CDD27EFFD6900EE251F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 4B4D2CE027EFFD6900EE251F /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 4B4D2CFC27F0043F00EE251F /* CustomStyleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomStyleView.swift; sourceTree = ""; }; 4B4D2CFD27F0043F00EE251F /* SegmentedViewWithControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentedViewWithControl.swift; sourceTree = ""; }; 4B4D2CFE27F0043F00EE251F /* SegmentedListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentedListView.swift; sourceTree = ""; }; 4B4D2CFF27F0043F00EE251F /* WithoutStyleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithoutStyleView.swift; sourceTree = ""; }; 4B4D2D0027F0043F00EE251F /* CustomStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomStyle.swift; sourceTree = ""; }; 4B4D2D0127F0043F00EE251F /* SelectionItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionItemView.swift; sourceTree = ""; }; 4B4D2D0927F0045200EE251F /* LineValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineValue.swift; sourceTree = ""; }; 4B4D2D0A27F0045200EE251F /* NormalValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NormalValue.swift; sourceTree = ""; }; 4B4D2D0B27F0045200EE251F /* ViscosityValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViscosityValue.swift; sourceTree = ""; }; 4B4D2D0C27F0045200EE251F /* NeumorphismValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NeumorphismValue.swift; sourceTree = ""; }; 4B4D2D0D27F0045200EE251F /* ScaleValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScaleValue.swift; sourceTree = ""; }; 4B4D2D0E27F0045200EE251F /* BacisValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BacisValue.swift; sourceTree = ""; }; 4B4D2D0F27F0045200EE251F /* JellyValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JellyValue.swift; sourceTree = ""; }; 4B4D2D1027F0045200EE251F /* CapsuleValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapsuleValue.swift; sourceTree = ""; }; 4BA1E34E27EC28D0002DE9F3 /* NormalValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NormalValue.swift; sourceTree = ""; }; 4BA1E35127EC2903002DE9F3 /* ViscosityValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViscosityValue.swift; sourceTree = ""; }; 4BFBB6E327EAEECD00663DED /* SegmentedListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentedListView.swift; sourceTree = ""; }; 4BFBB6E627EAEF2000663DED /* SegmentedViewWithControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentedViewWithControl.swift; sourceTree = ""; }; 4BFBB6E927EAEF6D00663DED /* SelectionItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionItemView.swift; sourceTree = ""; }; 4BFBB6EC27EAFEBB00663DED /* Color+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Color+Extensions.swift"; sourceTree = ""; }; 4BFBB6F027EB4EDA00663DED /* BacisValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BacisValue.swift; sourceTree = ""; }; 4BFBB6F327EB4F4300663DED /* CapsuleValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CapsuleValue.swift; sourceTree = ""; }; 4BFBB6F627EB4F8B00663DED /* JellyValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JellyValue.swift; sourceTree = ""; }; 4BFBB6F927EB4FCC00663DED /* LineValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineValue.swift; sourceTree = ""; }; 4BFBB6FC27EB501000663DED /* NeumorphismValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NeumorphismValue.swift; sourceTree = ""; }; 4BFBB6FF27EB504300663DED /* ScaleValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScaleValue.swift; sourceTree = ""; }; 4BFBB70227EB69EA00663DED /* WithoutStyleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithoutStyleView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 4B0EE0DE27E4B84E005B804E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 4B0EE0FC27E4BE88005B804E /* AxisSegmentedView in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 4B0EE0E427E4B84E005B804E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 4B0EE0FE27E4BE8C005B804E /* AxisSegmentedView in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; 4B4D2CD427EFFD6800EE251F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 4B4D2CE627EFFE3700EE251F /* AxisSegmentedView in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 4B0EE0D427E4B84D005B804E = { isa = PBXGroup; children = ( 4B0EE0F827E4B875005B804E /* Packages */, 4B0EE0D927E4B84D005B804E /* Shared */, 4B0EE0E827E4B84E005B804E /* macOS */, 4B4D2CD827EFFD6800EE251F /* AxisSegmentedViewExample (tvOS) */, 4B0EE0E227E4B84E005B804E /* Products */, 4B0EE0FA27E4BE88005B804E /* Frameworks */, ); sourceTree = ""; }; 4B0EE0D927E4B84D005B804E /* Shared */ = { isa = PBXGroup; children = ( 4B0EE0DA27E4B84D005B804E /* AxisSegmentedViewExampleApp.swift */, 4BFBB6EC27EAFEBB00663DED /* Color+Extensions.swift */, 4B0EE0DB27E4B84D005B804E /* ContentView.swift */, 4BFBB6E927EAEF6D00663DED /* SelectionItemView.swift */, 4BFBB6E327EAEECD00663DED /* SegmentedListView.swift */, 4BFBB6E627EAEF2000663DED /* SegmentedViewWithControl.swift */, 4BFBB70227EB69EA00663DED /* WithoutStyleView.swift */, 4B4A605827EF6052008D69AF /* CustomStyleView.swift */, 4B3D010A27EE014F000A8506 /* CustomStyle.swift */, 4BFBB6EF27EB4EC100663DED /* ViewModel */, 4B0EE0DC27E4B84E005B804E /* Assets.xcassets */, ); path = Shared; sourceTree = ""; }; 4B0EE0E227E4B84E005B804E /* Products */ = { isa = PBXGroup; children = ( 4B0EE0E127E4B84E005B804E /* AxisSegmentedViewExample.app */, 4B0EE0E727E4B84E005B804E /* AxisSegmentedViewExample.app */, 4B4D2CD727EFFD6800EE251F /* AxisSegmentedViewExample.app */, ); name = Products; sourceTree = ""; }; 4B0EE0E827E4B84E005B804E /* macOS */ = { isa = PBXGroup; children = ( 4B0EE0E927E4B84E005B804E /* macOS.entitlements */, ); path = macOS; sourceTree = ""; }; 4B0EE0F827E4B875005B804E /* Packages */ = { isa = PBXGroup; children = ( 4B0EE0F927E4B875005B804E /* AxisSegmentedView */, ); name = Packages; sourceTree = ""; }; 4B0EE0FA27E4BE88005B804E /* Frameworks */ = { isa = PBXGroup; children = ( ); name = Frameworks; sourceTree = ""; }; 4B4D2CD827EFFD6800EE251F /* AxisSegmentedViewExample (tvOS) */ = { isa = PBXGroup; children = ( 4B4D2CD927EFFD6800EE251F /* AxisSegmentedViewExample__tvOS_App.swift */, 4B4D2CDB27EFFD6800EE251F /* ContentView.swift */, 4B4D2D0827F0044C00EE251F /* ViewModel */, 4B4D2D0127F0043F00EE251F /* SelectionItemView.swift */, 4B4D2CFE27F0043F00EE251F /* SegmentedListView.swift */, 4B4D2CFD27F0043F00EE251F /* SegmentedViewWithControl.swift */, 4B4D2CFF27F0043F00EE251F /* WithoutStyleView.swift */, 4B4D2CFC27F0043F00EE251F /* CustomStyleView.swift */, 4B4D2D0027F0043F00EE251F /* CustomStyle.swift */, 4B4D2CDD27EFFD6900EE251F /* Assets.xcassets */, 4B4D2CDF27EFFD6900EE251F /* Preview Content */, ); path = "AxisSegmentedViewExample (tvOS)"; sourceTree = ""; }; 4B4D2CDF27EFFD6900EE251F /* Preview Content */ = { isa = PBXGroup; children = ( 4B4D2CE027EFFD6900EE251F /* Preview Assets.xcassets */, ); path = "Preview Content"; sourceTree = ""; }; 4B4D2D0827F0044C00EE251F /* ViewModel */ = { isa = PBXGroup; children = ( 4B4D2D0E27F0045200EE251F /* BacisValue.swift */, 4B4D2D0A27F0045200EE251F /* NormalValue.swift */, 4B4D2D0B27F0045200EE251F /* ViscosityValue.swift */, 4B4D2D1027F0045200EE251F /* CapsuleValue.swift */, 4B4D2D0F27F0045200EE251F /* JellyValue.swift */, 4B4D2D0927F0045200EE251F /* LineValue.swift */, 4B4D2D0C27F0045200EE251F /* NeumorphismValue.swift */, 4B4D2D0D27F0045200EE251F /* ScaleValue.swift */, ); path = ViewModel; sourceTree = ""; }; 4BFBB6EF27EB4EC100663DED /* ViewModel */ = { isa = PBXGroup; children = ( 4BFBB6F027EB4EDA00663DED /* BacisValue.swift */, 4BA1E34E27EC28D0002DE9F3 /* NormalValue.swift */, 4BA1E35127EC2903002DE9F3 /* ViscosityValue.swift */, 4BFBB6F327EB4F4300663DED /* CapsuleValue.swift */, 4BFBB6F627EB4F8B00663DED /* JellyValue.swift */, 4BFBB6F927EB4FCC00663DED /* LineValue.swift */, 4BFBB6FC27EB501000663DED /* NeumorphismValue.swift */, 4BFBB6FF27EB504300663DED /* ScaleValue.swift */, ); path = ViewModel; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 4B0EE0E027E4B84E005B804E /* AxisSegmentedViewExample (iOS) */ = { isa = PBXNativeTarget; buildConfigurationList = 4B0EE0F227E4B84E005B804E /* Build configuration list for PBXNativeTarget "AxisSegmentedViewExample (iOS)" */; buildPhases = ( 4B0EE0DD27E4B84E005B804E /* Sources */, 4B0EE0DE27E4B84E005B804E /* Frameworks */, 4B0EE0DF27E4B84E005B804E /* Resources */, ); buildRules = ( ); dependencies = ( ); name = "AxisSegmentedViewExample (iOS)"; packageProductDependencies = ( 4B0EE0FB27E4BE88005B804E /* AxisSegmentedView */, ); productName = "AxisSegmentedViewExample (iOS)"; productReference = 4B0EE0E127E4B84E005B804E /* AxisSegmentedViewExample.app */; productType = "com.apple.product-type.application"; }; 4B0EE0E627E4B84E005B804E /* AxisSegmentedViewExample (macOS) */ = { isa = PBXNativeTarget; buildConfigurationList = 4B0EE0F527E4B84E005B804E /* Build configuration list for PBXNativeTarget "AxisSegmentedViewExample (macOS)" */; buildPhases = ( 4B0EE0E327E4B84E005B804E /* Sources */, 4B0EE0E427E4B84E005B804E /* Frameworks */, 4B0EE0E527E4B84E005B804E /* Resources */, ); buildRules = ( ); dependencies = ( ); name = "AxisSegmentedViewExample (macOS)"; packageProductDependencies = ( 4B0EE0FD27E4BE8B005B804E /* AxisSegmentedView */, ); productName = "AxisSegmentedViewExample (macOS)"; productReference = 4B0EE0E727E4B84E005B804E /* AxisSegmentedViewExample.app */; productType = "com.apple.product-type.application"; }; 4B4D2CD627EFFD6800EE251F /* AxisSegmentedViewExample (tvOS) */ = { isa = PBXNativeTarget; buildConfigurationList = 4B4D2CE227EFFD6900EE251F /* Build configuration list for PBXNativeTarget "AxisSegmentedViewExample (tvOS)" */; buildPhases = ( 4B4D2CD327EFFD6800EE251F /* Sources */, 4B4D2CD427EFFD6800EE251F /* Frameworks */, 4B4D2CD527EFFD6800EE251F /* Resources */, ); buildRules = ( ); dependencies = ( ); name = "AxisSegmentedViewExample (tvOS)"; packageProductDependencies = ( 4B4D2CE527EFFE3700EE251F /* AxisSegmentedView */, ); productName = "AxisSegmentedViewExample (tvOS)"; productReference = 4B4D2CD727EFFD6800EE251F /* AxisSegmentedViewExample.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 4B0EE0D527E4B84D005B804E /* Project object */ = { isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1330; LastUpgradeCheck = 1330; TargetAttributes = { 4B0EE0E027E4B84E005B804E = { CreatedOnToolsVersion = 13.3; }; 4B0EE0E627E4B84E005B804E = { CreatedOnToolsVersion = 13.3; }; 4B4D2CD627EFFD6800EE251F = { CreatedOnToolsVersion = 13.3; }; }; }; buildConfigurationList = 4B0EE0D827E4B84D005B804E /* Build configuration list for PBXProject "AxisSegmentedViewExample" */; compatibilityVersion = "Xcode 13.0"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, Base, ); mainGroup = 4B0EE0D427E4B84D005B804E; productRefGroup = 4B0EE0E227E4B84E005B804E /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 4B0EE0E027E4B84E005B804E /* AxisSegmentedViewExample (iOS) */, 4B0EE0E627E4B84E005B804E /* AxisSegmentedViewExample (macOS) */, 4B4D2CD627EFFD6800EE251F /* AxisSegmentedViewExample (tvOS) */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 4B0EE0DF27E4B84E005B804E /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 4B0EE0EE27E4B84E005B804E /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 4B0EE0E527E4B84E005B804E /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 4B0EE0EF27E4B84E005B804E /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; 4B4D2CD527EFFD6800EE251F /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 4B4D2CE127EFFD6900EE251F /* Preview Assets.xcassets in Resources */, 4B4D2CDE27EFFD6900EE251F /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 4B0EE0DD27E4B84E005B804E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 4BFBB6F727EB4F8B00663DED /* JellyValue.swift in Sources */, 4BFBB6F127EB4EDA00663DED /* BacisValue.swift in Sources */, 4B4D2D1B27F004CC00EE251F /* CustomStyle.swift in Sources */, 4B4D2D1127F004B600EE251F /* SelectionItemView.swift in Sources */, 4B0EE0EC27E4B84E005B804E /* ContentView.swift in Sources */, 4B0EE0EA27E4B84E005B804E /* AxisSegmentedViewExampleApp.swift in Sources */, 4B4D2D1527F004C300EE251F /* SegmentedViewWithControl.swift in Sources */, 4BFBB6FD27EB501000663DED /* NeumorphismValue.swift in Sources */, 4B4D2D1927F004C900EE251F /* CustomStyleView.swift in Sources */, 4BFBB70027EB504300663DED /* ScaleValue.swift in Sources */, 4B4D2D1327F004C000EE251F /* SegmentedListView.swift in Sources */, 4BA1E34F27EC28D0002DE9F3 /* NormalValue.swift in Sources */, 4BFBB6FA27EB4FCC00663DED /* LineValue.swift in Sources */, 4BFBB6ED27EAFEBB00663DED /* Color+Extensions.swift in Sources */, 4B4D2D1727F004C600EE251F /* WithoutStyleView.swift in Sources */, 4BFBB6F427EB4F4300663DED /* CapsuleValue.swift in Sources */, 4BA1E35227EC2903002DE9F3 /* ViscosityValue.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 4B0EE0E327E4B84E005B804E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 4BFBB6F827EB4F8B00663DED /* JellyValue.swift in Sources */, 4BFBB6F227EB4EDA00663DED /* BacisValue.swift in Sources */, 4B4D2D1C27F004CC00EE251F /* CustomStyle.swift in Sources */, 4B4D2D1227F004B600EE251F /* SelectionItemView.swift in Sources */, 4B0EE0ED27E4B84E005B804E /* ContentView.swift in Sources */, 4B0EE0EB27E4B84E005B804E /* AxisSegmentedViewExampleApp.swift in Sources */, 4B4D2D1627F004C300EE251F /* SegmentedViewWithControl.swift in Sources */, 4BFBB6FE27EB501000663DED /* NeumorphismValue.swift in Sources */, 4B4D2D1A27F004C900EE251F /* CustomStyleView.swift in Sources */, 4BFBB70127EB504300663DED /* ScaleValue.swift in Sources */, 4B4D2D1427F004C000EE251F /* SegmentedListView.swift in Sources */, 4BA1E35027EC28D0002DE9F3 /* NormalValue.swift in Sources */, 4BFBB6FB27EB4FCC00663DED /* LineValue.swift in Sources */, 4BFBB6EE27EAFEBB00663DED /* Color+Extensions.swift in Sources */, 4B4D2D1827F004C600EE251F /* WithoutStyleView.swift in Sources */, 4BFBB6F527EB4F4300663DED /* CapsuleValue.swift in Sources */, 4BA1E35327EC2903002DE9F3 /* ViscosityValue.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; 4B4D2CD327EFFD6800EE251F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 4B4D2D1D27F0051900EE251F /* BacisValue.swift in Sources */, 4B4D2D0327F0043F00EE251F /* CustomStyle.swift in Sources */, 4B4D2D2227F0052C00EE251F /* LineValue.swift in Sources */, 4B4D2CDC27EFFD6800EE251F /* ContentView.swift in Sources */, 4B4D2D0527F0043F00EE251F /* SegmentedViewWithControl.swift in Sources */, 4B4D2D2327F0052E00EE251F /* NeumorphismValue.swift in Sources */, 4B4D2CDA27EFFD6800EE251F /* AxisSegmentedViewExample__tvOS_App.swift in Sources */, 4B4D2D1F27F0052500EE251F /* ViscosityValue.swift in Sources */, 4B4D2D2427F0053100EE251F /* ScaleValue.swift in Sources */, 4B4D2D0627F0043F00EE251F /* SelectionItemView.swift in Sources */, 4B4D2D2027F0052800EE251F /* CapsuleValue.swift in Sources */, 4B4D2D0427F0043F00EE251F /* WithoutStyleView.swift in Sources */, 4B4D2D0727F0043F00EE251F /* CustomStyleView.swift in Sources */, 4B4D2CE727EFFE7200EE251F /* Color+Extensions.swift in Sources */, 4B4D2D0227F0043F00EE251F /* SegmentedListView.swift in Sources */, 4B4D2D2127F0052A00EE251F /* JellyValue.swift in Sources */, 4B4D2D1E27F0052300EE251F /* NormalValue.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 4B0EE0F027E4B84E005B804E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 4B0EE0F127E4B84E005B804E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; }; name = Release; }; 4B0EE0F327E4B84E005B804E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = SM6445X39C; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.devstore.AxisSegmentedViewExample; PRODUCT_NAME = AxisSegmentedViewExample; SDKROOT = iphoneos; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; 4B0EE0F427E4B84E005B804E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = SM6445X39C; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.devstore.AxisSegmentedViewExample; PRODUCT_NAME = AxisSegmentedViewExample; SDKROOT = iphoneos; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; name = Release; }; 4B0EE0F627E4B84E005B804E /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = SM6445X39C; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.devstore.AxisSegmentedViewExample; PRODUCT_NAME = AxisSegmentedViewExample; SDKROOT = macosx; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; }; name = Debug; }; 4B0EE0F727E4B84E005B804E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = SM6445X39C; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.devstore.AxisSegmentedViewExample; PRODUCT_NAME = AxisSegmentedViewExample; SDKROOT = macosx; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; }; name = Release; }; 4B4D2CE327EFFD6900EE251F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"AxisSegmentedViewExample (tvOS)/Preview Content\""; DEVELOPMENT_TEAM = SM6445X39C; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UIUserInterfaceStyle = Automatic; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.devstore.AxisSegmentedViewExample; PRODUCT_NAME = AxisSegmentedViewExample; SDKROOT = appletvos; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 15.4; }; name = Debug; }; 4B4D2CE427EFFD6900EE251F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"AxisSegmentedViewExample (tvOS)/Preview Content\""; DEVELOPMENT_TEAM = SM6445X39C; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UIUserInterfaceStyle = Automatic; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.devstore.AxisSegmentedViewExample; PRODUCT_NAME = AxisSegmentedViewExample; SDKROOT = appletvos; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 15.4; VALIDATE_PRODUCT = YES; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 4B0EE0D827E4B84D005B804E /* Build configuration list for PBXProject "AxisSegmentedViewExample" */ = { isa = XCConfigurationList; buildConfigurations = ( 4B0EE0F027E4B84E005B804E /* Debug */, 4B0EE0F127E4B84E005B804E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 4B0EE0F227E4B84E005B804E /* Build configuration list for PBXNativeTarget "AxisSegmentedViewExample (iOS)" */ = { isa = XCConfigurationList; buildConfigurations = ( 4B0EE0F327E4B84E005B804E /* Debug */, 4B0EE0F427E4B84E005B804E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 4B0EE0F527E4B84E005B804E /* Build configuration list for PBXNativeTarget "AxisSegmentedViewExample (macOS)" */ = { isa = XCConfigurationList; buildConfigurations = ( 4B0EE0F627E4B84E005B804E /* Debug */, 4B0EE0F727E4B84E005B804E /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; 4B4D2CE227EFFD6900EE251F /* Build configuration list for PBXNativeTarget "AxisSegmentedViewExample (tvOS)" */ = { isa = XCConfigurationList; buildConfigurations = ( 4B4D2CE327EFFD6900EE251F /* Debug */, 4B4D2CE427EFFD6900EE251F /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ /* Begin XCSwiftPackageProductDependency section */ 4B0EE0FB27E4BE88005B804E /* AxisSegmentedView */ = { isa = XCSwiftPackageProductDependency; productName = AxisSegmentedView; }; 4B0EE0FD27E4BE8B005B804E /* AxisSegmentedView */ = { isa = XCSwiftPackageProductDependency; productName = AxisSegmentedView; }; 4B4D2CE527EFFE3700EE251F /* AxisSegmentedView */ = { isa = XCSwiftPackageProductDependency; productName = AxisSegmentedView; }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 4B0EE0D527E4B84D005B804E /* Project object */; } ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist ================================================ IDEDidComputeMac32BitWarning ================================================ FILE: AxisSegmentedViewExample/AxisSegmentedViewExample.xcodeproj/xcshareddata/xcschemes/AxisSegmentedView.xcscheme ================================================ ================================================ FILE: AxisSegmentedViewExample/Shared/Assets.xcassets/AccentColor.colorset/Contents.json ================================================ { "colors" : [ { "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: AxisSegmentedViewExample/Shared/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "iphone", "scale" : "2x", "size" : "20x20" }, { "idiom" : "iphone", "scale" : "3x", "size" : "20x20" }, { "idiom" : "iphone", "scale" : "2x", "size" : "29x29" }, { "idiom" : "iphone", "scale" : "3x", "size" : "29x29" }, { "idiom" : "iphone", "scale" : "2x", "size" : "40x40" }, { "idiom" : "iphone", "scale" : "3x", "size" : "40x40" }, { "idiom" : "iphone", "scale" : "2x", "size" : "60x60" }, { "idiom" : "iphone", "scale" : "3x", "size" : "60x60" }, { "idiom" : "ipad", "scale" : "1x", "size" : "20x20" }, { "idiom" : "ipad", "scale" : "2x", "size" : "20x20" }, { "idiom" : "ipad", "scale" : "1x", "size" : "29x29" }, { "idiom" : "ipad", "scale" : "2x", "size" : "29x29" }, { "idiom" : "ipad", "scale" : "1x", "size" : "40x40" }, { "idiom" : "ipad", "scale" : "2x", "size" : "40x40" }, { "idiom" : "ipad", "scale" : "1x", "size" : "76x76" }, { "idiom" : "ipad", "scale" : "2x", "size" : "76x76" }, { "idiom" : "ipad", "scale" : "2x", "size" : "83.5x83.5" }, { "idiom" : "ios-marketing", "scale" : "1x", "size" : "1024x1024" }, { "idiom" : "mac", "scale" : "1x", "size" : "16x16" }, { "idiom" : "mac", "scale" : "2x", "size" : "16x16" }, { "idiom" : "mac", "scale" : "1x", "size" : "32x32" }, { "idiom" : "mac", "scale" : "2x", "size" : "32x32" }, { "idiom" : "mac", "scale" : "1x", "size" : "128x128" }, { "idiom" : "mac", "scale" : "2x", "size" : "128x128" }, { "idiom" : "mac", "scale" : "1x", "size" : "256x256" }, { "idiom" : "mac", "scale" : "2x", "size" : "256x256" }, { "idiom" : "mac", "scale" : "1x", "size" : "512x512" }, { "idiom" : "mac", "scale" : "2x", "size" : "512x512" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: AxisSegmentedViewExample/Shared/Assets.xcassets/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: AxisSegmentedViewExample/Shared/Assets.xcassets/image.imageset/Contents.json ================================================ { "images" : [ { "filename" : "olimpia-campean-IkhxVv5Mkn4-unsplash.jpg", "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 }, "properties" : { "template-rendering-intent" : "original" } } ================================================ FILE: AxisSegmentedViewExample/Shared/AxisSegmentedViewExampleApp.swift ================================================ // // AxisSegmentedViewExampleApp.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/18. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI @main struct AxisSegmentedViewExampleApp: App { var body: some Scene { WindowGroup { ContentView() } } } ================================================ FILE: AxisSegmentedViewExample/Shared/Color+Extensions.swift ================================================ // // Color+Extensions.swift // AxisSegmentedViewExample // // Created by jasu on 2021/12/08. // Copyright (c) 2021 jasu All rights reserved. // import SwiftUI public extension Color { init(hex: UInt, alpha: Double = 1) { self.init( .sRGB, red: Double((hex >> 16) & 0xff) / 255, green: Double((hex >> 08) & 0xff) / 255, blue: Double((hex >> 00) & 0xff) / 255, opacity: alpha ) } init(hex: String) { let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) var int: UInt64 = 0 Scanner(string: hex).scanHexInt64(&int) let a, r, g, b: UInt64 switch hex.count { case 3: // RGB (12-bit) (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) case 6: // RGB (24-bit) (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) case 8: // ARGB (32-bit) (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) default: (a, r, g, b) = (1, 1, 1, 0) } self.init( .sRGB, red: Double(r) / 255, green: Double(g) / 255, blue: Double(b) / 255, opacity: Double(a) / 255 ) } static var random: Color { return Color( red: .random(in: 0...1), green: .random(in: 0...1), blue: .random(in: 0...1) ) } } ================================================ FILE: AxisSegmentedViewExample/Shared/ContentView.swift ================================================ // // ContentView.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/18. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView struct ContentView: View { @State private var tabViewSelection: Int = 0 private var content: some View { TabView(selection: $tabViewSelection) { SegmentedListView(axisMode: .horizontal) .tag(0) .tabItem { Image(systemName: "rectangle.arrowtriangle.2.inward") Text("Horizontal") } SegmentedListView(axisMode: .vertical) .tag(1) .tabItem { Image(systemName: "rectangle.portrait.arrowtriangle.2.inward") Text("Vertical") } WithoutStyleView() .tag(2) .tabItem { Image(systemName: "cpu") Text("Without style") } CustomStyleView() .tag(3) .tabItem { Image(systemName: "skew") Text("Custom Style") } .padding() } .navigationTitle(Text("AxisSegmentedView")) } var body: some View { ZStack { #if os(iOS) NavigationView { content .navigationBarTitleDisplayMode(.inline) } .navigationViewStyle(.stack) #else content .padding() #endif } .preferredColorScheme(.dark) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView().preferredColorScheme(.dark) } } ================================================ FILE: AxisSegmentedViewExample/Shared/CustomStyle.swift ================================================ // // CustomStyle.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/25. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView public struct CustomStyle: View { @EnvironmentObject var stateValue: ASStateValue let color: Color public init(color: Color = .purple) { self.color = color } private var selectionView: some View { RoundedRectangle(cornerRadius: 5) .fill(Color.yellow) } public var body: some View { let selectionFrame = stateValue.selectionFrame ZStack(alignment: .topLeading) { Color.clear RoundedRectangle(cornerRadius: 5) .stroke() .fill(color) .frame(width: selectionFrame.width, height: selectionFrame.height) .offset(x: selectionFrame.origin.x, y: selectionFrame.origin.y) } .animation(.easeInOut, value: stateValue.selectionIndex) } } struct CustomStyle_Previews: PreviewProvider { static var previews: some View { SegmentedViewPreview(constant: .init()) { CustomStyle() .preferredColorScheme(.dark) } .frame(height: 44) .padding(.horizontal, 16) } } ================================================ FILE: AxisSegmentedViewExample/Shared/CustomStyleView.swift ================================================ // // CustomStyleView.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/26. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView struct CustomStyleView: View { @State private var selection: Int = 0 var body: some View { HStack { AxisSegmentedView(selection: $selection, constant: .init(axisMode: .vertical)) { Image(systemName: "align.horizontal.left") .itemTag(0, selectArea: 0) { SelectionItemView("align.horizontal.left.fill") } Image(systemName: "align.horizontal.right") .itemTag(1, selectArea: 260) { SelectionItemView("align.horizontal.right.fill") } Image(systemName: "align.vertical.top") .itemTag(2, selectArea: 0) { SelectionItemView("align.vertical.top.fill") } Image(systemName: "align.vertical.bottom") .itemTag(3, selectArea: 260) { SelectionItemView("align.vertical.bottom.fill") } } style: { CustomStyle(color: .blue) } onTapReceive: { selectionTap in /// Imperative syntax print("---------------------") print("Selection : ", selectionTap) print("Already selected : ", self.selection == selectionTap) } .frame(width: 44) AxisSegmentedView(selection: $selection, constant: .init()) { Image(systemName: "align.horizontal.left") .itemTag(0, selectArea: 0) { SelectionItemView("align.horizontal.left.fill") } Image(systemName: "align.horizontal.right") .itemTag(1, selectArea: 160) { SelectionItemView("align.horizontal.right.fill") } Image(systemName: "align.vertical.top") .itemTag(2, selectArea: 0) { SelectionItemView("align.vertical.top.fill") } Image(systemName: "align.vertical.bottom") .itemTag(3, selectArea: 160) { SelectionItemView("align.vertical.bottom.fill") } } style: { CustomStyle(color: .red) } onTapReceive: { selectionTap in /// Imperative syntax print("---------------------") print("Selection : ", selectionTap) print("Already selected : ", self.selection == selectionTap) } .frame(height: 44) } } } struct CustomStyleView_Previews: PreviewProvider { static var previews: some View { CustomStyleView() } } ================================================ FILE: AxisSegmentedViewExample/Shared/SegmentedListView.swift ================================================ // // SegmentedListView.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/23. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView enum StyleType: String { case ASNormalStyle case ASViscosityStyle case ASBasicStyle case ASCapsuleStyle case ASJellyStyle case ASLineStyle case ASNeumorphismStyle case ASScaleStyle } struct SegmentedListView: View { let axisMode: ASAxisMode @StateObject private var normalValue: NormalValue = .init() @StateObject private var viscosityValue: ViscosityValue = .init() @StateObject private var basicValue: BasicValue = .init() @StateObject private var capsuleValue: CapsuleValue = .init() @StateObject private var jellyValue: JellyValue = .init() @StateObject private var lineValue: LineValue = .init() @StateObject private var neumorphismValue: NeumorphismValue = .init() @StateObject private var scaleValue: ScaleValue = .init() var content: some View { Group { SegmentedViewWithControl(title: "ABNormalStyle", styleType: .ASNormalStyle, axisMode: axisMode, constant: $normalValue.constant, tabs: { Group { Text("Clear") .font(.callout) .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .foregroundColor(Color.white.opacity(0.5)) .itemTag(0, selectArea: normalValue.selectArea0) { HStack { Image(systemName: "checkmark.circle") Text("Clear") } .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .font(.callout) .foregroundColor(Color.white) } Text("Confusing") .font(.callout) .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .foregroundColor(Color.white.opacity(0.5)) .itemTag(1, selectArea: normalValue.selectArea1) { HStack { Image(systemName: "checkmark.circle") Text("Confusing") } .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .font(.callout) .foregroundColor(Color.white) } } }, style: { ASNormalStyle { _ in RoundedRectangle(cornerRadius: 5) .fill(Color(hex: 0x191919)) .overlay( RoundedRectangle(cornerRadius: 5) .stroke() .fill(Color(hex: 0x282828)) ) .padding(3.5) } .background(Color(hex: 0x0B0C10)) .clipShape(RoundedRectangle(cornerRadius: 5)) }) SegmentedViewWithControl(title: "ASViscosityStyle", styleType: .ASViscosityStyle, axisMode: axisMode, constant: $viscosityValue.constant, tabs: { Group { Text("Store") .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .font(.callout) .foregroundColor(Color.white.opacity(0.5)) .itemTag(0, selectArea: viscosityValue.selectArea0) { Text("Store") .font(.callout) .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .foregroundColor(Color.white) } Text("Library") .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .font(.callout) .foregroundColor(Color.white.opacity(0.5)) .itemTag(1, selectArea: viscosityValue.selectArea1) { Text("Library") .font(.callout) .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .foregroundColor(Color.white) } Text("Downloads") .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .font(.callout) .foregroundColor(Color.white.opacity(0.5)) .itemTag(2, selectArea: viscosityValue.selectArea2) { Text("Downloads") .font(.callout) .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .foregroundColor(Color.white) } } }, style: { ASViscosityStyle { _ in Capsule() .fill(LinearGradient(colors: [Color(hex: 0x222222), Color(hex: 0x111111)], startPoint: axisMode == .horizontal ? UnitPoint.top : UnitPoint.leading, endPoint: axisMode == .horizontal ? UnitPoint.bottom : UnitPoint.trailing)) .overlay( Capsule() .stroke() .fill(Color.black) ) .padding(2) } .background(Color.black.opacity(0.2)) .clipShape(Capsule()) .innerShadow(Capsule(), radius: 1, opacity: 0.5, isDark: true) }) SegmentedViewWithControl(title: "ASBasicStyle", styleType: .ASBasicStyle, axisMode: axisMode, constant: $basicValue.constant, tabs: { Group { Text("Male") .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .font(.callout) .foregroundColor(Color.white.opacity(0.5)) .itemTag(0, selectArea: basicValue.selectArea0) { Text("Male") .font(.callout) .fixedSize() .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .foregroundColor(Color.white) } Text("Female") .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .font(.callout) .foregroundColor(Color.white.opacity(0.5)) .itemTag(1, selectArea: basicValue.selectArea1) { Text("Female") .font(.callout) .fixedSize() .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .foregroundColor(Color.white) } Text("Other") .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .font(.callout) .foregroundColor(Color.white.opacity(0.5)) .itemTag(2, selectArea: basicValue.selectArea2) { Text("Other") .font(.callout) .fixedSize() .rotationEffect(Angle(degrees: axisMode == .horizontal ? 0 : -90)) .foregroundColor(Color.white) } } }, style: { ASBasicStyle(backgroundColor: basicValue.backgroundColor, foregroundColor: basicValue.foregroundColor, cornerRadius: basicValue.cornerRadius, padding: basicValue.padding, isApplySelectionCornerRadius: basicValue.isApplySelectionCornerRadius, movementMode: basicValue.movementMode) }) SegmentedViewWithControl(title: "ASJellyStyle", styleType: .ASJellyStyle, axisMode: axisMode, constant: $jellyValue.constant, tabs: { Group { Image(systemName: "align.horizontal.left") .itemTag(0, selectArea: jellyValue.selectArea0) { SelectionItemView("align.horizontal.left.fill") } Image(systemName: "align.horizontal.right") .itemTag(1, selectArea: jellyValue.selectArea1) { SelectionItemView("align.horizontal.right.fill") } Image(systemName: "align.vertical.top") .itemTag(2, selectArea: jellyValue.selectArea2) { SelectionItemView("align.vertical.top.fill") } Image(systemName: "align.vertical.bottom") .itemTag(3, selectArea: jellyValue.selectArea3) { SelectionItemView("align.vertical.bottom.fill") } } }, style: { ASJellyStyle(backgroundColor: jellyValue.backgroundColor, foregroundColor: jellyValue.foregroundColor, jellyRadius: jellyValue.jellyRadius, jellyDepth: jellyValue.jellyDepth, jellyEdge: jellyValue.jellyEdge) // .overlay( // RoundedRectangle(cornerRadius: 11) // .stroke(.purple, lineWidth: 1) // .padding(1) // ) // .clipShape(RoundedRectangle(cornerRadius: 11)) }) SegmentedViewWithControl(title: "ASLineStyle", styleType: .ASLineStyle, axisMode: axisMode, constant: $lineValue.constant, tabs: { Group { Image(systemName: "align.horizontal.left") .itemTag(0, selectArea: lineValue.selectArea0) { SelectionItemView("align.horizontal.left.fill") } Image(systemName: "align.horizontal.right") .itemTag(1, selectArea: lineValue.selectArea1) { SelectionItemView("align.horizontal.right.fill") } Image(systemName: "align.vertical.top") .itemTag(2, selectArea: lineValue.selectArea2) { SelectionItemView("align.vertical.top.fill") } Image(systemName: "align.vertical.bottom") .itemTag(3, selectArea: lineValue.selectArea3) { SelectionItemView("align.vertical.bottom.fill") } } }, style: { ASLineStyle(lineColor: lineValue.lineColor, lineSmallWidth: lineValue.lineSmallWidth, lineLargeScale: lineValue.lineLargeScale, lineEdge: lineValue.lineEdge, movementMode: lineValue.movementMode) .overlay( Rectangle() .stroke() .fill(Color(hex: 0x303030)) ) }) SegmentedViewWithControl(title: "ASCapsuleStyle", styleType: .ASCapsuleStyle, axisMode: axisMode, constant: $capsuleValue.constant, tabs: { Group { Image(systemName: "align.horizontal.left") .itemTag(0, selectArea: capsuleValue.selectArea0) { SelectionItemView("align.horizontal.left.fill") } Image(systemName: "align.horizontal.right") .itemTag(1, selectArea: capsuleValue.selectArea1) { SelectionItemView("align.horizontal.right.fill") } Image(systemName: "align.vertical.top") .itemTag(2, selectArea: capsuleValue.selectArea2) { SelectionItemView("align.vertical.top.fill") } Image(systemName: "align.vertical.bottom") .itemTag(3, selectArea: capsuleValue.selectArea3) { SelectionItemView("align.vertical.bottom.fill") } } }, style: { ASCapsuleStyle(backgroundColor: capsuleValue.backgroundColor, foregroundColor: capsuleValue.foregroundColor, movementMode: capsuleValue.movementMode) }) SegmentedViewWithControl(title: "ASNeumorphismStyle", styleType: .ASNeumorphismStyle, axisMode: axisMode, constant: $neumorphismValue.constant, area: 70, tabs: { Group { Image(systemName: "align.horizontal.left") .itemTag(0, selectArea: neumorphismValue.selectArea0) { SelectionItemView("align.horizontal.left.fill") } Image(systemName: "align.horizontal.right") .itemTag(1, selectArea: neumorphismValue.selectArea1) { SelectionItemView("align.horizontal.right.fill") } Image(systemName: "align.vertical.top") .itemTag(2, selectArea: neumorphismValue.selectArea2) { SelectionItemView("align.vertical.top.fill") } Image(systemName: "align.vertical.bottom") .itemTag(3, selectArea: neumorphismValue.selectArea3) { SelectionItemView("align.vertical.bottom.fill") } } }, style: { ASNeumorphismStyle(backgroundColor: neumorphismValue.backgroundColor, foregroundColor: neumorphismValue.foregroundColor, cornerRadius: neumorphismValue.cornerRadius, padding: neumorphismValue.padding, shadowRadius: neumorphismValue.shadowRadius, shadowOpacity: neumorphismValue.shadowOpacity, isInner: neumorphismValue.isInner, movementMode: neumorphismValue.movementMode) }) SegmentedViewWithControl(title: "ASScaleStyle", styleType: .ASScaleStyle, axisMode: axisMode, constant: $scaleValue.constant, tabs: { Group { Image(systemName: "align.horizontal.left") .itemTag(0, selectArea: scaleValue.selectArea0) { SelectionItemView("align.horizontal.left.fill") } Image(systemName: "align.horizontal.right") .itemTag(1, selectArea: scaleValue.selectArea1) { SelectionItemView("align.horizontal.right.fill") } Image(systemName: "align.vertical.top") .itemTag(2, selectArea: scaleValue.selectArea2) { SelectionItemView("align.vertical.top.fill") } Image(systemName: "align.vertical.bottom") .itemTag(3, selectArea: scaleValue.selectArea3) { SelectionItemView("align.vertical.bottom.fill") } } }, style: { ASScaleStyle(backgroundColor: scaleValue.backgroundColor, foregroundColor: scaleValue.foregroundColor, cornerRadius: scaleValue.cornerRadius, minimumScale: scaleValue.minimumScale) }) } } var body: some View { ZStack { if axisMode == .horizontal { ScrollView { VStack(spacing: 20) { content } .padding(.horizontal, 5) } }else { ScrollView(.horizontal) { HStack(spacing: 20) { content } .padding(.vertical, 5) } } } .environmentObject(normalValue) .environmentObject(viscosityValue) .environmentObject(basicValue) .environmentObject(capsuleValue) .environmentObject(jellyValue) .environmentObject(lineValue) .environmentObject(neumorphismValue) .environmentObject(scaleValue) } } struct SegmentedListView_Previews: PreviewProvider { static var previews: some View { SegmentedListView(axisMode: .horizontal) .padding() .preferredColorScheme(.dark) } } ================================================ FILE: AxisSegmentedViewExample/Shared/SegmentedViewWithControl.swift ================================================ // // SegmentedViewWithControl.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/23. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView struct SegmentedViewWithControl : View { @State private var isShowControlView: Bool = false @State private var selection: Int = 0 @State private var maxSelectArea: CGFloat = 0 @EnvironmentObject private var normalValue: NormalValue @EnvironmentObject private var viscosityValue: ViscosityValue @EnvironmentObject private var basicValue: BasicValue @EnvironmentObject private var capsuleValue: CapsuleValue @EnvironmentObject private var jellyValue: JellyValue @EnvironmentObject private var lineValue: LineValue @EnvironmentObject private var neumorphismValue: NeumorphismValue @EnvironmentObject private var scaleValue: ScaleValue let title: String let styleType: StyleType let axisMode: ASAxisMode @Binding var constant: ASConstant let area: CGFloat var tabs: () -> Tabs var style: () -> Style init(title: String, styleType: StyleType, axisMode: ASAxisMode = .horizontal, constant: Binding, area: CGFloat = 44, @ViewBuilder tabs: @escaping () -> Tabs, @ViewBuilder style: @escaping () -> Style) { self.title = title self.styleType = styleType self.axisMode = axisMode _constant = constant self.area = area self.tabs = tabs self.style = style } private var segmentedView: some View { AxisSegmentedView(selection: $selection, constant: constant) { tabs() } style: { style() } onTapReceive: { selectionTap in /// Imperative syntax print("---------------------") print("Selection : ", selectionTap) print("Already selected : ", self.selection == selectionTap) } .font(.system(size: 20)) } private var controlView: some View { ZStack { Color(hex: 0x030303) .cornerRadius(8) ScrollView { VStack { getStyleControlView() VStack(alignment: .leading, spacing: 8) { Text("● Divide Line").opacity(0.5).font(.caption) HStack { Text("Color") Spacer() ColorPicker(selection: $constant.divideLine.color) {} } HStack { Text("Width") Spacer() Slider(value: $constant.divideLine.width, in: 0...5) Spacer() Text("\(constant.divideLine.width, specifier: "%.2f")") } HStack { Text("Scale") Spacer() Slider(value: $constant.divideLine.scale, in: 0...1) Spacer() Text("\(constant.divideLine.scale, specifier: "%.2f")") } HStack { Text("isShowSelectionLine") Spacer() Toggle(isOn: $constant.divideLine.isShowSelectionLine) {} } } .padding() VStack(alignment: .leading, spacing: 8) { Text("● Active").opacity(0.5).font(.caption) HStack { Text("ActivatedGeometryEffect") Spacer() Toggle(isOn: $constant.isActivatedGeometryEffect) {} } HStack { Text("ActivatedVibration") Spacer() Toggle(isOn: $constant.isActivatedVibration) {} } } .padding() } } .font(.footnote) .labelsHidden() } } var body: some View { ZStack { ZStack { if constant.axisMode == .horizontal { VStack { HStack { Text(title) .font(.caption) .foregroundColor(Color.white.opacity(0.6)) Spacer() Image(systemName: "chevron.down") .frame(width: 36, height: 36) .rotationEffect(Angle(degrees: isShowControlView ? -180 : 0)) .contentShape(Rectangle()) } .frame(height: 36) .contentShape(Rectangle()) .onTapGesture { withAnimation(.easeInOut) { isShowControlView.toggle() } } VStack(spacing: 0) { segmentedView .frame(height: area) Spacer().frame(height: 10) controlView .opacity(isShowControlView ? 1 : 0) .frame(height: isShowControlView ? 400 : 0) } } }else { VStack { HStack { Text(title) .font(.caption) .foregroundColor(Color.white.opacity(0.6)) Spacer() Image(systemName: "chevron.right") .frame(width: 36, height: 36) .rotationEffect(Angle(degrees: isShowControlView ? -180 : 0)) .contentShape(Rectangle()) } .frame(height: 36) .contentShape(Rectangle()) .onTapGesture { withAnimation(.easeInOut) { isShowControlView.toggle() } } HStack(spacing: 0) { segmentedView .frame(width: area) Spacer().frame(width: 10) controlView .frame(width: 260) .mask( Rectangle() .frame(width: isShowControlView ? 260 : 0) ) .opacity(isShowControlView ? 1 : 0) .frame(width: isShowControlView ? 260 : 0) } } } } .padding(10) } .background( GeometryReader { proxy in Color(hex: 0x15151A) .cornerRadius(8) .onAppear { self.maxSelectArea = constant.axisMode == .horizontal ? proxy.size.width * 0.5 : proxy.size.height * 0.5 } } ) .onAppear { constant.axisMode = axisMode } } private func getStyleControlView() -> some View { Group { VStack(alignment: .leading, spacing: 8) { HStack { Text("● " + title).opacity(0.5).font(.caption) Spacer() } switch styleType { case .ASNormalStyle: VStack(alignment: .leading, spacing: 8) { Text("● SelectArea").opacity(0.5).font(.caption) HStack { Text("0") .font(.system(size: 16)) Spacer() Slider(value: $normalValue.selectArea0, in: 0...maxSelectArea) Spacer() Text("\(normalValue.selectArea0, specifier: "%.2f")") } HStack { Text("1") .font(.system(size: 16)) Spacer() Slider(value: $normalValue.selectArea1, in: 0...maxSelectArea) Spacer() Text("\(normalValue.selectArea1, specifier: "%.2f")") } } .padding(.top, 20) case .ASViscosityStyle: VStack(alignment: .leading, spacing: 8) { Text("● SelectArea").opacity(0.5).font(.caption) HStack { Text("0") .font(.system(size: 16)) Spacer() Slider(value: $viscosityValue.selectArea0, in: 0...maxSelectArea) Spacer() Text("\(viscosityValue.selectArea0, specifier: "%.2f")") } HStack { Text("1") .font(.system(size: 16)) Spacer() Slider(value: $viscosityValue.selectArea1, in: 0...maxSelectArea) Spacer() Text("\(viscosityValue.selectArea1, specifier: "%.2f")") } HStack { Text("2") .font(.system(size: 16)) Spacer() Slider(value: $viscosityValue.selectArea2, in: 0...maxSelectArea) Spacer() Text("\(viscosityValue.selectArea2, specifier: "%.2f")") } } .padding(.top, 20) case .ASBasicStyle: Group { HStack { Text("Background Color") Spacer() ColorPicker(selection: $basicValue.backgroundColor) {} } HStack { Text("Foreground Color") Spacer() ColorPicker(selection: $basicValue.foregroundColor) {} } HStack { Text("Corner Raddius") Spacer() Slider(value: $basicValue.cornerRadius, in: 0...22) Spacer() Text("\(basicValue.cornerRadius, specifier: "%.2f")") } HStack { Text("Padding") Spacer() Slider(value: $basicValue.padding, in: 0...6) Spacer() Text("\(basicValue.padding, specifier: "%.2f")") } HStack { Text("isApplySelectionCornerRadius") Spacer() Toggle(isOn: $basicValue.isApplySelectionCornerRadius) {} } HStack { Text("Movement Mode") Spacer() Picker(selection: $basicValue.movementMode) { Text("Normal").tag(ASMovementMode.normal) Text("Viscosity").tag(ASMovementMode.viscosity) } label: {} .pickerStyle(.segmented) } VStack(alignment: .leading, spacing: 8) { Text("● SelectArea").opacity(0.5).font(.caption) HStack { Text("0") .font(.system(size: 16)) Spacer() Slider(value: $basicValue.selectArea0, in: 0...maxSelectArea) Spacer() Text("\(basicValue.selectArea0, specifier: "%.2f")") } HStack { Text("1") .font(.system(size: 16)) Spacer() Slider(value: $basicValue.selectArea1, in: 0...maxSelectArea) Spacer() Text("\(basicValue.selectArea1, specifier: "%.2f")") } HStack { Text("2") .font(.system(size: 16)) Spacer() Slider(value: $basicValue.selectArea2, in: 0...maxSelectArea) Spacer() Text("\(basicValue.selectArea2, specifier: "%.2f")") } } .padding(.top, 20) } case .ASCapsuleStyle: Group { HStack { Text("Background Color") Spacer() ColorPicker(selection: $capsuleValue.backgroundColor) {} } HStack { Text("Foreground Color") Spacer() ColorPicker(selection: $capsuleValue.foregroundColor) {} } HStack { Text("Movement Mode") Spacer() Picker(selection: $capsuleValue.movementMode) { Text("Normal").tag(ASMovementMode.normal) Text("Viscosity").tag(ASMovementMode.viscosity) } label: {} .pickerStyle(.segmented) } VStack(alignment: .leading, spacing: 8) { Text("● SelectArea").opacity(0.5).font(.caption) HStack { Text("0") .font(.system(size: 16)) Spacer() Slider(value: $capsuleValue.selectArea0, in: 0...maxSelectArea) Spacer() Text("\(capsuleValue.selectArea0, specifier: "%.2f")") } HStack { Text("1") .font(.system(size: 16)) Spacer() Slider(value: $capsuleValue.selectArea1, in: 0...maxSelectArea) Spacer() Text("\(capsuleValue.selectArea1, specifier: "%.2f")") } HStack { Text("2") .font(.system(size: 16)) Spacer() Slider(value: $capsuleValue.selectArea2, in: 0...maxSelectArea) Spacer() Text("\(capsuleValue.selectArea2, specifier: "%.2f")") } HStack { Text("3") .font(.system(size: 16)) Spacer() Slider(value: $capsuleValue.selectArea3, in: 0...maxSelectArea) Spacer() Text("\(capsuleValue.selectArea3, specifier: "%.2f")") } } .padding(.top, 20) } case .ASJellyStyle: Group { HStack { Text("Background Color") Spacer() ColorPicker(selection: $jellyValue.backgroundColor) {} } HStack { Text("Foreground Color") Spacer() ColorPicker(selection: $jellyValue.foregroundColor) {} } HStack { Text("Jelly Raddius") Spacer() Slider(value: $jellyValue.jellyRadius, in: 0...100) Spacer() Text("\(jellyValue.jellyRadius, specifier: "%.2f")") } HStack { Text("Jelly Depth") Spacer() Slider(value: $jellyValue.jellyDepth, in: 0...1.0) Spacer() Text("\(jellyValue.jellyDepth, specifier: "%.2f")") } HStack { Text("Jelly Edge") Spacer() Picker(selection: $jellyValue.jellyEdge) { Text("Bottom/Trailing").tag(ASEdgeMode.bottomTrailing) Text("Top/Leading").tag(ASEdgeMode.topLeading) } label: {} .pickerStyle(.segmented) } VStack(alignment: .leading, spacing: 8) { Text("● SelectArea").opacity(0.5).font(.caption) HStack { Text("0") .font(.system(size: 16)) Spacer() Slider(value: $jellyValue.selectArea0, in: 0...maxSelectArea) Spacer() Text("\(jellyValue.selectArea0, specifier: "%.2f")") } HStack { Text("1") .font(.system(size: 16)) Spacer() Slider(value: $jellyValue.selectArea1, in: 0...maxSelectArea) Spacer() Text("\(jellyValue.selectArea1, specifier: "%.2f")") } HStack { Text("2") .font(.system(size: 16)) Spacer() Slider(value: $jellyValue.selectArea2, in: 0...maxSelectArea) Spacer() Text("\(jellyValue.selectArea2, specifier: "%.2f")") } HStack { Text("3") .font(.system(size: 16)) Spacer() Slider(value: $jellyValue.selectArea3, in: 0...maxSelectArea) Spacer() Text("\(jellyValue.selectArea3, specifier: "%.2f")") } } .padding(.top, 20) } case .ASLineStyle: Group { HStack { Text("Line Color") Spacer() ColorPicker(selection: $lineValue.lineColor) {} } HStack { Text("Line Small Width") Spacer() Slider(value: $lineValue.lineSmallWidth, in: 0...6) Spacer() Text("\(lineValue.lineSmallWidth, specifier: "%.2f")") } HStack { Text("Line Large Scale") Spacer() Slider(value: $lineValue.lineLargeScale, in: 0...1) Spacer() Text("\(lineValue.lineLargeScale, specifier: "%.2f")") } HStack { Text("Line Edge") Spacer() Picker(selection: $lineValue.lineEdge) { Text("Bottom/Trailing").tag(ASEdgeMode.bottomTrailing) Text("Top/Leading").tag(ASEdgeMode.topLeading) } label: {} .pickerStyle(.segmented) } HStack { Text("Movement Mode") Spacer() Picker(selection: $lineValue.movementMode) { Text("Normal").tag(ASMovementMode.normal) Text("Viscosity").tag(ASMovementMode.viscosity) } label: {} .pickerStyle(.segmented) } VStack(alignment: .leading, spacing: 8) { Text("● SelectArea").opacity(0.5).font(.caption) HStack { Text("0") .font(.system(size: 16)) Spacer() Slider(value: $lineValue.selectArea0, in: 0...maxSelectArea) Spacer() Text("\(lineValue.selectArea0, specifier: "%.2f")") } HStack { Text("1") .font(.system(size: 16)) Spacer() Slider(value: $lineValue.selectArea1, in: 0...maxSelectArea) Spacer() Text("\(lineValue.selectArea1, specifier: "%.2f")") } HStack { Text("2") .font(.system(size: 16)) Spacer() Slider(value: $lineValue.selectArea2, in: 0...maxSelectArea) Spacer() Text("\(lineValue.selectArea2, specifier: "%.2f")") } HStack { Text("3") .font(.system(size: 16)) Spacer() Slider(value: $lineValue.selectArea3, in: 0...maxSelectArea) Spacer() Text("\(lineValue.selectArea3, specifier: "%.2f")") } } .padding(.top, 20) } case .ASNeumorphismStyle: Group { HStack { Text("Background Color") Spacer() ColorPicker(selection: $neumorphismValue.backgroundColor) {} } HStack { Text("Foreground Color") Spacer() ColorPicker(selection: $neumorphismValue.foregroundColor) {} } HStack { Text("Corner Raddius") Spacer() Slider(value: $neumorphismValue.cornerRadius, in: 0...35) Spacer() Text("\(neumorphismValue.cornerRadius, specifier: "%.2f")") } HStack { Text("Shadow Raddius") Spacer() Slider(value: $neumorphismValue.shadowRadius, in: 0...7) Spacer() Text("\(neumorphismValue.shadowRadius, specifier: "%.2f")") } HStack { Text("Padding") Spacer() Slider(value: $neumorphismValue.padding, in: 0...12) Spacer() Text("\(neumorphismValue.padding, specifier: "%.2f")") } HStack { Text("Shadow Opacity") Spacer() Slider(value: $neumorphismValue.shadowOpacity, in: 0...1) Spacer() Text("\(neumorphismValue.shadowOpacity, specifier: "%.2f")") } HStack { Text("isInner") Spacer() Toggle(isOn: $neumorphismValue.isInner) {} } HStack { Text("Movement Mode") Spacer() Picker(selection: $neumorphismValue.movementMode) { Text("Normal").tag(ASMovementMode.normal) Text("Viscosity").tag(ASMovementMode.viscosity) } label: {} .pickerStyle(.segmented) } VStack(alignment: .leading, spacing: 8) { Text("● SelectArea").opacity(0.5).font(.caption) HStack { Text("0") .font(.system(size: 16)) Spacer() Slider(value: $neumorphismValue.selectArea0, in: 0...maxSelectArea) Spacer() Text("\(neumorphismValue.selectArea0, specifier: "%.2f")") } HStack { Text("1") .font(.system(size: 16)) Spacer() Slider(value: $neumorphismValue.selectArea1, in: 0...maxSelectArea) Spacer() Text("\(neumorphismValue.selectArea1, specifier: "%.2f")") } HStack { Text("2") .font(.system(size: 16)) Spacer() Slider(value: $neumorphismValue.selectArea2, in: 0...maxSelectArea) Spacer() Text("\(neumorphismValue.selectArea2, specifier: "%.2f")") } HStack { Text("3") .font(.system(size: 16)) Spacer() Slider(value: $neumorphismValue.selectArea3, in: 0...maxSelectArea) Spacer() Text("\(neumorphismValue.selectArea3, specifier: "%.2f")") } } .padding(.top, 20) } case .ASScaleStyle: Group { HStack { Text("Background Color") Spacer() ColorPicker(selection: $scaleValue.backgroundColor) {} } HStack { Text("Foreground Color") Spacer() ColorPicker(selection: $scaleValue.foregroundColor) {} } HStack { Text("Corner Raddius") Spacer() Slider(value: $scaleValue.cornerRadius, in: 0...22) Spacer() Text("\(scaleValue.cornerRadius, specifier: "%.2f")") } HStack { Text("Minimum Scale") Spacer() Slider(value: $scaleValue.minimumScale, in: 0...1) Spacer() Text("\(scaleValue.minimumScale, specifier: "%.2f")") } VStack(alignment: .leading, spacing: 8) { Text("● SelectArea").opacity(0.5).font(.caption) HStack { Text("0") .font(.system(size: 16)) Spacer() Slider(value: $scaleValue.selectArea0, in: 0...maxSelectArea) Spacer() Text("\(scaleValue.selectArea0, specifier: "%.2f")") } HStack { Text("1") .font(.system(size: 16)) Spacer() Slider(value: $scaleValue.selectArea1, in: 0...maxSelectArea) Spacer() Text("\(scaleValue.selectArea1, specifier: "%.2f")") } HStack { Text("2") .font(.system(size: 16)) Spacer() Slider(value: $scaleValue.selectArea2, in: 0...maxSelectArea) Spacer() Text("\(scaleValue.selectArea2, specifier: "%.2f")") } HStack { Text("3") .font(.system(size: 16)) Spacer() Slider(value: $scaleValue.selectArea3, in: 0...maxSelectArea) Spacer() Text("\(scaleValue.selectArea3, specifier: "%.2f")") } } .padding(.top, 20) } } } .padding() } } } struct SegmentedViewWithControl_Previews: PreviewProvider { static var previews: some View { SegmentedViewWithControl(title: "ABBasicStyle", styleType: .ASBasicStyle, axisMode: .horizontal, constant: .constant(ASConstant.init()), tabs: { Group { Image(systemName: "align.horizontal.left") .itemTag(0, selectArea: 0) { SelectionItemView("align.horizontal.left.fill") } Image(systemName: "align.horizontal.right") .itemTag(1, selectArea: 0) { SelectionItemView("align.horizontal.right.fill") } Image(systemName: "align.vertical.top") .itemTag(2, selectArea: 0) { SelectionItemView("align.vertical.top.fill") } Image(systemName: "align.vertical.bottom") .itemTag(3, selectArea: 0) { SelectionItemView("align.vertical.bottom.fill") } } }, style: { ASBasicStyle() }) .padding() .preferredColorScheme(.dark) } } ================================================ FILE: AxisSegmentedViewExample/Shared/SelectionItemView.swift ================================================ // // SelectionItemView.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/23. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView struct SelectionItemView: View { @EnvironmentObject private var stateValue: ASStateValue @State private var scale: CGFloat = 1 let iconName: String init(_ iconName: String) { self.iconName = iconName } var body: some View { Image(systemName: iconName) .foregroundColor(Color.white) .scaleEffect(scale) .onAppear { scale = 1 if !stateValue.isInitialRun { withAnimation(.easeInOut(duration: 0.26)) { scale = 1.2 } withAnimation(.easeInOut(duration: 0.26).delay(0.26)) { scale = 1 } } } } } ================================================ FILE: AxisSegmentedViewExample/Shared/ViewModel/BacisValue.swift ================================================ // // BacisValue.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/23. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView class BasicValue: ObservableObject { @Published var constant = ASConstant(divideLine: .init(color: Color.white.opacity(0.2), scale: 0.3)) @Published var backgroundColor: Color = .gray.opacity(0.3) @Published var foregroundColor: Color = .black.opacity(0.7) @Published var cornerRadius: CGFloat = 6 @Published var padding: CGFloat = 3 @Published var isApplySelectionCornerRadius: Bool = true @Published var movementMode: ASMovementMode = .viscosity @Published var selectArea0: CGFloat = 0 @Published var selectArea1: CGFloat = 0 @Published var selectArea2: CGFloat = 0 } ================================================ FILE: AxisSegmentedViewExample/Shared/ViewModel/CapsuleValue.swift ================================================ // // CapsuleValue.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/23. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView class CapsuleValue: ObservableObject { @Published var constant = ASConstant(divideLine: .init(color: Color(hex: 0x444444), scale: 0)) @Published var backgroundColor: Color = .gray.opacity(0.4) @Published var foregroundColor: Color = Color.blue @Published var movementMode: ASMovementMode = .viscosity @Published var selectArea0: CGFloat = 0 @Published var selectArea1: CGFloat = 0 @Published var selectArea2: CGFloat = 0 @Published var selectArea3: CGFloat = 0 } ================================================ FILE: AxisSegmentedViewExample/Shared/ViewModel/JellyValue.swift ================================================ // // JellyValue.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/23. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView class JellyValue: ObservableObject { @Published var constant = ASConstant(divideLine: .init(color: Color(hex: 0x444444), scale: 0)) @Published var backgroundColor: Color = .gray.opacity(0.1) @Published var foregroundColor: Color = .purple @Published var jellyRadius: CGFloat = 56 @Published var jellyDepth: CGFloat = 0.9 @Published var jellyEdge: ASEdgeMode = .bottomTrailing @Published var selectArea0: CGFloat = 0 @Published var selectArea1: CGFloat = 0 @Published var selectArea2: CGFloat = 0 @Published var selectArea3: CGFloat = 0 } ================================================ FILE: AxisSegmentedViewExample/Shared/ViewModel/LineValue.swift ================================================ // // LineValue.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/23. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView class LineValue: ObservableObject { @Published var constant = ASConstant(divideLine: .init(color: Color(hex: 0x202020), scale: 0)) @Published var lineColor: Color = .blue @Published var lineSmallWidth: CGFloat = 2 @Published var lineLargeScale: CGFloat = 1.0 @Published var lineEdge: ASEdgeMode = .bottomTrailing @Published var movementMode: ASMovementMode = .viscosity @Published var selectArea0: CGFloat = 0 @Published var selectArea1: CGFloat = 0 @Published var selectArea2: CGFloat = 0 @Published var selectArea3: CGFloat = 0 } ================================================ FILE: AxisSegmentedViewExample/Shared/ViewModel/NeumorphismValue.swift ================================================ // // NeumorphismValue.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/23. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView class NeumorphismValue: ObservableObject { @Published var constant = ASConstant(divideLine: .init(color: Color(hex: 0x444444), scale: 0)) @Published var backgroundColor: Color = .clear @Published var foregroundColor: Color = .clear @Published var cornerRadius: CGFloat = 11 @Published var padding: CGFloat = 12 @Published var shadowRadius: CGFloat = 5 @Published var shadowOpacity: CGFloat = 0.7 @Published var isInner: Bool = false @Published var movementMode: ASMovementMode = .viscosity @Published var selectArea0: CGFloat = 0 @Published var selectArea1: CGFloat = 0 @Published var selectArea2: CGFloat = 0 @Published var selectArea3: CGFloat = 0 init(backgroundColor: Color = Color(hex: 0x31353A), foregroundColor: Color = Color(hex: 0x31353A), cornerRadius: CGFloat = 11, padding: CGFloat = 12, shadowRadius: CGFloat = 5, shadowOpacity: CGFloat = 0.7, isInner: Bool = false, movementMode: ASMovementMode = .viscosity) { self.backgroundColor = backgroundColor self.foregroundColor = foregroundColor self.cornerRadius = cornerRadius self.padding = padding self.shadowRadius = shadowRadius self.isInner = isInner self.movementMode = movementMode } } ================================================ FILE: AxisSegmentedViewExample/Shared/ViewModel/NormalValue.swift ================================================ // // NormalValue.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/24. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView class NormalValue: ObservableObject { @Published var constant = ASConstant(divideLine: .init(color: Color(hex: 0x444444), scale: 0)) @Published var selectArea0: CGFloat = 0 @Published var selectArea1: CGFloat = 0 } ================================================ FILE: AxisSegmentedViewExample/Shared/ViewModel/ScaleValue.swift ================================================ // // ScaleValue.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/23. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView class ScaleValue: ObservableObject { @Published var constant = ASConstant(divideLine: .init(color: Color(hex: 0x444444), scale: 0)) @Published var backgroundColor: Color = .clear @Published var foregroundColor: Color = Color.blue @Published var cornerRadius: CGFloat = 11 @Published var minimumScale: CGFloat = 0.1 @Published var selectArea0: CGFloat = 0 @Published var selectArea1: CGFloat = 0 @Published var selectArea2: CGFloat = 0 @Published var selectArea3: CGFloat = 0 } ================================================ FILE: AxisSegmentedViewExample/Shared/ViewModel/ViscosityValue.swift ================================================ // // ViscosityValue.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/24. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView class ViscosityValue: ObservableObject { @Published var constant = ASConstant(divideLine: .init(color: Color(hex: 0x444444), scale: 0)) @Published var selectArea0: CGFloat = 0 @Published var selectArea1: CGFloat = 0 @Published var selectArea2: CGFloat = 0 } ================================================ FILE: AxisSegmentedViewExample/Shared/WithoutStyleView.swift ================================================ // // WithoutStyleView.swift // AxisSegmentedViewExample // // Created by jasu on 2022/03/23. // Copyright (c) 2022 jasu All rights reserved. // import SwiftUI import AxisSegmentedView struct WithoutStyleView: View { @State private var selection1: Int = 0 @State private var constant1 = ASConstant(axisMode: .vertical) @State private var selection2: Int = 0 @State private var constant2 = ASConstant(axisMode: .horizontal) var body: some View { VStack { AxisSegmentedView(selection: $selection1, constant: constant1) { TabViews() } onTapReceive: { selectionTap in /// Imperative syntax print("---------------------") print("Selection : ", selectionTap) print("Already selected : ", self.selection1 == selectionTap) } .clipped() AxisSegmentedView(selection: $selection2, constant: constant2) { TabViews() } onTapReceive: { selectionTap in /// Imperative syntax print("---------------------") print("Selection : ", selectionTap) print("Already selected : ", self.selection2 == selectionTap) } .clipped() } } } struct TabViews: View { @State private var maxArea1: CGFloat = 200 @EnvironmentObject private var stateValue: ASStateValue let colors = [Color(hex: 0x295A76), Color(hex: 0x7FACAA), Color(hex: 0xEBF4CC), Color(hex: 0xE79875), Color(hex: 0xBA523C), Color(hex: 0x295A76)] var listView: some View { List(0...100, id: \.self) { index in Button { print("click") } label: { Text("Index \(index)") } }.listStyle(.plain) } var body: some View { Group { Rectangle() .fill(colors[0]) .overlay( Text("0") ) .itemTag(0, selectArea: maxArea1) { Rectangle() .fill(.red) .overlay( Text("0") ) } Rectangle() .fill(colors[1]) .overlay( Text("1") ) .itemTag(1, selectArea: maxArea1) { listView } Rectangle() .fill(colors[2]) .overlay( Text("2") ) .itemTag(2, selectArea: maxArea1) { listView } Rectangle() .fill(colors[3]) .overlay( Text("3") ) .itemTag(3, selectArea: maxArea1) { listView } } } } struct WithoutStyleView_Previews: PreviewProvider { static var previews: some View { WithoutStyleView() .preferredColorScheme(.dark) } } ================================================ FILE: AxisSegmentedViewExample/macOS/macOS.entitlements ================================================ com.apple.security.app-sandbox com.apple.security.files.user-selected.read-only ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2022 jasu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Package.swift ================================================ // swift-tools-version: 5.6 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "AxisSegmentedView", platforms: [ .iOS(.v14), .tvOS(.v14), .macOS(.v11) ], products: [ // Products define the executables and libraries a package produces, and make them visible to other packages. .library( name: "AxisSegmentedView", targets: ["AxisSegmentedView"]), ], dependencies: [ // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0"), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets can depend on other targets in this package, and on products in packages this package depends on. .target( name: "AxisSegmentedView", dependencies: []), .testTarget( name: "AxisSegmentedViewTests", dependencies: ["AxisSegmentedView"]), ] ) ================================================ FILE: README.md ================================================ # **AxisSegmentedView for SwiftUI** A library that allows you to easily create various styles of segmented views. Supports iOS, macOS and tvOS. [![Platforms](https://img.shields.io/badge/Platforms-iOS%20%7C%20macOS-blue?style=flat-square)](https://developer.apple.com/macOS) [![iOS](https://img.shields.io/badge/iOS-14.0-blue.svg)](https://developer.apple.com/iOS) [![macOS](https://img.shields.io/badge/macOS-11.0-blue.svg)](https://developer.apple.com/macOS) [![tvOS](https://img.shields.io/badge/tvOS-14.0-blue.svg)](https://developer.apple.com/tvOS) [![instagram](https://img.shields.io/badge/instagram-@dev.fabula-orange.svg?style=flat-square)](https://www.instagram.com/dev.fabula) [![SPM](https://img.shields.io/badge/SPM-compatible-red?style=flat-square)](https://developer.apple.com/documentation/swift_packages/package/) [![MIT](https://img.shields.io/badge/licenses-MIT-red.svg)](https://opensource.org/licenses/MIT) ## Screenshot |Horizontal|Vertical|For use without style| |:---:|:---:|:---:| |||| https://user-images.githubusercontent.com/1617304/160249891-a2fe15f2-5b07-4c2c-a204-fa9bd8981989.mov ## Example [https://fabulaapp.page.link/234](https://fabulaapp.page.link/234) ## Usages ```swift AxisSegmentedView(selection: $selection, constant: .init()) { Image(systemName: "align.horizontal.left") .itemTag(0, selectArea: 0) { Image(systemName: "align.horizontal.left.fill") } Image(systemName: "align.horizontal.right") .itemTag(1, selectArea: 160) { Image(systemName: "align.horizontal.right.fill") } Image(systemName: "align.vertical.top") .itemTag(2, selectArea: 0) { Image(systemName: "align.vertical.top.fill") } Image(systemName: "align.vertical.bottom") .itemTag(3, selectArea: 160) { Image(systemName: "align.vertical.bottom.fill") } } style: { ASBasicStyle() } onTapReceive: { selectionTap in /// Imperative syntax print("---------------------") print("Selection : ", selectionTap) print("Already selected : ", self.selection == selectionTap) } .frame(height: 44) ``` ## Usages - For use without style ```swift var listView: some View { List(0...100, id: \.self) { index in Button { print("click") } label: { Text("Index \(index)") } }.listStyle(.plain) } AxisSegmentedView(selection: $selection, constant: .init()) { Rectangle() .overlay( Text("0") ) .itemTag(0, selectArea: maxArea1) { Rectangle() .overlay( Text("0") ) } Rectangle() .overlay( Text("1") ) .itemTag(1, selectArea: maxArea1) { listView } Rectangle() .overlay( Text("2") ) .itemTag(2, selectArea: maxArea1) { listView } Rectangle() .overlay( Text("3") ) .itemTag(3, selectArea: maxArea1) { listView } } ``` ## Swift Package Manager The Swift Package Manager is a tool for automating the distribution of Swift code and is integrated into the swift compiler. Once you have your Swift package set up, adding AxisSegmentedView as a dependency is as easy as adding it to the dependencies value of your Package.swift. ```swift dependencies: [ .package(url: "https://github.com/jasudev/AxisSegmentedView.git", .branch("main")) ] ``` ## Contact instagram : [@dev.fabula](https://www.instagram.com/dev.fabula) email : [dev.fabula@gmail.com](mailto:dev.fabula@gmail.com) ## License AxisSegmentedView is available under the MIT license. See the [LICENSE](LICENSE) file for more info. ================================================ FILE: Sources/AxisSegmentedView/AxisSegmentedView.swift ================================================ // // AxisSegmentedView.swift // AxisSegmentedView // // Created by jasu on 2022/03/19. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI public struct AxisSegmentedView : View where SelectionValue : Hashable, Content : View, Style : View { @StateObject private var stateValue: ASStateValue = .init() @StateObject private var positionValue: ASPositionValue = .init() @State private var currentSize: CGSize = .zero private let selectionValue: ASSelectionValue private let constant: ASConstant public var content: () -> Content public var style: (() -> Style)? = nil public var body: some View { GeometryReader { proxy in ZStack { if currentSize != proxy.size { if positionValue.toggleSelectArea { getContent(proxy) }else { getContent(proxy) } }else { if positionValue.toggleSelectArea { getContent(proxy) }else { getContent(proxy) } } } } .animation(constant.animation ?? .none, value: selectionValue.selection) .environmentObject(selectionValue) .environmentObject(positionValue) .environmentObject(stateValue) .onChange(of: constant) { newValue in stateValue.constant = newValue positionValue.constant = newValue } } private func getContent(_ proxy: GeometryProxy) -> some View { ZStack { Color.clear if constant.axisMode == .horizontal { HStack(spacing: 0) { content() } }else { VStack(spacing: 0) { content() } } } .onAppear { DispatchQueue.main.async { positionValue.isHasStyle = self.style != nil currentSize = proxy.size } } .backgroundPreferenceValue(ASItemPreferenceKey.self) { items in getBackground(items, size: proxy.size) } } private func getBackground(_ items: [ASItem], size: CGSize) -> some View { return Color.clear .overlay(style?()) .onAppear { positionValue.constant = constant positionValue.items = items positionValue.size = size } } } public extension AxisSegmentedView where SelectionValue: Hashable, Content: View, Style: View { /// Initializes `AxisSegmentedView` /// - Parameters: /// - selection: The currently selected tap value. /// - constant: A constant value for segmented view. /// - content: Content views with tab items applied. /// - style: The style of the segmented view. /// - onTapReceive: Method that treats the currently selected tab as imperative syntax. init(selection: Binding, constant: ASConstant = .init(), @ViewBuilder _ content: @escaping () -> Content, @ViewBuilder style: @escaping () -> Style, onTapReceive: ((SelectionValue) -> Void)? = nil) { self.selectionValue = ASSelectionValue(selection: selection, onTapReceive: onTapReceive) self.constant = constant self.style = style self.content = content } } public extension AxisSegmentedView where SelectionValue: Hashable, Content: View, Style == EmptyView { /// Initializes `AxisSegmentedView` /// - Parameters: /// - selection: The currently selected tap value. /// - constant: A constant value for segmented view. /// - content: Content views with tab items applied. /// - onTapReceive: Method that treats the currently selected tab as imperative syntax. init(selection: Binding, constant: ASConstant = .init(), @ViewBuilder _ content: @escaping () -> Content, onTapReceive: ((SelectionValue) -> Void)? = nil) { self.selectionValue = ASSelectionValue(selection: selection, onTapReceive: onTapReceive) self.constant = constant self.content = content } } ================================================ FILE: Sources/AxisSegmentedView/Private/Model/ASItem.swift ================================================ // // ASItem.swift // AxisSegmentedView // // Created by jasu on 2022/03/18. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI /// Information on the tab button. struct ASItem: Identifiable { let id = UUID() /// A tag that separates the tab view. let tag: Any /// The size of the selected tab. /// If it is less than or equal to 0, it is the same as normal size. let selectArea: CGFloat /// The tab button view in the selected state. let select: AnyView /// Initializes `ASItem` /// - Parameters: /// - tag: A tag that separates the tab view. /// - selectArea: The size of the selected tab. If it is less than or equal to 0, it is the same as normal size. /// - select: The tab button view in the selected state. init(tag: Any, selectArea: CGFloat = 0, select: V) { self.tag = tag self.selectArea = selectArea self.select = AnyView(select) } } ================================================ FILE: Sources/AxisSegmentedView/Private/Modifiers/ASItemModifier.swift ================================================ // // ASItemModifier.swift // AxisSegmentedView // // Created by jasu on 2022/03/18. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI struct ASItemPreferenceKey: PreferenceKey { typealias Value = [ASItem] static var defaultValue: [ASItem] = [] static func reduce(value: inout [ASItem], nextValue: () -> [ASItem]) { value.append(contentsOf: nextValue()) } } struct ASItemModifier: ViewModifier { @EnvironmentObject private var stateValue: ASStateValue @EnvironmentObject private var selectionValue: ASSelectionValue @EnvironmentObject private var positionValue: ASPositionValue @Namespace private var namespace var item: ASItem { return ASItem(tag: tag, selectArea: selectArea, select: AnyView(select)) } var tag: SelectionValue var selectArea: CGFloat var select: S? = nil private var selection: Binding { Binding( get: { self.selectionValue.selection }, set: { self.selectionValue.onTapReceive?($0) self.selectionValue.selection = $0 self.setupStateValue() }) } var normalSize: CGSize { if positionValue.constant.axisMode == .horizontal { return CGSize(width: positionValue.getNormalArea(self.selection.wrappedValue), height: positionValue.size.height) }else { return CGSize(width: positionValue.size.width, height: positionValue.getNormalArea(self.selection.wrappedValue)) } } var selectSize: CGSize { if positionValue.constant.axisMode == .horizontal { return CGSize(width: positionValue.getSelectArea(self.selection.wrappedValue), height: positionValue.size.height) }else { return CGSize(width: positionValue.size.width, height: positionValue.getSelectArea(self.selection.wrappedValue)) } } var divideLineView: some View { ZStack { if positionValue.constant.axisMode == .horizontal { Rectangle() .fill(positionValue.constant.divideLine.color) .frame(width: positionValue.constant.divideLine.width, height: positionValue.size.height) .scaleEffect(CGSize(width: 1, height: positionValue.constant.divideLine.scale)) }else { Rectangle() .fill(positionValue.constant.divideLine.color) .frame(width: positionValue.size.width, height: positionValue.constant.divideLine.width) .scaleEffect(CGSize(width: positionValue.constant.divideLine.scale, height: 1)) } } } func body(content: Content) -> some View { let item = ASItem(tag: tag, selectArea: selectArea, select: AnyView(select)) ZStack(alignment: .leading) { if positionValue.isHasStyle { Button { self.selection.wrappedValue = tag self.stateValue.isInitialRun = false if positionValue.constant.isActivatedVibration { vibration() } } label: { ZStack(alignment: positionValue.constant.axisMode == .horizontal ? .leading : .top) { getItemView(content) .frame(width: getItemSize().width, height: getItemSize().height) } .contentShape(Rectangle()) } .buttonStyle(.plain) .preference(key: ASItemPreferenceKey.self, value: [item]) }else { ZStack { Button { self.selection.wrappedValue = tag self.stateValue.isInitialRun = false if positionValue.constant.isActivatedVibration { vibration() } } label: { content } .contentShape(Rectangle()) .buttonStyle(.plain) .opacity(tag != self.selection.wrappedValue ? 1 : 0) select? .contentShape(Rectangle()) .opacity(tag == self.selection.wrappedValue ? 1 : 0) } .frame(width: getItemSize().width, height: getItemSize().height) .preference(key: ASItemPreferenceKey.self, value: [item]) } if isShowDivideLine() { ZStack { if positionValue.constant.axisMode == .horizontal { divideLineView .offset(x: -positionValue.constant.divideLine.width * 0.5) .matchedGeometryEffect(id: "DivideLine", in: namespace) }else { divideLineView .offset(y: -positionValue.constant.divideLine.width * 0.5) .matchedGeometryEffect(id: "DivideLine", in: namespace) } } } } .onAppear { self.setupStateValue() } .onChange(of: selectArea) { newValue in positionValue.toggleSelectArea.toggle() } } private func getItemSize() -> CGSize { if positionValue.constant.axisMode == .horizontal { return CGSize(width: tag == self.selection.wrappedValue ? selectSize.width : normalSize.width, height: positionValue.size.height) }else { return CGSize(width: positionValue.size.width, height: tag == self.selection.wrappedValue ? selectSize.height : normalSize.height) } } private func isShowDivideLine() -> Bool { let currentIndex = positionValue.indexOfTag(tag) let selectionIndex = positionValue.indexOfTag(self.selection.wrappedValue) if positionValue.constant.divideLine.isShowSelectionLine { return currentIndex != 0 }else { return currentIndex != 0 && currentIndex != selectionIndex && currentIndex != selectionIndex + 1 } } private func getItemView(_ content: Content) -> some View { ZStack { if tag == self.selection.wrappedValue { ZStack { if let select = select { select .onAppear { DispatchQueue.main.async { stateValue.previousIndex = stateValue.selectionIndex stateValue.previousFrame = positionValue.getSelectFrame(self.selection.wrappedValue, selectionIndex: stateValue.selectionIndex) if positionValue.constant.axisMode == .horizontal { stateValue.otherSize = CGSize(width: normalSize.width, height: positionValue.size.height) }else { stateValue.otherSize = CGSize(width: positionValue.size.width, height: normalSize.height) } } } .fixedSize() .matchedGeometryEffect(id: stateValue.constant.isActivatedGeometryEffect ? "ITEM-NAMESPACE-ID" : "\(UUID())", in: namespace) }else { content .fixedSize() .matchedGeometryEffect(id: stateValue.constant.isActivatedGeometryEffect ? "ITEM-NAMESPACE-ID" : "\(UUID())", in: namespace) } } } else { content .fixedSize() .matchedGeometryEffect(id: stateValue.constant.isActivatedGeometryEffect ? "ITEM-NAMESPACE-ID" : "\(UUID())", in: namespace) } } } private func setupStateValue() { let selectionIndex = positionValue.indexOfTag(self.selection.wrappedValue) stateValue.constant = positionValue.constant stateValue.itemCount = positionValue.itemCount stateValue.selectionIndex = selectionIndex stateValue.selectionFrame = positionValue.getSelectFrame(self.selection.wrappedValue, selectionIndex: selectionIndex) stateValue.size = positionValue.size } #if os(iOS) /// The device generates vibrations. /// - Parameter style: Vibration style. private func vibration(_ style: UIImpactFeedbackGenerator.FeedbackStyle = .soft) { let feedback = UIImpactFeedbackGenerator(style: style) feedback.prepare() feedback.impactOccurred() } #else private func vibration() {} #endif } extension ASItemModifier where SelectionValue: Hashable, S: View { init(tag: SelectionValue, selectArea: CGFloat, select: S) { self.tag = tag self.selectArea = selectArea self.select = select } } extension ASItemModifier where SelectionValue: Hashable, S == EmptyView { init(tag: SelectionValue, selectArea: CGFloat) { self.tag = tag self.selectArea = selectArea } } struct ASItemModifier_Previews: PreviewProvider { static var previews: some View { SegmentedViewPreview(constant: .init(axisMode: .horizontal, divideLine: .init(color: .blue.opacity(0.5)))) { ASBasicStyle() } .frame(height: 44) .padding(.horizontal, 16) .preferredColorScheme(.dark) } } ================================================ FILE: Sources/AxisSegmentedView/Private/ViewModel/ASPositionValue.swift ================================================ // // ASPositionValue.swift // AxisSegmentedView // // Created by jasu on 2022/03/18. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI /// A viewmodel that provides the state value of the tab button. class ASPositionValue: ObservableObject { @Published var size: CGSize = .zero @Published var items: [ASItem] = [] @Published var toggleSelectArea: Bool = false @Published var isHasStyle: Bool = true @Published var constant: ASConstant = .init() var itemCount: Int { return items.count } var sizeArea: CGFloat { constant.axisMode == .horizontal ? size.width : size.height } func indexOfTag(_ tag: SelectionValue?) -> Int { guard let tag = tag else { return 0 } return items.firstIndex(where: {$0.tag as! SelectionValue == tag}) ?? 0 } func getNormalArea(_ selection: SelectionValue?) -> CGFloat { let selectArea = getItemSelectArea(selection) let area: CGFloat = selectArea > 0 ? ((sizeArea - selectArea) / CGFloat(itemCount - 1)) : sizeArea / CGFloat(itemCount) return area > 0 ? area : 1 } func getSelectArea(_ selection: SelectionValue?) -> CGFloat { let selectArea = getItemSelectArea(selection) let area: CGFloat = selectArea > 0 ? selectArea : sizeArea / CGFloat(itemCount) return area > 0 ? area : 1 } func getSelectFrame(_ selection: SelectionValue?, selectionIndex: Int) -> CGRect { let normalArea = getNormalArea(selection) let selectArea = getSelectArea(selection) if constant.axisMode == .horizontal { return CGRect(x: normalArea * CGFloat(selectionIndex), y: 0, width: selectArea, height: size.height) }else { return CGRect(x: 0, y: normalArea * CGFloat(selectionIndex), width: size.width, height: selectArea) } } private func getItemSelectArea(_ selection: SelectionValue?) -> CGFloat { guard !items.isEmpty else { return 0 } let selectionIndex = indexOfTag(selection) return items[selectionIndex].selectArea } } struct ASPositionValue_previews: PreviewProvider { static var previews: some View { SegmentedViewPreview { ASBasicStyle() } .frame(height: 44) .padding(.horizontal, 16) } } ================================================ FILE: Sources/AxisSegmentedView/Private/ViewModel/ASSelectionValue.swift ================================================ // // ASSelectionValue.swift // AxisSegmentedView // // Created by jasu on 2022/03/18. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI /// Manage the currently selected tab status. class ASSelectionValue: ObservableObject { /// The currently selected tap value. @Binding var selection: SelectionValue var onTapReceive: ((SelectionValue) -> Void)? /// Initializes `ASSelectionValue` /// - Parameters: /// - selection: The currently selected tap value. /// - onTapReceive: Method that treats the currently selected tab as imperative syntax. init(selection: Binding, onTapReceive: ((SelectionValue) -> Void)? = nil) { _selection = selection self.onTapReceive = onTapReceive } } ================================================ FILE: Sources/AxisSegmentedView/Public/Constants/ASConstant.swift ================================================ // // ASConstant.swift // AxisSegmentedView // // Created by jasu on 2022/03/18. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI /// Motion effect mode in style. public enum ASMovementMode: Hashable { case normal case viscosity } /// The object position mode in style. public enum ASEdgeMode: Hashable { case topLeading case bottomTrailing } /// The axis mode of the segmented view. public enum ASAxisMode: Hashable { case horizontal case vertical } /// Defines the settings for the segmented view. public struct ASConstant: Equatable { /// The axis mode of the segmented view. public var axisMode: ASAxisMode /// Defines the line between the tab buttons. public var divideLine: ASLineConstant /// Activate the geometry effect. public var isActivatedGeometryEffect: Bool /// Activate the device's vibration. Only iOS is supported. public var isActivatedVibration: Bool /// A transition when a tab is selected. public var transition: AnyTransition /// Animation when selecting a tab. public var animation: Animation? /// Initializes `ASConstant` /// - Parameters: /// - axisMode: The axis mode of the segmented view. /// - divideLine: Defines the line between the tab buttons. /// - isActivatedGeometryEffect: Whether to use a GeometryEffect when switching tab buttons. /// - isActivatedVibration: Activate the device's vibration. Only iOS is supported. /// - transition: A transition when a tab is selected. /// - animation: Animation when selecting a tab. public init(axisMode: ASAxisMode = .horizontal, divideLine: ASLineConstant = .init(), isActivatedGeometryEffect: Bool = true, isActivatedVibration: Bool = true, transition: AnyTransition = .opacity, animation: Animation? = .easeInOut) { self.axisMode = axisMode self.divideLine = divideLine self.isActivatedGeometryEffect = isActivatedGeometryEffect self.isActivatedVibration = isActivatedVibration self.transition = transition self.animation = animation } public static func == (lhs: ASConstant, rhs: ASConstant) -> Bool { lhs.axisMode == rhs.axisMode && lhs.divideLine == rhs.divideLine && lhs.isActivatedGeometryEffect == rhs.isActivatedGeometryEffect && lhs.isActivatedVibration == rhs.isActivatedVibration && lhs.animation == rhs.animation } } ================================================ FILE: Sources/AxisSegmentedView/Public/Constants/ASLineConstant.swift ================================================ // // ASLineConstant.swift // AxisSegmentedView // // Created by jasu on 2022/03/20. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI /// Defines the line between the tab buttons. public struct ASLineConstant: Equatable { /// The color of the line. public var color: Color /// The short axis length of the line. public var width: CGFloat /// The length scale of the line's long axis. public var scale: CGFloat /// Whether to show the line around the selected tab. public var isShowSelectionLine: Bool /// Initializes `ASLineConstant` /// - Parameters: /// - color: The color of the line. /// - width: The short axis length of the line. /// - scale: The length scale of the line's long axis. /// - isShowSelectionLine: Whether to show the line around the selected tab. public init(color: Color = .clear, width: CGFloat = 1, scale: CGFloat = 0.6, isShowSelectionLine: Bool = false) { self.color = color self.width = width self.scale = scale self.isShowSelectionLine = isShowSelectionLine } } ================================================ FILE: Sources/AxisSegmentedView/Public/Extensions/Animation+Extensions.swift ================================================ // // Animation+Extensions.swift // AxisSegmentedView // // Created by jasu on 2022/03/20. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI extension Animation { public static var axisSegmentedSpring = Animation.spring(response: 0.36, dampingFraction: 0.8, blendDuration: 0.8) } ================================================ FILE: Sources/AxisSegmentedView/Public/Extensions/View+Extensions.swift ================================================ // // View+Extensions.swift // AxisSegmentedView // // Created by jasu on 2022/03/07. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI extension View { public func itemTag(_ tag: SelectionValue, selectArea: CGFloat = 0) -> some View { modifier(ASItemModifier(tag: tag, selectArea: selectArea)) } public func itemTag(_ tag: SelectionValue, selectArea: CGFloat = 0, @ViewBuilder select: @escaping () -> S) -> some View { modifier(ASItemModifier(tag: tag, selectArea: selectArea, select: select())) } public func outerShadow(offset: CGFloat = 2, radius: CGFloat = 2, opacity: CGFloat = 1, isDark: Bool = true) -> some View { modifier(NeumorphismOuterModifier(offset: offset, radius: radius, opacity: opacity, isDark: isDark)) } public func innerShadow(_ content: S, radius: CGFloat = 6, opacity: CGFloat = 1, isDark: Bool = true) -> some View { modifier(NeumorphismInnerModifier(shape: content, radius: radius, opacity: opacity, isDark: isDark)) } func reverseMask(_ mask: M) -> some View where M: View { self.mask( mask .background(Color.white) .foregroundColor(.black) .compositingGroup() .luminanceToAlpha() ) } } ================================================ FILE: Sources/AxisSegmentedView/Public/Modifiers/NeumorphismInnerModifier.swift ================================================ // // NeumorphismInnerModifier.swift // AxisSegmentedView // // Created by jasu on 2022/03/19. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI public struct NeumorphismInnerModifier : ViewModifier { public var lightColor: Color = .clear public var darkColor: Color = .clear public var shape: T public var radius: CGFloat public var opacity: CGFloat public var isDark: Bool public init(shape: T, radius: CGFloat = 10, opacity: CGFloat = 1, isDark: Bool = true) { self.shape = shape self.radius = radius self.opacity = opacity self.isDark = isDark if isDark { lightColor = .white.opacity(0.35) darkColor = .black.opacity(1.0) }else { lightColor = .white.opacity(1.0) darkColor = .black.opacity(0.35) } } public func body(content: Content) -> some View { content .overlay( GeometryReader { proxy in self.shape.fill(self.lightColor.opacity(shadowOpacity())) .reverseMask( self.shape.offset(x: -self.shadowOffset(proxy), y: -self.shadowOffset(proxy)) ) .blur(radius: self.radius) .offset(x: self.shadowOffset(proxy) , y: self.shadowOffset(proxy)) .overlay( self.shape.fill(self.darkColor.opacity(shadowOpacity())) .reverseMask( self.shape.offset(x: self.shadowOffset(proxy), y: self.shadowOffset(proxy)) ) .blur(radius: self.radius) .offset(x: -self.shadowOffset(proxy) , y: -self.shadowOffset(proxy)) ) .mask(self.shape) } ) } private func shadowOpacity() -> CGFloat { return 3 * max(min(opacity, 1), 0) } private func shadowOffset(_ proxy: GeometryProxy) -> CGFloat { return (proxy.size.width < proxy.size.height ? proxy.size.width : proxy.size.height) * 0.02 } } struct NeumorphismInnerModifier_Previews: PreviewProvider { static var previews: some View { SegmentedViewPreview(constant: .init(axisMode: .horizontal)) { ASNeumorphismStyle(cornerRadius: 16, isInner: false) } .frame(height: 44) .padding(.horizontal, 16) .preferredColorScheme(.dark) } } ================================================ FILE: Sources/AxisSegmentedView/Public/Modifiers/NeumorphismOuterModifier.swift ================================================ // // NeumorphismOuterModifier.swift // AxisSegmentedView // // Created by jasu on 2022/03/19. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI public struct NeumorphismOuterModifier: ViewModifier { public var lightColor : Color = .clear public var darkColor : Color = .clear public var offset: CGFloat public var radius : CGFloat public var opacity: CGFloat public init(offset: CGFloat = 2, radius: CGFloat = 2, opacity: CGFloat = 1, isDark: Bool = true) { self.offset = offset self.radius = radius self.opacity = opacity if isDark { lightColor = .white.opacity(0.10) darkColor = .black.opacity(0.30) }else { lightColor = .white.opacity(1.0) darkColor = .black.opacity(0.20) } } public func body(content: Content) -> some View { content .shadow(color: lightColor.opacity(shadowOpacity()), radius: radius, x: -offset, y: -offset) .shadow(color: darkColor.opacity(shadowOpacity()), radius: radius, x: offset, y: offset) } private func shadowOpacity() -> CGFloat { return 3 * max(min(opacity, 1), 0) } } struct NeumorphismOuterModifier_Previews: PreviewProvider { static var previews: some View { SegmentedViewPreview(constant: .init(axisMode: .vertical)) { ASNeumorphismStyle(cornerRadius: 25) } .frame(width: 50) .padding(.vertical, 16) .preferredColorScheme(.dark) } } ================================================ FILE: Sources/AxisSegmentedView/Public/Previews/SegmentedViewPreview.swift ================================================ // // SegmentedViewPreview.swift // AxisSegmentedView // // Created by jasu on 2022/03/18. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI /// This is a preview for testing. public struct SegmentedViewPreview: View where S : View { @State private var selection: Int = 0 private let constant: ASConstant private let style: () -> S public init(constant: ASConstant = .init(), @ViewBuilder style: @escaping () -> S) { self.constant = constant self.style = style } public var body: some View { GeometryReader { proxy in ZStack { Color.clear AxisSegmentedView(selection: $selection, constant: constant, { iconGroup }, style: { style() }) .font(.system(size: 20)) } } } var iconGroup: some View { Group { Image(systemName: "align.horizontal.left") .itemTag(0, selectArea: 0) { Image(systemName: "align.horizontal.left.fill").foregroundColor(Color.white) } Image(systemName: "align.horizontal.right") .itemTag(1, selectArea: 0) { Image(systemName: "align.horizontal.right.fill").foregroundColor(Color.white) } Image(systemName: "align.vertical.top") .itemTag(2, selectArea: 160) { Image(systemName: "align.vertical.top.fill").foregroundColor(Color.white) } Image(systemName: "align.vertical.bottom") .itemTag(3, selectArea: 0) { Image(systemName: "align.vertical.bottom.fill").foregroundColor(Color.white) } } } var textGroup: some View { Group { Text("버튼1") .font(.callout) .frame(width: 60) .itemTag(0, selectArea: 0) { Text("버튼") .font(.callout) } Text("버튼2") .font(.callout) .itemTag(1, selectArea: 0) { Text("버튼22") .font(.callout) } Text("버튼3") .font(.callout) .itemTag(2, selectArea: 160) { Text("버튼33") .font(.callout) } Text("버튼4") .font(.callout) .itemTag(3, selectArea: 0) { Text("버튼44") .font(.callout) } } } } struct SegmentedViewPreview_previews: PreviewProvider { static var previews: some View { SegmentedViewPreview(constant: .init()) { ASBasicStyle() } .frame(height: 44) .padding(.horizontal, 16) } } ================================================ FILE: Sources/AxisSegmentedView/Public/Shapes/ASCurveShape.swift ================================================ // // ASCurveShape.swift // AxisSegmentedView // // Created by jasu on 2022/03/04. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI /// A shape that expresses a curve. public struct ASCurveShape: Shape { /// The diameter representing the curve. let radius: CGFloat /// A value indicating the depth of the curve. A value between -1 and 1. var depth: CGFloat /// The value of the horizontal position of the curve. A value between 0 and 1. var position: CGFloat /// Animate depth and position. public var animatableData: AnimatablePair { get { AnimatablePair(depth, position) } set { depth = max(min(newValue.first, 1), -1) position = max(min(newValue.second, 1), 0) } } /// Initializes `ATCurveShape` /// - Parameters: /// - radius: The diameter representing the curve. /// - depth: A value indicating the depth of the curve. A value between -1 and 1. /// - position: The value of the horizontal position of the curve. A value between 0 and 1. public init(radius: CGFloat, depth: CGFloat, position: CGFloat) { self.radius = radius self.depth = depth self.position = position } public func path(in rect: CGRect) -> Path { var path = Path() let delta = 1 + (1 - abs(depth)) let half = rect.width * position let curvePoint: CGPoint = CGPoint(x: radius / 1.74, y: (radius / 3.5) * depth) let edgeX: CGFloat = radius / ((2.85 - 0.36 * abs(depth)) - delta) path.move(to: CGPoint(x: half - radius , y: 0)) path.addQuadCurve(to: CGPoint(x: half - curvePoint.x, y: curvePoint.y), control: CGPoint(x: half - edgeX , y: 0)) path.addCurve(to: CGPoint(x: half + curvePoint.x, y: curvePoint.y), control1: CGPoint(x: half - radius / (delta * (3 + (1 - depth))), y: (radius / delta) * depth), control2: CGPoint(x: half + radius / (delta * (3 + (1 - depth))), y: (radius / delta) * depth)) path.addQuadCurve(to: CGPoint(x: half + radius, y: 0), control: CGPoint(x: half + edgeX, y: 0)) path.addLine(to: CGPoint(x: rect.width , y: 0)) path.addLine(to: CGPoint(x: rect.width , y: rect.height)) path.addLine(to: CGPoint(x: 0 , y: rect.height)) path.addLine(to: CGPoint(x: 0 , y: 0)) path.closeSubpath() return path } } struct ASCurveShape_Previews: PreviewProvider { static var previews: some View { ASCurveShape(radius: 60, depth: 0.8, position: 0.5) .stroke() .frame(width: 220, height: 60) } } ================================================ FILE: Sources/AxisSegmentedView/Public/Styles/ASBasicStyle.swift ================================================ // // ASBasicStyle.swift // AxisSegmentedView // // Created by jasu on 2022/03/12. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI /// Basic style for segmented view. public struct ASBasicStyle: View { @EnvironmentObject var stateValue: ASStateValue public var backgroundColor: Color public var foregroundColor: Color public var cornerRadius: CGFloat public var padding: CGFloat public var isApplySelectionCornerRadius: Bool public var movementMode: ASMovementMode public var animation: Animation public init(backgroundColor: Color = .gray, foregroundColor: Color = Color.blue, cornerRadius: CGFloat = 6, padding: CGFloat = 3, isApplySelectionCornerRadius: Bool = true, movementMode: ASMovementMode = .viscosity, animation: Animation = .axisSegmentedSpring) { self.backgroundColor = backgroundColor self.foregroundColor = foregroundColor self.cornerRadius = cornerRadius self.padding = padding self.isApplySelectionCornerRadius = isApplySelectionCornerRadius self.movementMode = movementMode self.animation = animation } private var selectionView: some View { RoundedRectangle(cornerRadius: isApplySelectionCornerRadius ? cornerRadius : 0) .fill(foregroundColor) .padding(padding + 0.5) } public var body: some View { ZStack(alignment: .topLeading) { RoundedRectangle(cornerRadius: cornerRadius) .fill(backgroundColor.opacity(0.2)) .overlay( RoundedRectangle(cornerRadius: cornerRadius) .stroke(lineWidth: 1) .fill(foregroundColor) ) .padding(0.5) switch movementMode { case .normal: ASNormalStyle(animation) { _ in selectionView } case .viscosity: ASViscosityStyle { _ in selectionView } } } .mask(RoundedRectangle(cornerRadius: cornerRadius)) .animation(animation, value: stateValue.selectionIndex) } } struct ASBasicStyle_Previews: PreviewProvider { static var previews: some View { SegmentedViewPreview(constant: .init(divideLine: .init(color: .blue, width: 1, scale: 1))) { ASBasicStyle() .preferredColorScheme(.dark) } .frame(height: 44) .padding(.horizontal, 16) } } ================================================ FILE: Sources/AxisSegmentedView/Public/Styles/ASCapsuleStyle.swift ================================================ // // ASCapsuleStyle.swift // AxisSegmentedView // // Created by jasu on 2022/03/19. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI /// Capsule style for segmented view. public struct ASCapsuleStyle: View { @EnvironmentObject var stateValue: ASStateValue public var backgroundColor: Color public var foregroundColor: Color public var movementMode: ASMovementMode public var animation: Animation public init(backgroundColor: Color = .gray.opacity(0.4), foregroundColor: Color = Color.blue, movementMode: ASMovementMode = .viscosity, animation: Animation = .axisSegmentedSpring) { self.backgroundColor = backgroundColor self.foregroundColor = foregroundColor self.movementMode = movementMode self.animation = animation } private var selectionView: some View { Capsule() .fill(foregroundColor) .padding(3) } public var body: some View { ZStack(alignment: .topLeading) { Capsule() .fill(backgroundColor.opacity(0.2)) Capsule() .stroke(lineWidth: 1) .fill(backgroundColor.opacity(0.2)) .padding(1) switch movementMode { case .normal: ASNormalStyle(animation) { _ in selectionView } case .viscosity: ASViscosityStyle(animation) { _ in selectionView } } } .animation(animation, value: stateValue.selectionIndex) } } struct ASCapsuleStyle_Previews: PreviewProvider { static var previews: some View { SegmentedViewPreview { ASCapsuleStyle() .preferredColorScheme(.dark) } .frame(height: 44) .padding(.horizontal, 16) } } ================================================ FILE: Sources/AxisSegmentedView/Public/Styles/ASJellyStyle.swift ================================================ // // ASJellyStyle.swift // AxisSegmentedView // // Created by jasu on 2022/03/22. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI /// Jelly style for segmented view. public struct ASJellyStyle: View { @EnvironmentObject var stateValue: ASStateValue public var backgroundColor: Color public var foregroundColor: Color public var jellyRadius: CGFloat public var jellyDepth: CGFloat public var jellyEdge: ASEdgeMode public var animation: Animation @State private var currentFrame: CGRect = .zero @State private var depth: CGFloat = 0 public init(backgroundColor: Color = .gray.opacity(0.1), foregroundColor: Color = .purple, jellyRadius: CGFloat = 56, jellyDepth: CGFloat = 0.9, jellyEdge: ASEdgeMode = .bottomTrailing, animation: Animation = .axisSegmentedSpring) { self.backgroundColor = backgroundColor self.foregroundColor = foregroundColor self.jellyRadius = jellyRadius self.jellyDepth = jellyDepth self.jellyEdge = jellyEdge self.animation = animation } private var jellyView: some View { ZStack { if stateValue.constant.axisMode == .horizontal { ZStack { Color.clear Rectangle() .fill(backgroundColor) Rectangle() .fill(foregroundColor) .reverseMask( ASCurveShape(radius: jellyRadius, depth: depth, position: currentFrame.midX / stateValue.size.width) .fill(.black) .scaleEffect(CGSize(width: 1, height: jellyEdge == .bottomTrailing ? -1 : 1)) ) .mask( Rectangle() .fill(foregroundColor) ) } }else { ZStack { Color.clear Rectangle() .fill(backgroundColor) .frame(width: stateValue.size.width) Rectangle() .fill(foregroundColor) .frame(width: stateValue.size.height + 1, height: stateValue.size.width + 1) .reverseMask( ASCurveShape(radius: jellyRadius, depth: depth, position: currentFrame.midY / stateValue.size.height) .fill(.black) ) .mask( Rectangle() .fill(foregroundColor) ) .rotationEffect(Angle(degrees: 90)) .scaleEffect(CGSize(width: jellyEdge == .bottomTrailing ? 1 : -1, height: 1)) } } } .onAppear { self.currentFrame = stateValue.selectionFrame } } public var body: some View { ZStack(alignment: .topLeading) { jellyView ASNormalStyle(animation) { rect in Color.clear .onChange(of: rect) { newValue in updateUI() withAnimation(animation) { self.currentFrame = newValue } } } } .onChange(of: jellyDepth, perform: { newValue in depth = newValue }) .animation(animation, value: stateValue.selectionIndex) } private func updateUI() { withAnimation(.easeInOut(duration: 0.16)) { depth = 0.4 } DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { withAnimation(.spring(response: 0.4, dampingFraction: 0.6, blendDuration: 0.5)) { depth = jellyDepth } } } } struct ASJellyStyle_Previews: PreviewProvider { static var previews: some View { SegmentedViewPreview(constant: .init(axisMode: .horizontal)) { ASJellyStyle() } .frame(height: 44) .padding(.horizontal, 16) .preferredColorScheme(.dark) // SegmentedViewPreview(constant: .init(axisMode: .vertical)) { // ASJellyStyle() // } // .frame(width: 50) // .padding(.horizontal, 16) // .preferredColorScheme(.dark) } } ================================================ FILE: Sources/AxisSegmentedView/Public/Styles/ASLineStyle.swift ================================================ // // ASLineStyle.swift // AxisSegmentedView // // Created by jasu on 2022/03/19. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI /// Line style for segmented view. public struct ASLineStyle: View { @EnvironmentObject var stateValue: ASStateValue public var lineColor: Color public var lineSmallWidth: CGFloat public var lineLargeScale: CGFloat public var lineEdge: ASEdgeMode public var movementMode: ASMovementMode public var animation: Animation public init(lineColor: Color = .blue, lineSmallWidth: CGFloat = 2, lineLargeScale: CGFloat = 1, lineEdge: ASEdgeMode = .bottomTrailing, movementMode: ASMovementMode = .viscosity, animation: Animation = .axisSegmentedSpring) { self.lineColor = lineColor self.lineSmallWidth = lineSmallWidth self.lineLargeScale = lineLargeScale self.lineEdge = lineEdge self.movementMode = movementMode self.animation = animation } private var selectionView: some View { return Group { if lineEdge == .bottomTrailing { Spacer() } ZStack(alignment: .topLeading) { if stateValue.constant.axisMode == .horizontal { Rectangle() .fill(lineColor) .frame(height: lineSmallWidth) .scaleEffect(CGSize(width: lineLargeScale, height: 1)) }else { Rectangle() .fill(lineColor) .frame(width: lineSmallWidth) .scaleEffect(CGSize(width: 1, height: lineLargeScale)) } } if lineEdge == .topLeading { Spacer() } } } public var body: some View { ZStack(alignment: .topLeading) { Color.clear if stateValue.constant.axisMode == .horizontal { switch movementMode { case .normal: ASNormalStyle(animation) { _ in VStack(spacing: 0) { selectionView } } case .viscosity: ASViscosityStyle(animation) { _ in VStack(spacing: 0) { selectionView } } } }else { switch movementMode { case .normal: ASNormalStyle(animation) { _ in HStack(spacing: 0) { selectionView } } case .viscosity: ASViscosityStyle(animation) { _ in HStack(spacing: 0) { selectionView } } } } } .animation(animation, value: stateValue.selectionIndex) } } struct ASLineStyle_Previews: PreviewProvider { static var previews: some View { SegmentedViewPreview(constant: .init(axisMode: .horizontal, divideLine: .init(color: .blue.opacity(0.5), isShowSelectionLine: true))) { ASLineStyle(lineEdge: .bottomTrailing) .preferredColorScheme(.dark) } .frame(height: 44) .padding(.horizontal, 16) } } ================================================ FILE: Sources/AxisSegmentedView/Public/Styles/ASNeumorphismStyle.swift ================================================ // // ASNeumorphismStyle.swift // AxisSegmentedView // // Created by jasu on 2022/03/19. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI /// Neumorphism style for segmented view. public struct ASNeumorphismStyle: View { @Environment(\.colorScheme) private var colorScheme @EnvironmentObject var stateValue: ASStateValue public var backgroundColor: Color public var foregroundColor: Color public var cornerRadius: CGFloat public var padding: CGFloat public var shadowRadius: CGFloat public var shadowOpacity: CGFloat public var isInner: Bool public var movementMode: ASMovementMode public var animation: Animation public init(backgroundColor: Color = .clear, foregroundColor: Color = .clear, cornerRadius: CGFloat = 11, padding: CGFloat = 3, shadowRadius: CGFloat = 5, shadowOpacity: CGFloat = 1, isInner: Bool = false, movementMode: ASMovementMode = .viscosity, animation: Animation = .axisSegmentedSpring) { self.backgroundColor = backgroundColor self.foregroundColor = foregroundColor self.cornerRadius = cornerRadius self.padding = padding self.shadowRadius = shadowRadius self.shadowOpacity = shadowOpacity self.isInner = isInner self.movementMode = movementMode self.animation = animation } public var body: some View { ZStack { if isInner { innerView }else { outerView } } .animation(animation, value: stateValue.selectionIndex) } private var innerView: some View { ZStack(alignment: .topLeading) { RoundedRectangle(cornerRadius: cornerRadius) .fill(getBackgroundColor()) .outerShadow(radius: shadowRadius, opacity: shadowOpacity, isDark: colorScheme == .dark) switch movementMode { case .normal: ASNormalStyle(animation) { _ in selectionView } case .viscosity: ASViscosityStyle(animation) { _ in selectionView } } } .mask(RoundedRectangle(cornerRadius: cornerRadius)) } private var outerView: some View { ZStack(alignment: .topLeading) { RoundedRectangle(cornerRadius: cornerRadius) .fill(getBackgroundColor()) .innerShadow(RoundedRectangle(cornerRadius: cornerRadius), radius: shadowRadius, opacity: shadowOpacity, isDark: colorScheme == .dark) switch movementMode { case .normal: ASNormalStyle(animation) { _ in selectionView } case .viscosity: ASViscosityStyle(animation) { _ in selectionView } } } .mask(RoundedRectangle(cornerRadius: cornerRadius)) } private var selectionView: some View { ZStack { if isInner { RoundedRectangle(cornerRadius: cornerRadius) .fill(getForegroundColor()) .innerShadow(RoundedRectangle(cornerRadius: cornerRadius), radius: shadowRadius, opacity: shadowOpacity, isDark: colorScheme == .dark) .padding(padding) }else { RoundedRectangle(cornerRadius: cornerRadius) .fill(getForegroundColor()) .padding(padding) .outerShadow(radius: shadowRadius, opacity: shadowOpacity, isDark: colorScheme == .dark) } } } private func getBackgroundColor() -> Color { return backgroundColor == .clear ? (colorScheme == .dark ? Color(red: 0.185, green: 0.190, blue: 0.202) : Color(red: 0.926, green: 0.942, blue: 0.952)) : backgroundColor } private func getForegroundColor() -> Color { return foregroundColor == .clear ? (colorScheme == .dark ? Color(red: 0.185, green: 0.190, blue: 0.202) : Color(red: 0.926, green: 0.942, blue: 0.952)) : foregroundColor } } struct ASNeumorphismStyle_Previews: PreviewProvider { static var previews: some View { SegmentedViewPreview(constant: .init(axisMode: .horizontal)) { ASNeumorphismStyle(backgroundColor: .blue.opacity(0.5), cornerRadius: 25, shadowOpacity: 1, isInner: false) .preferredColorScheme(.dark) } .frame(height: 44) .padding(.horizontal, 16) } } ================================================ FILE: Sources/AxisSegmentedView/Public/Styles/ASScaleStyle.swift ================================================ // // ASScaleStyle.swift // AxisSegmentedView // // Created by jasu on 2022/03/20. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI /// Scale style for segmented view. public struct ASScaleStyle: View { @EnvironmentObject var stateValue: ASStateValue public var backgroundColor: Color public var foregroundColor: Color public var cornerRadius: CGFloat public var minimumScale: CGFloat public var animation: Animation public init(backgroundColor: Color = .clear, foregroundColor: Color = Color.blue, cornerRadius: CGFloat = 11, minimumScale: CGFloat = 0.1, animation: Animation = .axisSegmentedSpring) { self.backgroundColor = backgroundColor self.foregroundColor = foregroundColor self.cornerRadius = cornerRadius self.minimumScale = minimumScale self.animation = animation } @State private var scale: CGFloat = 1 @State private var alpha: CGFloat = 1 private var content: some View { ASNormalStyle(animation) { _ in RoundedRectangle(cornerRadius: cornerRadius) .fill(foregroundColor) .padding(3) .scaleEffect(scale) .opacity(alpha) .transition(.opacity) } } public var body: some View { ZStack(alignment: .topLeading) { Color.clear content } .background(backgroundColor) .onChange(of: stateValue.selectionIndex) { newValue in withAnimation(.easeInOut(duration: 0.3)) { scale = minimumScale alpha = 0 } DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { withAnimation(animation) { scale = 1.0 alpha = 1.0 } } } } } struct ASScaleStyle_Previews: PreviewProvider { static var previews: some View { SegmentedViewPreview(constant: .init(axisMode: .horizontal, divideLine: .init(color: .blue.opacity(0.4), scale: 0.34))) { ASScaleStyle(cornerRadius: 5) } .frame(height: 44) .padding(.horizontal, 16) .preferredColorScheme(.dark) } } ================================================ FILE: Sources/AxisSegmentedView/Public/Styles/Movement/ASNormalStyle.swift ================================================ // // ASNormalStyle.swift // AxisSegmentedView // // Created by jasu on 2022/03/22. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI /// Normal movement style for segmented view. public struct ASNormalStyle: View where Content: View { @EnvironmentObject var stateValue: ASStateValue @State private var currentFrame: CGRect = .zero public var animation: Animation public var selectionView: (CGRect) -> Content public init(_ animation: Animation = .axisSegmentedSpring, @ViewBuilder selectionView: @escaping (CGRect) -> Content) { self.animation = animation self.selectionView = selectionView } private var content: some View { let rect = stateValue.selectionFrame return selectionView(currentFrame) .frame(width: rect.width, height: rect.height) .offset(x: rect.origin.x, y: rect.origin.y) } public var body: some View { ZStack(alignment: .topLeading) { Color.clear content } .onAppear { currentFrame = stateValue.selectionFrame } .onChange(of: stateValue.selectionFrame) { newValue in if stateValue.isInitialRun { currentFrame = newValue }else { withAnimation(animation) { currentFrame = newValue } } } } } struct ASNormalStyle_Previews: PreviewProvider { static var previews: some View { SegmentedViewPreview(constant: .init(axisMode: .horizontal, divideLine: .init(color: .blue.opacity(0.4), scale: 0.34))) { ASNormalStyle { _ in RoundedRectangle(cornerRadius: 11) .stroke(lineWidth: 1) .fill(Color.blue) .padding(1) } .preferredColorScheme(.dark) } .frame(height: 44) .padding(.horizontal, 16) } } ================================================ FILE: Sources/AxisSegmentedView/Public/Styles/Movement/ASViscosityStyle.swift ================================================ // // ASViscosityStyle.swift // AxisSegmentedView // // Created by jasu on 2022/03/22. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI /// Viscosity movement style for segmented view. public struct ASViscosityStyle: View where Content: View { @EnvironmentObject var stateValue: ASStateValue @State private var currentFrame: CGRect = .zero public var animation: Animation public var selectionView: (CGRect) -> Content public init(_ animation: Animation = .axisSegmentedSpring, @ViewBuilder selectionView: @escaping (CGRect) -> Content) { self.animation = animation self.selectionView = selectionView } private var content: some View { selectionView(currentFrame) .frame(width: currentFrame.width, height: currentFrame.height) .offset(x: currentFrame.origin.x, y: currentFrame.origin.y) } public var body: some View { ZStack(alignment: .topLeading) { Color.clear content } .onAppear { currentFrame = stateValue.selectionFrame } .onChange(of: stateValue.selectionFrame) { newValue in if stateValue.isInitialRun { currentFrame = newValue }else { withAnimation(.easeInOut(duration: 0.24)) { currentFrame = getExpandRect() } DispatchQueue.main.asyncAfter(deadline: .now() + 0.24) { withAnimation(animation) { currentFrame = newValue } } } } } private func getExpandRect() -> CGRect { let selectionFrame = stateValue.selectionFrame let previousFrame = stateValue.previousFrame if stateValue.previousIndex < stateValue.selectionIndex { if stateValue.constant.axisMode == .horizontal { return CGRect(x: currentFrame.origin.x, y: currentFrame.origin.y, width: selectionFrame.origin.x + selectionFrame.width - currentFrame.origin.x, height: selectionFrame.height) }else { return CGRect(x: currentFrame.origin.x, y: currentFrame.origin.y, width: selectionFrame.width, height: selectionFrame.origin.y + selectionFrame.height - currentFrame.origin.y) } }else { if stateValue.constant.axisMode == .horizontal { return CGRect(x: selectionFrame.origin.x, y: selectionFrame.origin.y, width: currentFrame.origin.x + currentFrame.width - selectionFrame.origin.x, height: previousFrame.height) }else { return CGRect(x: selectionFrame.origin.x, y: selectionFrame.origin.y, width: previousFrame.width, height: currentFrame.origin.y + currentFrame.height - selectionFrame.origin.y) } } } } struct ASViscosityStyle_Previews: PreviewProvider { static var previews: some View { SegmentedViewPreview(constant: .init(axisMode: .horizontal, divideLine: .init(color: .blue.opacity(0.4), scale: 0.34))) { ASViscosityStyle { rect in RoundedRectangle(cornerRadius: 11) .stroke(lineWidth: 1) .fill(Color.blue) .padding(1) } .preferredColorScheme(.dark) } .frame(height: 44) .padding(.horizontal, 16) } } ================================================ FILE: Sources/AxisSegmentedView/Public/ViewModel/ASStateValue.swift ================================================ // // ASStateValue.swift // AxisSegmentedView // // Created by jasu on 2022/03/19. // Copyright (c) 2022 jasu All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is furnished // to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import SwiftUI /// An environment viewmodel that can be referenced by styles. public class ASStateValue: ObservableObject { /// Check if it is in the first running state. /// Used to remove animation at first startup. public var isInitialRun: Bool = true /// A constant value for segmented view. @Published public var constant: ASConstant = .init() /// Total number of tab buttons. @Published public var itemCount: Int = 0 /// The previously selected position index value. @Published public var previousIndex: Int = 0 /// The previously selected position frame value. @Published public var previousFrame: CGRect = .zero /// The currently selected position index value. @Published public var selectionIndex: Int = 0 /// The currently selected position frame value. @Published public var selectionFrame: CGRect = .zero /// The size of the tab button that is not currently selected. @Published public var otherSize: CGSize = .zero /// The full size of the segmented view. @Published public var size: CGSize = .zero } ================================================ FILE: Tests/AxisSegmentedViewTests/AxisSegmentedViewTests.swift ================================================ import XCTest @testable import AxisSegmentedView final class AxisSegmentedViewTests: XCTestCase { func testExample() throws { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct // results. // XCTAssertEqual(AxisSegmentedView().text, "Hello, World!") } }