diff --git a/README.md b/README.md index 6a054d1c..09f06306 100644 --- a/README.md +++ b/README.md @@ -4,110 +4,92 @@ ![Pod License](https://img.shields.io/cocoapods/l/SimpleRoulette.svg?style=flat) [![Pod Version](https://img.shields.io/cocoapods/v/SimpleRoulette.svg?style=flat)](http://cocoapods.org/pods/SimpleRoulette) ![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat) -![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg)] +![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg) + +--- ## SimpleRoulette -SimpleRoulette is a UIView to make customizable Roulette. +SimpleRoulette helps you to create customizable Roulette **both UIView and View**. + +--- ## Demo ![demo](https://github.com/fummicc1/SimpleRoulette/blob/master/Assets/demo_0.0.2.gif) ![demo](https://github.com/fummicc1/SimpleRoulette/blob/master/Assets/demo_0.0.5.gif) +--- + ## Install ### Swift Package Manager Create `Package.swift` and add dependency like following. ```swift dependencies: [ - .package(url: "https://github.com/fummicc1/SimpleRoulette.git", from: "0.1.1") + .package(url: "https://github.com/fummicc1/SimpleRoulette.git", from: "0.2.0") ] ``` -## Cocoapods +### Cocoapods Create `Podfile` and add dependency like following. ```ruby -pod 'SimpleRoulette', '~> 0.1' +pod 'SimpleRoulette', '~> 0.2' ``` -## Carthage +### Carthage Create `Cartfile` and add dependency like following. ``` github "fummicc1/SimpleRoulette" ``` +--- ## Usage +### UIKit without Storyboard + 1. First, create RouletteView instance. ```swift let rouletteView: RouletteView = .init(frame: .zero) ``` -or you can initiate RouletteView with Storyboard. - -```swift -@IBOutlet weak var rouletteView: RouletteView! -``` - -2. Next, insert parts with `RouletteView().configure`. +2. Next, insert parts with `RouletteView#.configure`. You can choose parts from [Roulette.AnglePart](https://github.com/fummicc1/SimpleRoulette/blob/41d77fb2a98f0112a13b1e5fa58ed096bd572142/SimpleRoulette/Sources/RoulettePart.swift#L57) or [Roulette.HugePart](https://github.com/fummicc1/SimpleRoulette/blob/41d77fb2a98f0112a13b1e5fa58ed096bd572142/SimpleRoulette/Sources/RoulettePart.swift#L29). -1. Start Roulette by `RouletteView().start`. +1. Start Roulette by `RouletteView#.start`. You can check if Rotating via `RouletteView().isAnimating`. -4. Stop Roulette by `RouletteView().stop` +4. Stop Roulette by `RouletteView#.stop` 5. Detect when stopping roulette using `RouletteViewDelegate`. -- Example +### UIKit with Storyboard +you can use RouletteView with Storyboard. ```swift -class IBRouletteViewController: UIViewController { - - @IBOutlet var rouletteView: RouletteView! - - override func viewDidLoad() { - super.viewDidLoad() - rouletteView.delegate = self - rouletteView.configure(parts: [ - Roulette.HugePart(name: "Title A", huge: .large, delegate: rouletteView, index: 0), - Roulette.HugePart(name: "Title B", huge: .small, delegate: rouletteView, index: 1), - Roulette.HugePart(name: "Title C", huge: .normal, delegate: rouletteView, index: 2), - Roulette.HugePart(name: "Title D", huge: .small, delegate: rouletteView, index: 3), - ]) - - rouletteView.start() - - // can check if animating - // if rouletteView.isAnimating { } - - DispatchQueue.main.asyncAfter(deadline: .now() + 3) { - self.rouletteView.stop() - } - } -} - -extension IBRouletteViewController: RouletteViewDelegate { - func rouletteView(_ rouletteView: RouletteView, didStopAt part: RoulettePartType) { - let alert = UIAlertController(title: "結果", message: part.name, preferredStyle: .alert) - alert.addAction(UIAlertAction(title: "閉じる", style: .default, handler: nil)) - present(alert, animated: true, completion: nil) - } -} +@IBOutlet weak var rouletteView: RouletteView! ``` -![example](https://github.com/fummicc1/SimpleRoulette/blob/master/Assets/Alert.jpeg) +### SwiftUI +Documentation is not ready. please contribute if possilble :) + +--- + +## Example [WIP] +- [UIKit without Storyboard]() +- [UIKit with Storyboard]() +- [SwiftUI]() -### Sample Code of updating RouletteView().parts. -#### update with Angle -Create `Roulette.AnglePart`. +--- -**you can choose radian or degree** +## About RoulettePartType +`RoulettePartType` is either `Roulette.Huge` or `Roulette.AnglePart`. +### About Roulette.AnglePart +This struct needs both degrees from start to end precisely like the following. ```swift rouletteView.configure(parts: [ @@ -117,9 +99,11 @@ rouletteView.configure(parts: [ ]) ``` -#### update with Huge +If you think configuring each degree is a bit of troublesome, please use `Roulette.HugePart`. -Create `Roulette.HugePart`. +### About Roulette.HugePart +This struct does not need both degrees from start to end precisely like the following. +Just, specify `delegate` and `kind`. ```swift rouletteView.configure(parts: [ @@ -131,6 +115,13 @@ rouletteView.configure(parts: [ **IMPORTANT: can not combine Huge with Angle in RouletteView().configure.** +--- + +## Contributing + +Pull requests, bug reports and feature requests are welcome 🚀 + +--- ## License [MIT LICENSE](https://github.com/fummicc1/SimpleRoulette/blob/master/LICENSE) diff --git a/SimpleRoulette.podspec b/SimpleRoulette.podspec index 633c3209..5788ed10 100644 --- a/SimpleRoulette.podspec +++ b/SimpleRoulette.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |spec| # spec.name = "SimpleRoulette" - spec.version = "0.1.1" + spec.version = "0.2.0" spec.summary = "Create Roulette with ease." # This description is used to generate tags and improve search results. @@ -24,7 +24,7 @@ Pod::Spec.new do |spec| # * Try to keep it short, snappy and to the point. # * Write the description between the DESC delimiters below. # * Finally, don't worry about the indent, CocoaPods strips it! - spec.description = "SimpleRoulette is a UIView to make customizable Roulette." + spec.description = "SimpleRoulette helps you to create customizable Roulette both UIView and View." spec.homepage = "https://github.com/fummicc1/SimpleRoulette" # spec.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" @@ -80,7 +80,7 @@ Pod::Spec.new do |spec| # Supports git, hg, bzr, svn and HTTP. # - spec.source = { :git => "https://github.com/fummicc1/SimpleRoulette.git", :tag => "v0.1.1" } + spec.source = { :git => "https://github.com/fummicc1/SimpleRoulette.git", :tag => "v0.2.0" } # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # diff --git a/SimpleRoulette.xcodeproj/project.pbxproj b/SimpleRoulette.xcodeproj/project.pbxproj index e1e59213..2949f01d 100644 --- a/SimpleRoulette.xcodeproj/project.pbxproj +++ b/SimpleRoulette.xcodeproj/project.pbxproj @@ -9,6 +9,16 @@ /* Begin PBXBuildFile section */ D3112F7F2486B3B900F1E174 /* Calculation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3112F7E2486B3B900F1E174 /* Calculation.swift */; }; D316BBE424868C8000D1844A /* OperatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D316BBE324868C8000D1844A /* OperatorTests.swift */; }; + D33835A72545515200403222 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D33835A62545515200403222 /* AppDelegate.swift */; }; + D33835A92545515200403222 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D33835A82545515200403222 /* SceneDelegate.swift */; }; + D33835AB2545515200403222 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D33835AA2545515200403222 /* ViewController.swift */; }; + D33835AE2545515200403222 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D33835AC2545515200403222 /* Main.storyboard */; }; + D33835B02545515300403222 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D33835AF2545515300403222 /* Assets.xcassets */; }; + D33835B32545515300403222 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D33835B12545515300403222 /* LaunchScreen.storyboard */; }; + D33835D32545522E00403222 /* SimpleRouletteDemoSwiftUIApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D33835D22545522E00403222 /* SimpleRouletteDemoSwiftUIApp.swift */; }; + D33835D52545522E00403222 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D33835D42545522E00403222 /* ContentView.swift */; }; + D33835D72545523000403222 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D33835D62545523000403222 /* Assets.xcassets */; }; + D33835DA2545523000403222 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D33835D92545523000403222 /* Preview Assets.xcassets */; }; D34054EA248643C2005D54FB /* RouletteAngleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D34054E9248643C2005D54FB /* RouletteAngleTests.swift */; }; D35C37292480A1F70029CDA8 /* SimpleRoulette.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D35C371F2480A1F70029CDA8 /* SimpleRoulette.framework */; }; D35C37302480A1F70029CDA8 /* SimpleRoulette.h in Headers */ = {isa = PBXBuildFile; fileRef = D35C37222480A1F70029CDA8 /* SimpleRoulette.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -19,8 +29,6 @@ D35C374C2480A2290029CDA8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D35C374A2480A2290029CDA8 /* LaunchScreen.storyboard */; }; D35C37572480A2290029CDA8 /* SimpleRouletteDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D35C37562480A2290029CDA8 /* SimpleRouletteDemoTests.swift */; }; D35C37622480A22A0029CDA8 /* SimpleRouletteDemoUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D35C37612480A22A0029CDA8 /* SimpleRouletteDemoUITests.swift */; }; - D3693B092487DEDF00CDAB41 /* IBRouletteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3693B082487DEDF00CDAB41 /* IBRouletteViewController.swift */; }; - D3693B0C2487DEF000CDAB41 /* IBRouletteViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D3693B0B2487DEF000CDAB41 /* IBRouletteViewController.storyboard */; }; D37D9345251CB44D00607AD0 /* Double+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = D37D9335251CB44D00607AD0 /* Double+Ex.swift */; }; D37D9346251CB44D00607AD0 /* CGFloat+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = D37D9336251CB44D00607AD0 /* CGFloat+Ex.swift */; }; D37D9347251CB44D00607AD0 /* RoulettePointView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D37D9338251CB44D00607AD0 /* RoulettePointView.swift */; }; @@ -37,6 +45,11 @@ D37D9352251CB44D00607AD0 /* RouletteAngle.swift in Sources */ = {isa = PBXBuildFile; fileRef = D37D9344251CB44D00607AD0 /* RouletteAngle.swift */; }; D3C1267624827B470057D327 /* SimpleRoulette.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D35C371F2480A1F70029CDA8 /* SimpleRoulette.framework */; }; D3C1267724827B470057D327 /* SimpleRoulette.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D35C371F2480A1F70029CDA8 /* SimpleRoulette.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D3C8F83F25303490008D7E08 /* RoulettePartSwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3C8F83E25303490008D7E08 /* RoulettePartSwiftUIView.swift */; }; + D3C8F845253037C4008D7E08 /* Color+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3C8F844253037C4008D7E08 /* Color+Ex.swift */; }; + D3D26B0C2524CE32002D2516 /* RouletteViewSwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3D26B0B2524CE32002D2516 /* RouletteViewSwiftUI.swift */; }; + D3D26B122524D008002D2516 /* RouletteViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3D26B112524D008002D2516 /* RouletteViewModel.swift */; }; + D3D26B182524E543002D2516 /* RouletteShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3D26B172524E543002D2516 /* RouletteShape.swift */; }; D3F0040C248F641600C81C87 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = D3F0040B248F641600C81C87 /* README.md */; }; /* End PBXBuildFile section */ @@ -88,6 +101,20 @@ /* Begin PBXFileReference section */ D3112F7E2486B3B900F1E174 /* Calculation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Calculation.swift; sourceTree = ""; }; D316BBE324868C8000D1844A /* OperatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperatorTests.swift; sourceTree = ""; }; + D33835A42545515200403222 /* SimpleRouletteDemoStoryboard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SimpleRouletteDemoStoryboard.app; sourceTree = BUILT_PRODUCTS_DIR; }; + D33835A62545515200403222 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + D33835A82545515200403222 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + D33835AA2545515200403222 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + D33835AD2545515200403222 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + D33835AF2545515300403222 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + D33835B22545515300403222 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + D33835B42545515300403222 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D33835D02545522E00403222 /* SimpleRouletteDemoSwiftUI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SimpleRouletteDemoSwiftUI.app; sourceTree = BUILT_PRODUCTS_DIR; }; + D33835D22545522E00403222 /* SimpleRouletteDemoSwiftUIApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleRouletteDemoSwiftUIApp.swift; sourceTree = ""; }; + D33835D42545522E00403222 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + D33835D62545523000403222 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + D33835D92545523000403222 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + D33835DB2545523000403222 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D34054E9248643C2005D54FB /* RouletteAngleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouletteAngleTests.swift; sourceTree = ""; }; D35C371F2480A1F70029CDA8 /* SimpleRoulette.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SimpleRoulette.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D35C37222480A1F70029CDA8 /* SimpleRoulette.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SimpleRoulette.h; sourceTree = ""; }; @@ -107,8 +134,6 @@ D35C375D2480A22A0029CDA8 /* SimpleRouletteDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SimpleRouletteDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; D35C37612480A22A0029CDA8 /* SimpleRouletteDemoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleRouletteDemoUITests.swift; sourceTree = ""; }; D35C37632480A22A0029CDA8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D3693B082487DEDF00CDAB41 /* IBRouletteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IBRouletteViewController.swift; sourceTree = ""; }; - D3693B0B2487DEF000CDAB41 /* IBRouletteViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = IBRouletteViewController.storyboard; sourceTree = ""; }; D37D9335251CB44D00607AD0 /* Double+Ex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Double+Ex.swift"; sourceTree = ""; }; D37D9336251CB44D00607AD0 /* CGFloat+Ex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGFloat+Ex.swift"; sourceTree = ""; }; D37D9338251CB44D00607AD0 /* RoulettePointView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoulettePointView.swift; sourceTree = ""; }; @@ -123,10 +148,29 @@ D37D9342251CB44D00607AD0 /* RouletteView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouletteView.swift; sourceTree = ""; }; D37D9343251CB44D00607AD0 /* RoulettePart.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoulettePart.swift; sourceTree = ""; }; D37D9344251CB44D00607AD0 /* RouletteAngle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouletteAngle.swift; sourceTree = ""; }; + D3C8F83E25303490008D7E08 /* RoulettePartSwiftUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoulettePartSwiftUIView.swift; sourceTree = ""; }; + D3C8F844253037C4008D7E08 /* Color+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Ex.swift"; sourceTree = ""; }; + D3D26B0B2524CE32002D2516 /* RouletteViewSwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouletteViewSwiftUI.swift; sourceTree = ""; }; + D3D26B112524D008002D2516 /* RouletteViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouletteViewModel.swift; sourceTree = ""; }; + D3D26B172524E543002D2516 /* RouletteShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouletteShape.swift; sourceTree = ""; }; D3F0040B248F641600C81C87 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + D33835A12545515200403222 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D33835CD2545522E00403222 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; D35C371C2480A1F70029CDA8 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -167,6 +211,40 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + D33835A52545515200403222 /* SimpleRouletteDemoStoryboard */ = { + isa = PBXGroup; + children = ( + D33835A62545515200403222 /* AppDelegate.swift */, + D33835A82545515200403222 /* SceneDelegate.swift */, + D33835AA2545515200403222 /* ViewController.swift */, + D33835AC2545515200403222 /* Main.storyboard */, + D33835AF2545515300403222 /* Assets.xcassets */, + D33835B12545515300403222 /* LaunchScreen.storyboard */, + D33835B42545515300403222 /* Info.plist */, + ); + path = SimpleRouletteDemoStoryboard; + sourceTree = ""; + }; + D33835D12545522E00403222 /* SimpleRouletteDemoSwiftUI */ = { + isa = PBXGroup; + children = ( + D33835D22545522E00403222 /* SimpleRouletteDemoSwiftUIApp.swift */, + D33835D42545522E00403222 /* ContentView.swift */, + D33835D62545523000403222 /* Assets.xcassets */, + D33835DB2545523000403222 /* Info.plist */, + D33835D82545523000403222 /* Preview Content */, + ); + path = SimpleRouletteDemoSwiftUI; + sourceTree = ""; + }; + D33835D82545523000403222 /* Preview Content */ = { + isa = PBXGroup; + children = ( + D33835D92545523000403222 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; D35C37152480A1F70029CDA8 = { isa = PBXGroup; children = ( @@ -176,6 +254,8 @@ D35C373E2480A2260029CDA8 /* SimpleRouletteDemo */, D35C37552480A2290029CDA8 /* SimpleRouletteDemoTests */, D35C37602480A22A0029CDA8 /* SimpleRouletteDemoUITests */, + D33835A52545515200403222 /* SimpleRouletteDemoStoryboard */, + D33835D12545522E00403222 /* SimpleRouletteDemoSwiftUI */, D35C37202480A1F70029CDA8 /* Products */, D3C1267524827B470057D327 /* Frameworks */, ); @@ -189,6 +269,8 @@ D35C373D2480A2260029CDA8 /* SimpleRouletteDemo.app */, D35C37522480A2290029CDA8 /* SimpleRouletteDemoTests.xctest */, D35C375D2480A22A0029CDA8 /* SimpleRouletteDemoUITests.xctest */, + D33835A42545515200403222 /* SimpleRouletteDemoStoryboard.app */, + D33835D02545522E00403222 /* SimpleRouletteDemoSwiftUI.app */, ); name = Products; sourceTree = ""; @@ -222,7 +304,6 @@ D35C37412480A2260029CDA8 /* SceneDelegate.swift */, D35C37432480A2260029CDA8 /* ViewController.swift */, D35C37482480A2290029CDA8 /* Assets.xcassets */, - D3693B0D2487DF5600CDAB41 /* IB */, D35C374A2480A2290029CDA8 /* LaunchScreen.storyboard */, ); path = SimpleRouletteDemo; @@ -246,18 +327,10 @@ path = SimpleRouletteDemoUITests; sourceTree = ""; }; - D3693B0D2487DF5600CDAB41 /* IB */ = { - isa = PBXGroup; - children = ( - D3693B082487DEDF00CDAB41 /* IBRouletteViewController.swift */, - D3693B0B2487DEF000CDAB41 /* IBRouletteViewController.storyboard */, - ); - path = IB; - sourceTree = ""; - }; D37D9333251CB44D00607AD0 /* Sources */ = { isa = PBXGroup; children = ( + D3D26B0A2524CE12002D2516 /* SwiftUI */, D37D9334251CB44D00607AD0 /* Ex */, D37D9337251CB44D00607AD0 /* Internal */, D37D9342251CB44D00607AD0 /* RouletteView.swift */, @@ -272,6 +345,7 @@ children = ( D37D9335251CB44D00607AD0 /* Double+Ex.swift */, D37D9336251CB44D00607AD0 /* CGFloat+Ex.swift */, + D3C8F844253037C4008D7E08 /* Color+Ex.swift */, ); path = Ex; sourceTree = ""; @@ -307,6 +381,17 @@ name = Frameworks; sourceTree = ""; }; + D3D26B0A2524CE12002D2516 /* SwiftUI */ = { + isa = PBXGroup; + children = ( + D3D26B0B2524CE32002D2516 /* RouletteViewSwiftUI.swift */, + D3D26B112524D008002D2516 /* RouletteViewModel.swift */, + D3D26B172524E543002D2516 /* RouletteShape.swift */, + D3C8F83E25303490008D7E08 /* RoulettePartSwiftUIView.swift */, + ); + path = SwiftUI; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -321,6 +406,40 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + D33835A32545515200403222 /* SimpleRouletteDemoStoryboard */ = { + isa = PBXNativeTarget; + buildConfigurationList = D33835B52545515300403222 /* Build configuration list for PBXNativeTarget "SimpleRouletteDemoStoryboard" */; + buildPhases = ( + D33835A02545515200403222 /* Sources */, + D33835A12545515200403222 /* Frameworks */, + D33835A22545515200403222 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SimpleRouletteDemoStoryboard; + productName = SimpleRouletteDemoStoryboard; + productReference = D33835A42545515200403222 /* SimpleRouletteDemoStoryboard.app */; + productType = "com.apple.product-type.application"; + }; + D33835CF2545522E00403222 /* SimpleRouletteDemoSwiftUI */ = { + isa = PBXNativeTarget; + buildConfigurationList = D33835DC2545523000403222 /* Build configuration list for PBXNativeTarget "SimpleRouletteDemoSwiftUI" */; + buildPhases = ( + D33835CC2545522E00403222 /* Sources */, + D33835CD2545522E00403222 /* Frameworks */, + D33835CE2545522E00403222 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SimpleRouletteDemoSwiftUI; + productName = SimpleRouletteDemoSwiftUI; + productReference = D33835D02545522E00403222 /* SimpleRouletteDemoSwiftUI.app */; + productType = "com.apple.product-type.application"; + }; D35C371E2480A1F70029CDA8 /* SimpleRoulette */ = { isa = PBXNativeTarget; buildConfigurationList = D35C37332480A1F70029CDA8 /* Build configuration list for PBXNativeTarget "SimpleRoulette" */; @@ -418,10 +537,16 @@ D35C37162480A1F70029CDA8 /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1150; + LastSwiftUpdateCheck = 1210; LastUpgradeCheck = 1150; ORGANIZATIONNAME = "Fumiya Tanaka"; TargetAttributes = { + D33835A32545515200403222 = { + CreatedOnToolsVersion = 12.1; + }; + D33835CF2545522E00403222 = { + CreatedOnToolsVersion = 12.1; + }; D35C371E2480A1F70029CDA8 = { CreatedOnToolsVersion = 11.5; LastSwiftMigration = 1150; @@ -460,11 +585,32 @@ D35C373C2480A2260029CDA8 /* SimpleRouletteDemo */, D35C37512480A2290029CDA8 /* SimpleRouletteDemoTests */, D35C375C2480A2290029CDA8 /* SimpleRouletteDemoUITests */, + D33835A32545515200403222 /* SimpleRouletteDemoStoryboard */, + D33835CF2545522E00403222 /* SimpleRouletteDemoSwiftUI */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + D33835A22545515200403222 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D33835B32545515300403222 /* LaunchScreen.storyboard in Resources */, + D33835B02545515300403222 /* Assets.xcassets in Resources */, + D33835AE2545515200403222 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D33835CE2545522E00403222 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D33835DA2545523000403222 /* Preview Assets.xcassets in Resources */, + D33835D72545523000403222 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; D35C371D2480A1F70029CDA8 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -486,7 +632,6 @@ files = ( D35C374C2480A2290029CDA8 /* LaunchScreen.storyboard in Resources */, D35C37492480A2290029CDA8 /* Assets.xcassets in Resources */, - D3693B0C2487DEF000CDAB41 /* IBRouletteViewController.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -507,15 +652,39 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + D33835A02545515200403222 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D33835AB2545515200403222 /* ViewController.swift in Sources */, + D33835A72545515200403222 /* AppDelegate.swift in Sources */, + D33835A92545515200403222 /* SceneDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D33835CC2545522E00403222 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D33835D52545522E00403222 /* ContentView.swift in Sources */, + D33835D32545522E00403222 /* SimpleRouletteDemoSwiftUIApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; D35C371B2480A1F70029CDA8 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( D37D9345251CB44D00607AD0 /* Double+Ex.swift in Sources */, D37D934B251CB44D00607AD0 /* RoulettePartTextLayer.swift in Sources */, + D3D26B122524D008002D2516 /* RouletteViewModel.swift in Sources */, + D3C8F845253037C4008D7E08 /* Color+Ex.swift in Sources */, D37D934F251CB44D00607AD0 /* AccurateDouble.swift in Sources */, + D3C8F83F25303490008D7E08 /* RoulettePartSwiftUIView.swift in Sources */, + D3D26B182524E543002D2516 /* RouletteShape.swift in Sources */, D37D934C251CB44D00607AD0 /* AccuracyType.swift in Sources */, D37D9350251CB44D00607AD0 /* RouletteView.swift in Sources */, + D3D26B0C2524CE32002D2516 /* RouletteViewSwiftUI.swift in Sources */, D37D934A251CB44D00607AD0 /* RoulettePartView.swift in Sources */, D37D934E251CB44D00607AD0 /* AccurateCGFloat.swift in Sources */, D37D9347251CB44D00607AD0 /* RoulettePointView.swift in Sources */, @@ -545,7 +714,6 @@ D35C37442480A2260029CDA8 /* ViewController.swift in Sources */, D35C37402480A2260029CDA8 /* AppDelegate.swift in Sources */, D35C37422480A2260029CDA8 /* SceneDelegate.swift in Sources */, - D3693B092487DEDF00CDAB41 /* IBRouletteViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -591,6 +759,22 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ + D33835AC2545515200403222 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + D33835AD2545515200403222 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + D33835B12545515300403222 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + D33835B22545515300403222 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; D35C374A2480A2290029CDA8 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; children = ( @@ -602,6 +786,94 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + D33835B62545515300403222 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 2X6AD4W323; + INFOPLIST_FILE = SimpleRouletteDemoStoryboard/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.fummicc1.sample.SimpleRouletteDemoStoryboard; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + D33835B72545515300403222 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 2X6AD4W323; + INFOPLIST_FILE = SimpleRouletteDemoStoryboard/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.fummicc1.sample.SimpleRouletteDemoStoryboard; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + D33835DD2545523000403222 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"SimpleRouletteDemoSwiftUI/Preview Content\""; + DEVELOPMENT_TEAM = 2X6AD4W323; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = SimpleRouletteDemoSwiftUI/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.fummicc1.sample.SimpleRouletteDemoSwiftUI; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + D33835DE2545523000403222 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"SimpleRouletteDemoSwiftUI/Preview Content\""; + DEVELOPMENT_TEAM = 2X6AD4W323; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = SimpleRouletteDemoSwiftUI/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.fummicc1.sample.SimpleRouletteDemoSwiftUI; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; D35C37312480A1F70029CDA8 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -732,6 +1004,11 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "SIMPLEROULETTE=1", + "$(inherited)", + ); INFOPLIST_FILE = SimpleRoulette/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; @@ -743,6 +1020,7 @@ PRODUCT_BUNDLE_IDENTIFIER = dev.fummicc1.SimpleRoulette; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG SIMPLEROULETTE"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -759,6 +1037,10 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "SIMPLEROULETTE=1", + "$(inherited)", + ); INFOPLIST_FILE = SimpleRoulette/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 13.0; @@ -820,6 +1102,11 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 2X6AD4W323; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "SIMPLEROULETTEDEMO=1", + "$(inherited)", + ); INFOPLIST_FILE = SimpleRouletteDemo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -828,6 +1115,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = dev.fummicc1.demo.SimpleRouletteDemo; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "SIMPLEROULETTEDEMO DEBUG"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -840,6 +1128,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 2X6AD4W323; + GCC_PREPROCESSOR_DEFINITIONS = "SIMPLEROULETTEDEMO=1"; INFOPLIST_FILE = SimpleRouletteDemo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -940,6 +1229,24 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + D33835B52545515300403222 /* Build configuration list for PBXNativeTarget "SimpleRouletteDemoStoryboard" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D33835B62545515300403222 /* Debug */, + D33835B72545515300403222 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D33835DC2545523000403222 /* Build configuration list for PBXNativeTarget "SimpleRouletteDemoSwiftUI" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D33835DD2545523000403222 /* Debug */, + D33835DE2545523000403222 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; D35C37192480A1F70029CDA8 /* Build configuration list for PBXProject "SimpleRoulette" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/SimpleRoulette.xcodeproj/project.xcworkspace/xcuserdata/fumiyatanaka.xcuserdatad/UserInterfaceState.xcuserstate b/SimpleRoulette.xcodeproj/project.xcworkspace/xcuserdata/fumiyatanaka.xcuserdatad/UserInterfaceState.xcuserstate index f3d19ba4..44199654 100644 Binary files a/SimpleRoulette.xcodeproj/project.xcworkspace/xcuserdata/fumiyatanaka.xcuserdatad/UserInterfaceState.xcuserstate and b/SimpleRoulette.xcodeproj/project.xcworkspace/xcuserdata/fumiyatanaka.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/SimpleRoulette.xcodeproj/xcuserdata/fumiyatanaka.xcuserdatad/xcschemes/xcschememanagement.plist b/SimpleRoulette.xcodeproj/xcuserdata/fumiyatanaka.xcuserdatad/xcschemes/xcschememanagement.plist index b4c6ff83..db381dd8 100644 --- a/SimpleRoulette.xcodeproj/xcuserdata/fumiyatanaka.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/SimpleRoulette.xcodeproj/xcuserdata/fumiyatanaka.xcuserdatad/xcschemes/xcschememanagement.plist @@ -14,6 +14,16 @@ orderHint 0 + SimpleRouletteDemoStoryboard.xcscheme_^#shared#^_ + + orderHint + 2 + + SimpleRouletteDemoSwiftUI.xcscheme_^#shared#^_ + + orderHint + 3 + SuppressBuildableAutocreation diff --git a/SimpleRoulette/Sources/Ex/Color+Ex.swift b/SimpleRoulette/Sources/Ex/Color+Ex.swift new file mode 100644 index 00000000..a3b26f86 --- /dev/null +++ b/SimpleRoulette/Sources/Ex/Color+Ex.swift @@ -0,0 +1,17 @@ +// +// Color+Ex.swift +// SimpleRoulette +// +// Created by Fumiya Tanaka on 2020/10/09. +// Copyright © 2020 Fumiya Tanaka. All rights reserved. +// + +import Foundation +import UIKit +import SwiftUI + +extension UIColor { + func color() -> Color { + Color(self) + } +} diff --git a/SimpleRoulette/Sources/RoulettePart.swift b/SimpleRoulette/Sources/RoulettePart.swift index dd3bf325..f77b671d 100644 --- a/SimpleRoulette/Sources/RoulettePart.swift +++ b/SimpleRoulette/Sources/RoulettePart.swift @@ -13,17 +13,14 @@ public protocol RoulettePartType { var id: UUID { get } var name: String { get } var index: Int { get } - /// From [-1/2 pi, 3/2 pi) + /// From [0, 2pi) var startRadianAngle: Double { get } - /// From [-1/2 pi, 3/2 pi) + /// From [0, 2pi) var endRadianAngle: Double { get } var fillColor: UIColor { get } var strokeColor: UIColor { get } } -extension RoulettePartType { -} - public enum Roulette { public struct HugePart { @@ -40,7 +37,7 @@ public enum Roulette { public init( name: String, huge: Kind, - delegate: RoulettePartHugeDelegate, + delegate: RoulettePartHugeDelegate?, index: Int, fillColor: UIColor = .secondarySystemBackground, strokeColor: UIColor = .systemGray4 @@ -97,7 +94,6 @@ extension Roulette.HugePart: RoulettePartType { func getPreviousEndAngle() -> Double { guard let delegate = delegate else { - assert(false, "No delegate.") return .zero } var previousEndAngle: Double = 0 @@ -118,8 +114,7 @@ extension Roulette.HugePart: RoulettePartType { } public var endRadianAngle: Double { - guard let delegate = delegate else { - assert(false, "No delegate.") + guard let delegate = delegate else { return .zero } let ratio = Double(huge.area) / Double(delegate.allHuge.reduce(0, { $0 + $1.area })) diff --git a/SimpleRoulette/Sources/SwiftUI/RoulettePartSwiftUIView.swift b/SimpleRoulette/Sources/SwiftUI/RoulettePartSwiftUIView.swift new file mode 100644 index 00000000..3d0641e9 --- /dev/null +++ b/SimpleRoulette/Sources/SwiftUI/RoulettePartSwiftUIView.swift @@ -0,0 +1,40 @@ +// +// RoulettePartSwiftUIView.swift +// SimpleRoulette +// +// Created by Fumiya Tanaka on 2020/10/09. +// Copyright © 2020 Fumiya Tanaka. All rights reserved. +// + +import Foundation +import SwiftUI + +struct RoulettePartSwiftUIView: View { + + var radius: CGFloat + var center: CGPoint + var part: RoulettePartType + var currentAngle: Angle + + var body: some View { + ZStack { + RouletteShape(radius: radius, center: center, part: part) + .fill(fillContent: part.fillColor.color(), strokeContent: part.strokeColor.color()) + Text(part.name) + .offset( + CGSize(width: { () -> CGFloat in + let mean = (part.startRadianAngle + part.endRadianAngle) / 2 + let x: CGFloat = radius / 1.5 * CGFloat(cos(mean)) + return x + }(), height: { () -> CGFloat in + let mean = (part.startRadianAngle + part.endRadianAngle) / 2 + let y: CGFloat = radius / 1.5 * CGFloat(sin(mean)) + return y + }()) + ) + .lineLimit(nil) + .frame(width: radius / 1.5, height: radius / 1.5) + } + .frame(width: radius * 2, height: radius * 2) + } +} diff --git a/SimpleRoulette/Sources/SwiftUI/RouletteShape.swift b/SimpleRoulette/Sources/SwiftUI/RouletteShape.swift new file mode 100644 index 00000000..fa285c53 --- /dev/null +++ b/SimpleRoulette/Sources/SwiftUI/RouletteShape.swift @@ -0,0 +1,54 @@ +// +// RouletteShape.swift +// SimpleRoulette +// +// Created by Fumiya Tanaka on 2020/10/01. +// Copyright © 2020 Fumiya Tanaka. All rights reserved. +// + +import SwiftUI + +struct RouletteShape: Shape { + var startAngle: Angle { + Angle(radians: part.startRadianAngle) + } + var endAngle: Angle { + Angle(radians: part.endRadianAngle) + } + var radius: CGFloat + var center: CGPoint + var fillColor: Color { + Color(part.fillColor) + } + var strokeColor: Color { + Color(part.strokeColor) + } + + var part: RoulettePartType + + func path(in rect: CGRect) -> Path { + Path { path in + path.move(to: center) + path.addArc(center: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: false, transform: .identity) + } + } +} + +extension Shape { + /// fills and strokes a shape + public func fill( + fillContent: F, + strokeContent: S + ) -> some View { + ZStack { + self.fill(fillContent) + self.stroke(strokeContent) + } + } +} + +struct RouletteShape_Previews: PreviewProvider { + static var previews: some View { + RouletteShape(radius: 96, center: .init(x: 100, y: 100), part: Roulette.HugePart(name: "Test A", huge: .normal, delegate: nil, index: 0, fillColor: .systemRed, strokeColor: .systemBlue)) + } +} diff --git a/SimpleRoulette/Sources/SwiftUI/RouletteViewModel.swift b/SimpleRoulette/Sources/SwiftUI/RouletteViewModel.swift new file mode 100644 index 00000000..f6cfebf9 --- /dev/null +++ b/SimpleRoulette/Sources/SwiftUI/RouletteViewModel.swift @@ -0,0 +1,169 @@ +// +// RouletteViewModel.swift +// SimpleRoulette +// +// Created by Fumiya Tanaka on 2020/09/30. +// Copyright © 2020 Fumiya Tanaka. All rights reserved. +// + +import Foundation +import SwiftUI +import Combine + +public struct RouletteSpeed: ExpressibleByFloatLiteral { + var value: Double + + public init(floatLiteral value: FloatLiteralType) { + self.value = value + } + + public static let normal: Self = .init(floatLiteral: 2000) + public static let slow: Self = .init(floatLiteral: 1000) + public static let fast: Self = .init(floatLiteral: 3000) +} + +enum RouletteState { + case start + case run(angle: Angle, speed: RouletteSpeed) + case pause(angle: Angle, speed: RouletteSpeed) + case stop(location: RoulettePartType, angle: Angle) + + var angle: Angle { + let degrees: Double + if case RouletteState.run(let angle, _) = self { + degrees = angle.degrees + } else if case RouletteState.pause(let angle, _) = self { + degrees = angle.degrees + } else if case RouletteState.stop(_, let angle) = self { + degrees = angle.degrees + } else { + degrees = 0 + } + return Angle(degrees: degrees) + } + + var speed: RouletteSpeed { + if case RouletteState.run(_, let speed) = self { + return speed + } else if case RouletteState.run(_, let speed) = self { + return speed + } + return .normal + } + + var canStart: Bool { + switch self { + case .start: + return true + + case .run, .pause, .stop: + return false + } + } + + var isAnimating: Bool { + switch self { + case .start, .pause, .stop: + return false + case .run: + return true + } + } +} + +public final class RouletteViewModel: ObservableObject { + @Published private(set) var parts: [RoulettePartType] = [] + @Published private(set) var state: RouletteState = .start + @Published private(set) var duration: Double + + private var onDecide: PassthroughSubject + public var onDecidePublisher: AnyPublisher { + onDecide.eraseToAnyPublisher() + } + + public init(duration: Double, onDecide: PassthroughSubject = .init()) { + self.onDecide = onDecide + self.duration = duration + + } + + public func start(speed: RouletteSpeed = .normal, automaticallyStop: Bool = true) { + if state.canStart { + var angle = Angle() + angle.degrees = speed.value + self.state = RouletteState.run(angle: angle, speed: speed) + self.objectWillChange.send() + if automaticallyStop { + DispatchQueue.main.asyncAfter(deadline: .now() + duration) { + self.stop() + } + } + } + } + + public func stop() { + if !state.isAnimating { + return + } + guard case let RouletteState.run(angle, _) = state else { + return + } + var degrees = CGFloat(angle.degrees) + #if SIMPLEROULETTE || SIMPLEROULETTEDEMO + print("Pure Angle degreees: \(degrees)") + #endif + + while degrees > 360 { + degrees -= 360 + } + + degrees = 360 - degrees + + + #if SIMPLEROULETTE || SIMPLEROULETTEDEMO + print("Processed Angle degreees: \(degrees)") + #endif + + for part in parts { + let start = (part.startRadianAngle + Double.pi / 2).degree() + let end = (part.endRadianAngle + Double.pi / 2).degree() + #if SIMPLEROULETTE || SIMPLEROULETTEDEMO + print("name: \(part.name)") + print("start: \(start)") + print("end: \(end)") + #endif + + if checkIfContainsPoint(from: CGFloat(start), to: CGFloat(end), point: degrees) { + state = .stop(location: part, angle: angle) + onDecide.send(part) + objectWillChange.send() + break + } + } + + } + + func update(to state: inout State, keypath: WritableKeyPath, _ value: V) { + if state[keyPath: keypath] != value { + state[keyPath: keypath] = value + } + } + + private func checkIfContainsPoint(from source: CGFloat, to destination: CGFloat, point: CGFloat) -> Bool { + return source <= point && destination > point + } + + public func configureParts(_ parts: [RoulettePartType]) { + self.parts = parts + } +} + +extension RouletteViewModel: RoulettePartHugeDelegate { + public var total: Double { + Double.pi * 2 + } + + public var allHuge: [Roulette.HugePart.Kind] { + parts.compactMap { $0 as? Roulette.HugePart }.map { $0.huge } + } +} diff --git a/SimpleRoulette/Sources/SwiftUI/RouletteViewSwiftUI.swift b/SimpleRoulette/Sources/SwiftUI/RouletteViewSwiftUI.swift new file mode 100644 index 00000000..da213908 --- /dev/null +++ b/SimpleRoulette/Sources/SwiftUI/RouletteViewSwiftUI.swift @@ -0,0 +1,101 @@ +// +// RouletteViewSwiftUI.swift +// SimpleRoulette +// +// Created by Fumiya Tanaka on 2020/09/30. +// Copyright © 2020 Fumiya Tanaka. All rights reserved. +// + +import SwiftUI +import UIKit + +public struct RouletteViewSwiftUI: View { + + @ObservedObject var viewModel: RouletteViewModel + + @State private var radius: CGFloat = 0 + @State private var center: CGPoint = .zero + @State private var currentAngle: Angle = .init() + + let pointSize: CGSize = CGSize(width: 32, height: 32) + let pointView: AnyView + + public var body: some View { + VStack { + Spacer() + pointView.frame(width: pointSize.width, height: pointSize.height) + GeometryReader { geometry in + content + .aspectRatio(1, contentMode: .fit) + .rotationEffect(currentAngle) + .onAppear(perform: { + let midX = geometry.frame(in: .local).midX + let midY = geometry.frame(in: .local).midY + let centerValue = min(midX, midY) + center = CGPoint(x: centerValue, y: centerValue) + radius = centerValue + }) + .onReceive(viewModel.$state, perform: { state in + withAnimation(.easeOut(duration: viewModel.duration)) { + self.currentAngle = state.angle + } + }) + } + Spacer() + } + } + + private var annotations: some View { + ForEach(viewModel.parts.indices) { (index: Int) -> AnyView in + let part = viewModel.parts[index] + return AnyView( + Text(part.name) + .offset(CGSize(width: { _ -> CGFloat in + let mean = (part.startRadianAngle + part.endRadianAngle) / 2 + let x: CGFloat = radius / 2 * CGFloat(cos(mean)) + return x + }(()), height: { _ -> CGFloat in + let mean = (part.startRadianAngle + part.endRadianAngle) / 2 + let y: CGFloat = radius / 2 * CGFloat(sin(mean)) + return y + }(()))) + .multilineTextAlignment(.center) + .frame(maxWidth: radius * 0.8, maxHeight: radius * 0.8) + ) + } + } + + private var content: some View { + ForEach(viewModel.parts.indices) { (index: Int) -> RoulettePartSwiftUIView in + let part = viewModel.parts[index] + return RoulettePartSwiftUIView( + radius: radius, + center: center, + part: part, + currentAngle: viewModel.state.angle + ) + } + } + + public init(viewModel: RouletteViewModel, pointView: AnyView? = nil) { + self.viewModel = viewModel + if let pointView = pointView { + self.pointView = pointView + } else { + self.pointView = AnyView(Image(systemName: "arrowtriangle.down").font(.system(size: 32))) + } + } +} + +struct RouletteViewSwiftUI_Previews: PreviewProvider { + static var previews: some View { + let viewModel = RouletteViewModel(duration: 5) + viewModel.configureParts([ + Roulette.HugePart(name: "Test A", huge: .normal, delegate: viewModel, index: 0), + Roulette.HugePart(name: "Test B", huge: .normal, delegate: viewModel, index: 1), + Roulette.HugePart(name: "Test C", huge: .normal, delegate: viewModel, index: 2), + Roulette.HugePart(name: "Test D", huge: .normal, delegate: viewModel, index: 3), + ]) + return RouletteViewSwiftUI(viewModel: viewModel) + } +} diff --git a/SimpleRouletteDemo/IB/IBRouletteViewController.storyboard b/SimpleRouletteDemo/IB/IBRouletteViewController.storyboard deleted file mode 100644 index c9846883..00000000 --- a/SimpleRouletteDemo/IB/IBRouletteViewController.storyboard +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SimpleRouletteDemo/SceneDelegate.swift b/SimpleRouletteDemo/SceneDelegate.swift index 5339d73d..456a69e1 100644 --- a/SimpleRouletteDemo/SceneDelegate.swift +++ b/SimpleRouletteDemo/SceneDelegate.swift @@ -7,6 +7,8 @@ // import UIKit +import SwiftUI +import SimpleRoulette class SceneDelegate: UIResponder, UIWindowSceneDelegate { @@ -17,12 +19,12 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). -// guard let windowScene = scene as? UIWindowScene else { return } -// let window = UIWindow(windowScene: windowScene) -// window.rootViewController = ViewController(nibName: nil, bundle: nil) -// window.rootViewController?.view.frame = UIScreen.main.bounds -// window.makeKeyAndVisible() -// self.window = window + guard let windowScene = scene as? UIWindowScene else { return } + let window = UIWindow(windowScene: windowScene) + window.rootViewController = ViewController(nibName: nil, bundle: nil) + window.rootViewController?.view.frame = UIScreen.main.bounds + window.makeKeyAndVisible() + self.window = window } func sceneDidDisconnect(_ scene: UIScene) { diff --git a/SimpleRouletteDemoStoryboard/AppDelegate.swift b/SimpleRouletteDemoStoryboard/AppDelegate.swift new file mode 100644 index 00000000..42e15789 --- /dev/null +++ b/SimpleRouletteDemoStoryboard/AppDelegate.swift @@ -0,0 +1,37 @@ +// +// AppDelegate.swift +// SimpleRouletteDemoStoryboard +// +// Created by Fumiya Tanaka on 2020/10/25. +// Copyright © 2020 Fumiya Tanaka. All rights reserved. +// + +import UIKit + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/SimpleRouletteDemoStoryboard/Assets.xcassets/AccentColor.colorset/Contents.json b/SimpleRouletteDemoStoryboard/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/SimpleRouletteDemoStoryboard/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SimpleRouletteDemoStoryboard/Assets.xcassets/AppIcon.appiconset/Contents.json b/SimpleRouletteDemoStoryboard/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..9221b9bb --- /dev/null +++ b/SimpleRouletteDemoStoryboard/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "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" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SimpleRouletteDemoStoryboard/Assets.xcassets/Contents.json b/SimpleRouletteDemoStoryboard/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/SimpleRouletteDemoStoryboard/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SimpleRouletteDemoStoryboard/Base.lproj/LaunchScreen.storyboard b/SimpleRouletteDemoStoryboard/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..865e9329 --- /dev/null +++ b/SimpleRouletteDemoStoryboard/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SimpleRouletteDemoStoryboard/Base.lproj/Main.storyboard b/SimpleRouletteDemoStoryboard/Base.lproj/Main.storyboard new file mode 100644 index 00000000..153ba5ad --- /dev/null +++ b/SimpleRouletteDemoStoryboard/Base.lproj/Main.storyboard @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SimpleRouletteDemoStoryboard/Info.plist b/SimpleRouletteDemoStoryboard/Info.plist new file mode 100644 index 00000000..5b531f7b --- /dev/null +++ b/SimpleRouletteDemoStoryboard/Info.plist @@ -0,0 +1,66 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/SimpleRouletteDemoStoryboard/SceneDelegate.swift b/SimpleRouletteDemoStoryboard/SceneDelegate.swift new file mode 100644 index 00000000..cbbc410a --- /dev/null +++ b/SimpleRouletteDemoStoryboard/SceneDelegate.swift @@ -0,0 +1,53 @@ +// +// SceneDelegate.swift +// SimpleRouletteDemoStoryboard +// +// Created by Fumiya Tanaka on 2020/10/25. +// Copyright © 2020 Fumiya Tanaka. All rights reserved. +// + +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + guard let _ = (scene as? UIWindowScene) else { return } + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/SimpleRouletteDemo/IB/IBRouletteViewController.swift b/SimpleRouletteDemoStoryboard/ViewController.swift similarity index 90% rename from SimpleRouletteDemo/IB/IBRouletteViewController.swift rename to SimpleRouletteDemoStoryboard/ViewController.swift index 3822c050..75dc1726 100644 --- a/SimpleRouletteDemo/IB/IBRouletteViewController.swift +++ b/SimpleRouletteDemoStoryboard/ViewController.swift @@ -7,12 +7,14 @@ // import UIKit +import SwiftUI import SimpleRoulette -class IBRouletteViewController: UIViewController { +class ViewController: UIViewController { @IBOutlet var rouletteView: RouletteView! @IBOutlet var secondRouletteView: RouletteView! + @IBOutlet var stackView: UIStackView! override func viewDidLoad() { super.viewDidLoad() @@ -33,10 +35,12 @@ class IBRouletteViewController: UIViewController { Roulette.HugePart(name: "Title J", huge: .normal, delegate: secondRouletteView, index: 5), ]) -// rouletteView.start() + rouletteView.start() secondRouletteView.start(duration: 10) DispatchQueue.main.asyncAfter(deadline: .now() + 3) { -// self.rouletteView.stop() + self.rouletteView.stop() + } + DispatchQueue.main.asyncAfter(deadline: .now() + 10) { self.secondRouletteView.stop() } } @@ -51,7 +55,7 @@ class IBRouletteViewController: UIViewController { } } -extension IBRouletteViewController: RouletteViewDelegate { +extension ViewController: RouletteViewDelegate { func rouletteView(_ rouletteView: RouletteView, didStopAt part: RoulettePartType) { let alert = UIAlertController(title: "結果", message: part.name, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "閉じる", style: .default, handler: nil)) diff --git a/SimpleRouletteDemoSwiftUI/Assets.xcassets/AccentColor.colorset/Contents.json b/SimpleRouletteDemoSwiftUI/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/SimpleRouletteDemoSwiftUI/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SimpleRouletteDemoSwiftUI/Assets.xcassets/AppIcon.appiconset/Contents.json b/SimpleRouletteDemoSwiftUI/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..9221b9bb --- /dev/null +++ b/SimpleRouletteDemoSwiftUI/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "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" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SimpleRouletteDemoSwiftUI/Assets.xcassets/Contents.json b/SimpleRouletteDemoSwiftUI/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/SimpleRouletteDemoSwiftUI/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SimpleRouletteDemoSwiftUI/ContentView.swift b/SimpleRouletteDemoSwiftUI/ContentView.swift new file mode 100644 index 00000000..04242268 --- /dev/null +++ b/SimpleRouletteDemoSwiftUI/ContentView.swift @@ -0,0 +1,46 @@ +// +// ContentView.swift +// SimpleRouletteDemoSwiftUI +// +// Created by Fumiya Tanaka on 2020/10/25. +// Copyright © 2020 Fumiya Tanaka. All rights reserved. +// + +import SwiftUI +import SimpleRoulette + +struct ContentView: View { + @ObservedObject var viewModel: RouletteViewModel = { + let viewModel = RouletteViewModel(duration: 5) + viewModel.configureParts([ + Roulette.HugePart(name: "Title A", huge: .large, delegate: viewModel, index: 0), + Roulette.HugePart(name: "Title B", huge: .small, delegate: viewModel, index: 1), + Roulette.HugePart(name: "Title C", huge: .normal, delegate: viewModel, index: 2), + ]) + return viewModel + }() + @State private var decidedPart: RoulettePartType? + + var body: some View { + VStack { + Spacer() + RouletteViewSwiftUI(viewModel: viewModel) + Button("Start") { + viewModel.start(speed: .slow) + } + if let part = decidedPart { + Text(part.name) + } + Spacer() + } + .onReceive(viewModel.onDecidePublisher) { part in + decidedPart = part + } + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} diff --git a/SimpleRouletteDemoSwiftUI/Info.plist b/SimpleRouletteDemoSwiftUI/Info.plist new file mode 100644 index 00000000..efc211a0 --- /dev/null +++ b/SimpleRouletteDemoSwiftUI/Info.plist @@ -0,0 +1,50 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + UIApplicationSupportsIndirectInputEvents + + UILaunchScreen + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/SimpleRouletteDemoSwiftUI/Preview Content/Preview Assets.xcassets/Contents.json b/SimpleRouletteDemoSwiftUI/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/SimpleRouletteDemoSwiftUI/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/SimpleRouletteDemoSwiftUI/SimpleRouletteDemoSwiftUIApp.swift b/SimpleRouletteDemoSwiftUI/SimpleRouletteDemoSwiftUIApp.swift new file mode 100644 index 00000000..68074511 --- /dev/null +++ b/SimpleRouletteDemoSwiftUI/SimpleRouletteDemoSwiftUIApp.swift @@ -0,0 +1,18 @@ +// +// SimpleRouletteDemoSwiftUIApp.swift +// SimpleRouletteDemoSwiftUI +// +// Created by Fumiya Tanaka on 2020/10/25. +// Copyright © 2020 Fumiya Tanaka. All rights reserved. +// + +import SwiftUI + +@main +struct SimpleRouletteDemoSwiftUIApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +}