diff --git a/.DS_Store b/.DS_Store index 8545bbf..a2570cb 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Example/Athlee-Onboarding/OnboardingKit Swift3.xcodeproj/project.pbxproj b/Example/Athlee-Onboarding/OnboardingKit Swift3.xcodeproj/project.pbxproj index 6f92f14..ba1bba5 100644 --- a/Example/Athlee-Onboarding/OnboardingKit Swift3.xcodeproj/project.pbxproj +++ b/Example/Athlee-Onboarding/OnboardingKit Swift3.xcodeproj/project.pbxproj @@ -29,6 +29,7 @@ 048680EA1D8E2BA9004B011E /* TZStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048680DC1D8E2BA9004B011E /* TZStackView.swift */; }; 048680EB1D8E2BA9004B011E /* TZStackViewAlignment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048680DD1D8E2BA9004B011E /* TZStackViewAlignment.swift */; }; 048680EC1D8E2BA9004B011E /* TZStackViewDistribution.swift in Sources */ = {isa = PBXBuildFile; fileRef = 048680DE1D8E2BA9004B011E /* TZStackViewDistribution.swift */; }; + 45DD8E41268B452200C85E3D /* UILayoutPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45DD8E40268B452200C85E3D /* UILayoutPriority.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -68,6 +69,7 @@ 048680DC1D8E2BA9004B011E /* TZStackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TZStackView.swift; sourceTree = ""; }; 048680DD1D8E2BA9004B011E /* TZStackViewAlignment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TZStackViewAlignment.swift; sourceTree = ""; }; 048680DE1D8E2BA9004B011E /* TZStackViewDistribution.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TZStackViewDistribution.swift; sourceTree = ""; }; + 45DD8E40268B452200C85E3D /* UILayoutPriority.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UILayoutPriority.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -185,6 +187,7 @@ 048680D81D8E2BA9004B011E /* PageItemView.swift */, 048680D91D8E2BA9004B011E /* PageView.swift */, 048680DA1D8E2BA9004B011E /* TZStackView */, + 45DD8E40268B452200C85E3D /* UILayoutPriority.swift */, ); name = Source; path = ../../Source; @@ -269,6 +272,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); @@ -315,6 +319,7 @@ 048680E11D8E2BA9004B011E /* CompletionObject.swift in Sources */, 048680E31D8E2BA9004B011E /* OnboardingView.swift in Sources */, 048680E21D8E2BA9004B011E /* OnboardingConfiguration.swift in Sources */, + 45DD8E41268B452200C85E3D /* UILayoutPriority.swift in Sources */, 048680E61D8E2BA9004B011E /* PageControlView.swift in Sources */, 048680DF1D8E2BA9004B011E /* Anchor.swift in Sources */, 048680671D8E204C004B011E /* AppDelegate.swift in Sources */, @@ -462,7 +467,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.athlee.OnboardingKit-Swift3"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -476,7 +481,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.athlee.OnboardingKit-Swift3"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; }; name = Release; }; diff --git a/Example/Athlee-Onboarding/OnboardingKit Swift3.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Example/Athlee-Onboarding/OnboardingKit Swift3.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Example/Athlee-Onboarding/OnboardingKit Swift3.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/OnboardingKit.podspec b/OnboardingKit.podspec deleted file mode 100644 index 929a7ca..0000000 --- a/OnboardingKit.podspec +++ /dev/null @@ -1,27 +0,0 @@ -# -# Be sure to run `pod spec lint OnboardingKit.podspec' to ensure this is a -# valid spec and to remove all comments including this before submitting the spec. -# -# To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html -# To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ -# - -Pod::Spec.new do |s| - - s.name = "OnboardingKit" - s.version = "0.0.5" - s.summary = "A simple and interactive framework for making iOS onboarding experience easy and fun!" - s.homepage = "https://github.com/Athlee/OnboardingKit" - s.license = { :type => "MIT", :file => "LICENSE" } - s.author = { "Eugene Mozharovsky" => "mozharovsky@live.com" } - s.social_media_url = "http://twitter.com/dottieyottie" - s.platform = :ios, "10.0" - s.ios.deployment_target = "8.0" - s.source = { :git => "https://github.com/Athlee/OnboardingKit.git", :tag => s.version } - s.source_files = "Source/**/*.swift" - s.requires_arc = true - - # Waiting for Swift 3 support - # s.dependency 'TZStackView' - -end diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..7de62ac --- /dev/null +++ b/Package.swift @@ -0,0 +1,31 @@ +// swift-tools-version:5.3 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "OnboardingKit", + platforms: [ + .iOS(.v13) + ], + products: [ + // Products define the executables and libraries a package produces, and make them visible to other packages. + .library( + name: "OnboardingKit", + targets: ["OnboardingKit"]), + ], + 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: "OnboardingKit", + dependencies: []), + .testTarget( + name: "OnboardingKitTests", + dependencies: ["OnboardingKit"]), + ] +) diff --git a/README.md b/README.md index 08f1726..8d989a1 100644 --- a/README.md +++ b/README.md @@ -1,101 +1,3 @@ # OnboardingKit -

- -

- -`OnboardingKit` is a simple and interactive framework for making iOS onboarding experience easy and fun! - -### Requirements -- Swift 3 & Xcode 8.x.x (check out v0.0.3 & [swift2](https://github.com/Athlee/OnboardingKit/tree/swift2) branch for previous version) -- iOS 8+ - -### Features -- [x] Customizable page views -- [x] Customizable background images -- [x] Customizable containers' background images -- [x] Animatable page control -- [x] Animatable transitions between pages on swipes - -# Installation -### CocoaPods - -`OnboardingKit` is available for installation using the [CocoaPods](https://cocoapods.org). - -Add the following code to your `Podfile`: -```ruby - pod 'OnboardingKit' -``` - -# Usage -Import the module. - -```swift - import OnboardingKit -``` - -Add a `UIView` instance that inherits from `OnboardingView`. Traditionally, you do this through Storyboard or manually. - -Implement `OnboardingViewDataSource` and `OnboardingViewDelegate` protocols with required methods. What you have to do is to let `OnboardingView` know how many pages it should build and provide configurations for these pages. - -```swift - extension DataModel: OnboardingViewDataSource, OnboardingViewDelegate { - func numberOfPages() -> Int { - return 1 - } - - func onboardingView(_ onboardingView: OnboardingView, configurationForPage page: Int) -> OnboardingConfiguration { - return OnboardingConfiguration( - image: UIImage(named: "DemoImage")!, - itemImage: UIImage(named: "DemoIcon")!, - pageTitle: "Demo Title", - pageDescription: "Demo Description Text!", - backgroundImage: UIImage(named: "DemoBackground"), - topBackgroundImage: nil, // your image here - bottomBackgroundImage: nil // your image here - ) - } -``` - -`OnboardingConfiguration` is implemented this way: - -```swift - public struct OnboardingConfiguration { - let image: UIImage - let itemImage: UIImage - let pageTitle: String - let pageDescription: String - - let backgroundImage: UIImage? - let topBackgroundImage: UIImage? - let bottomBackgroundImage: UIImage? - } -``` - -If you need a custom configuration for a `PageView` this is possible with a delegate's method `onboardingView(_:, configurePageView _:, atPage _:)`. - -```swift - func onboardingView(_ onboardingView: OnboardingView, configurePageView pageView: PageView, atPage page: Int) { - pageView.titleLabel.textColor = UIColor.white - pageView.titleLabel.layer.shadowOpacity = 0.6 - pageView.titleLabel.layer.shadowColor = UIColor.black.cgColor - pageView.titleLabel.layer.shadowOffset = CGSize(width: 0, height: 1) - pageView.titleLabel.layer.shouldRasterize = true - pageView.titleLabel.layer.rasterizationScale = UIScreen.main.scale - - if DeviceTarget.IS_IPHONE_4 { - pageView.titleLabel.font = UIFont.boldSystemFont(ofSize: 30) - pageView.descriptionLabel.font = UIFont.systemFont(ofSize: 15) - } - } -``` - -That's it. :] - -# Community -* For help & feedback please use [issues](https://github.com/Athlee/OnboardingKit/issues). -* Got a new feature? Please submit a [pull request](https://github.com/Athlee/OnboardingKit/pulls). -* Email us with urgent queries. - -# License -`OnboardingKit` is available under the MIT license, see the [LICENSE](https://github.com/Athlee/OnboardingKit/blob/master/LICENSE) file for more information. +A description of this package. diff --git a/Source/.DS_Store b/Source/.DS_Store deleted file mode 100644 index 393f9c5..0000000 Binary files a/Source/.DS_Store and /dev/null differ diff --git a/Source/TZStackView/.DS_Store b/Source/TZStackView/.DS_Store deleted file mode 100644 index 3b49f08..0000000 Binary files a/Source/TZStackView/.DS_Store and /dev/null differ diff --git a/Source/Anchor.swift b/Sources/OnboardingKit/Anchor.swift similarity index 98% rename from Source/Anchor.swift rename to Sources/OnboardingKit/Anchor.swift index 08a27c1..8f0211e 100644 --- a/Source/Anchor.swift +++ b/Sources/OnboardingKit/Anchor.swift @@ -74,9 +74,9 @@ public struct Anchors { open class LayoutAnchor { internal var item:View - internal var attribute:NSLayoutAttribute + internal var attribute:NSLayoutConstraint.Attribute - internal init(item:View, attribute:NSLayoutAttribute) { + internal init(item:View, attribute:NSLayoutConstraint.Attribute) { self.item = item self.attribute = attribute } diff --git a/Source/CGRect.swift b/Sources/OnboardingKit/CGRect.swift similarity index 100% rename from Source/CGRect.swift rename to Sources/OnboardingKit/CGRect.swift diff --git a/Source/CompletionObject.swift b/Sources/OnboardingKit/CompletionObject.swift similarity index 90% rename from Source/CompletionObject.swift rename to Sources/OnboardingKit/CompletionObject.swift index f45fd96..23cdf8d 100644 --- a/Source/CompletionObject.swift +++ b/Sources/OnboardingKit/CompletionObject.swift @@ -13,7 +13,7 @@ internal protocol Completion { } internal final class CompletionObject: Completion { - internal var completion: ((Void) -> Void)? + internal var completion: (() -> ())? internal static let sharedInstance = CompletionObject() diff --git a/Source/OnboardingConfiguration.swift b/Sources/OnboardingKit/OnboardingConfiguration.swift similarity index 100% rename from Source/OnboardingConfiguration.swift rename to Sources/OnboardingKit/OnboardingConfiguration.swift diff --git a/Sources/OnboardingKit/OnboardingKit.swift b/Sources/OnboardingKit/OnboardingKit.swift new file mode 100644 index 0000000..dec9445 --- /dev/null +++ b/Sources/OnboardingKit/OnboardingKit.swift @@ -0,0 +1,3 @@ +struct OnboardingKit { + var text = "Hello, World!" +} diff --git a/Source/OnboardingView.swift b/Sources/OnboardingKit/OnboardingView.swift similarity index 95% rename from Source/OnboardingView.swift rename to Sources/OnboardingKit/OnboardingView.swift index b9bfc76..dd34f5f 100644 --- a/Source/OnboardingView.swift +++ b/Sources/OnboardingKit/OnboardingView.swift @@ -166,9 +166,9 @@ public final class OnboardingView: UIView, CAAnimationDelegate { addGestureRecognizer(rightSwipeRecognizer) } - internal func didRecognizeSwipe(_ recognizer: UISwipeGestureRecognizer) { + @objc internal func didRecognizeSwipe(_ recognizer: UISwipeGestureRecognizer) { switch recognizer.direction { - case UISwipeGestureRecognizerDirection.left: + case UISwipeGestureRecognizer.Direction.left: guard pageControlView.currentPage + 1 < pageControlView.pages else { return } @@ -192,7 +192,7 @@ public final class OnboardingView: UIView, CAAnimationDelegate { animateSubviews(current) animatePageView(previous, forState: .fadeIn) - case UISwipeGestureRecognizerDirection.right: + case UISwipeGestureRecognizer.Direction.right: guard pageControlView.currentPage - 1 >= 0 else { return } @@ -279,7 +279,7 @@ public final class OnboardingView: UIView, CAAnimationDelegate { arcCenter: center, radius: state == .expanded ? frame.height * 2 : 0.1, startAngle: 0, - endAngle: CGFloat(M_PI) * 2, + endAngle: CGFloat(Double.pi) * 2, clockwise: false ) } @@ -290,15 +290,15 @@ public final class OnboardingView: UIView, CAAnimationDelegate { pageView.layer.mask = shape } - func animatePageView(_ pageView: PageView, forState state: State, completion: ((Void) -> Void)? = nil) { + func animatePageView(_ pageView: PageView, forState state: State, completion: (() -> ())? = nil) { if state == .expanded || state == .folded { if let shapeLayer = pageView.layer.mask as? CAShapeLayer { let animation = CABasicAnimation(keyPath: "path") animation.toValue = pathForState(state).cgPath animation.duration = 0.7 animation.isRemovedOnCompletion = false - animation.fillMode = kCAFillModeBoth - animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn) + animation.fillMode = CAMediaTimingFillMode.both + animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn) if let completion = completion { animation.delegate = self diff --git a/Source/OnboardingViewDataSource.swift b/Sources/OnboardingKit/OnboardingViewDataSource.swift similarity index 100% rename from Source/OnboardingViewDataSource.swift rename to Sources/OnboardingKit/OnboardingViewDataSource.swift diff --git a/Source/OnboardingViewDelegate.swift b/Sources/OnboardingKit/OnboardingViewDelegate.swift similarity index 100% rename from Source/OnboardingViewDelegate.swift rename to Sources/OnboardingKit/OnboardingViewDelegate.swift diff --git a/Source/PageControlView.swift b/Sources/OnboardingKit/PageControlView.swift similarity index 100% rename from Source/PageControlView.swift rename to Sources/OnboardingKit/PageControlView.swift diff --git a/Source/PageItemView.swift b/Sources/OnboardingKit/PageItemView.swift similarity index 98% rename from Source/PageItemView.swift rename to Sources/OnboardingKit/PageItemView.swift index 2a7e6e1..8924098 100644 --- a/Source/PageItemView.swift +++ b/Sources/OnboardingKit/PageItemView.swift @@ -70,7 +70,7 @@ public final class PageItemView: UIView { UIView.animate( withDuration: 0.5, delay: 0, - options: UIViewAnimationOptions(), + options: UIView.AnimationOptions(), animations: animations, completion: nil ) @@ -97,7 +97,7 @@ public final class PageItemView: UIView { UIView.animate( withDuration: 0.5, delay: 0, - options: UIViewAnimationOptions(), + options: UIView.AnimationOptions(), animations: animations, completion: nil ) diff --git a/Source/PageView.swift b/Sources/OnboardingKit/PageView.swift similarity index 100% rename from Source/PageView.swift rename to Sources/OnboardingKit/PageView.swift diff --git a/Source/TZStackView/TZSpacerView.swift b/Sources/OnboardingKit/TZStackView/TZSpacerView.swift similarity index 100% rename from Source/TZStackView/TZSpacerView.swift rename to Sources/OnboardingKit/TZStackView/TZSpacerView.swift diff --git a/Source/TZStackView/TZStackView.swift b/Sources/OnboardingKit/TZStackView/TZStackView.swift similarity index 96% rename from Source/TZStackView/TZStackView.swift rename to Sources/OnboardingKit/TZStackView/TZStackView.swift index cca567c..c62a736 100755 --- a/Source/TZStackView/TZStackView.swift +++ b/Sources/OnboardingKit/TZStackView/TZStackView.swift @@ -25,7 +25,7 @@ open class TZStackView: UIView { } } - open var axis: UILayoutConstraintAxis = .horizontal { + open var axis: NSLayoutConstraint.Axis = .horizontal { didSet { setNeedsUpdateConstraints() } @@ -131,7 +131,7 @@ open class TZStackView: UIView { addHiddenListener(arrangedSubview) } - func hiddenAnimationStopped() { + @objc func hiddenAnimationStopped() { var queueEntriesToRemove = [TZAnimationDidStopQueueEntry]() for entry in animationDidStopQueueEntries { let view = entry.view @@ -348,10 +348,10 @@ open class TZStackView: UIView { } var topPriority: Float = 1000 - var topRelation: NSLayoutRelation = .lessThanOrEqual + var topRelation: NSLayoutConstraint.Relation = .lessThanOrEqual var bottomPriority: Float = 1000 - var bottomRelation: NSLayoutRelation = .greaterThanOrEqual + var bottomRelation: NSLayoutConstraint.Relation = .greaterThanOrEqual if alignment == .top || alignment == .leading { topPriority = 999.5 @@ -437,7 +437,7 @@ open class TZStackView: UIView { } // Chains together the given views using Leading/Trailing or Top/Bottom - fileprivate func createFillConstraints(_ views: [UIView], priority: Float = 1000, relatedBy relation: NSLayoutRelation = .equal, constant: CGFloat) -> [NSLayoutConstraint] { + fileprivate func createFillConstraints(_ views: [UIView], priority: Float = 1000, relatedBy relation: NSLayoutConstraint.Relation = .equal, constant: CGFloat) -> [NSLayoutConstraint] { var constraints = [NSLayoutConstraint]() var previousView: UIView? @@ -567,7 +567,7 @@ open class TZStackView: UIView { return constraints } - fileprivate func equalAttributes(_ views: [UIView], attribute: NSLayoutAttribute, priority: Float = 1000) -> [NSLayoutConstraint] { + fileprivate func equalAttributes(_ views: [UIView], attribute: NSLayoutConstraint.Attribute, priority: Float = 1000) -> [NSLayoutConstraint] { var currentPriority = priority var constraints = [NSLayoutConstraint]() if views.count > 0 { @@ -590,12 +590,12 @@ open class TZStackView: UIView { } // Convenience method to help make NSLayoutConstraint in a less verbose way - fileprivate func constraint(item view1: AnyObject, attribute attr1: NSLayoutAttribute, relatedBy relation: NSLayoutRelation = .equal, toItem view2: AnyObject?, attribute attr2: NSLayoutAttribute? = nil, multiplier: CGFloat = 1, constant c: CGFloat = 0, priority: Float = 1000) -> NSLayoutConstraint { + fileprivate func constraint(item view1: AnyObject, attribute attr1: NSLayoutConstraint.Attribute, relatedBy relation: NSLayoutConstraint.Relation = .equal, toItem view2: AnyObject?, attribute attr2: NSLayoutConstraint.Attribute? = nil, multiplier: CGFloat = 1, constant c: CGFloat = 0, priority: Float = 1000) -> NSLayoutConstraint { let attribute2 = attr2 != nil ? attr2! : attr1 let constraint = NSLayoutConstraint(item: view1, attribute: attr1, relatedBy: relation, toItem: view2, attribute: attribute2, multiplier: multiplier, constant: c) - constraint.priority = priority + constraint.priority = UILayoutPriority(priority) return constraint } diff --git a/Source/TZStackView/TZStackViewAlignment.swift b/Sources/OnboardingKit/TZStackView/TZStackViewAlignment.swift similarity index 100% rename from Source/TZStackView/TZStackViewAlignment.swift rename to Sources/OnboardingKit/TZStackView/TZStackViewAlignment.swift diff --git a/Source/TZStackView/TZStackViewDistribution.swift b/Sources/OnboardingKit/TZStackView/TZStackViewDistribution.swift similarity index 100% rename from Source/TZStackView/TZStackViewDistribution.swift rename to Sources/OnboardingKit/TZStackView/TZStackViewDistribution.swift diff --git a/Sources/OnboardingKit/UILayoutPriority.swift b/Sources/OnboardingKit/UILayoutPriority.swift new file mode 100644 index 0000000..17a7098 --- /dev/null +++ b/Sources/OnboardingKit/UILayoutPriority.swift @@ -0,0 +1,25 @@ +// +// UILayoutPriority.swift +// OnboardingKit Swift3 +// +// Created by Carlos barros on 29/06/2021. +// Copyright © 2021 Athlee. All rights reserved. +// +import UIKit +import Foundation + +extension UILayoutPriority { + static func +(lhs: UILayoutPriority, rhs: Float) -> UILayoutPriority { + let raw = lhs.rawValue + rhs + return UILayoutPriority(rawValue:raw) + } +} + +// MARK: - Allow to pass an integer value to the functions depending on UILayoutPriority +extension UILayoutPriority: ExpressibleByIntegerLiteral { + public typealias IntegerLiteralType = Int + + public init(integerLiteral value: Int) { + self.init(Float(value)) + } +} diff --git a/Tests/OnboardingKitTests/OnboardingKitTests.swift b/Tests/OnboardingKitTests/OnboardingKitTests.swift new file mode 100644 index 0000000..b9cc063 --- /dev/null +++ b/Tests/OnboardingKitTests/OnboardingKitTests.swift @@ -0,0 +1,11 @@ + import XCTest + @testable import OnboardingKit + + final class OnboardingKitTests: XCTestCase { + func testExample() { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct + // results. + XCTAssertEqual(OnboardingKit().text, "Hello, World!") + } + }