├── Assets
├── about.png
├── socialPreview.png
└── ALRadioButtons.sketch
├── ExampleApp
├── iOS App
│ ├── Sources
│ │ ├── Assets.xcassets
│ │ │ ├── Contents.json
│ │ │ └── AppIcon.appiconset
│ │ │ │ ├── ipad-20x20.png
│ │ │ │ ├── ipad-29x29.png
│ │ │ │ ├── ipad-40x40.png
│ │ │ │ ├── ipad-76x76.png
│ │ │ │ ├── ipad-20x20@2x.png
│ │ │ │ ├── ipad-29x29@2x.png
│ │ │ │ ├── ipad-40x40@2x.png
│ │ │ │ ├── ipad-76x76@2x.png
│ │ │ │ ├── iphone-20x20@2x.png
│ │ │ │ ├── iphone-20x20@3x.png
│ │ │ │ ├── iphone-29x29@2x.png
│ │ │ │ ├── iphone-29x29@3x.png
│ │ │ │ ├── iphone-40x40@2x.png
│ │ │ │ ├── iphone-40x40@3x.png
│ │ │ │ ├── iphone-60x60@2x.png
│ │ │ │ ├── iphone-60x60@3x.png
│ │ │ │ ├── ipad-83.5x83.5@2x.png
│ │ │ │ ├── ios-marketing-1024x1024.png
│ │ │ │ └── Contents.json
│ │ ├── Views
│ │ │ ├── ALHeaderView.swift
│ │ │ └── ALScrollView.swift
│ │ └── Info.plist
│ ├── AppDelegate.swift
│ └── Controllers
│ │ ├── Launch
│ │ └── Base.lproj
│ │ │ └── LaunchScreen.storyboard
│ │ ├── RadioGroupViewController.swift
│ │ └── MainViewController.swift
└── ALRadioButtons Example.xcodeproj
│ ├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcuserdata
│ │ └── alxrguz.xcuserdatad
│ │ │ ├── UserInterfaceState.xcuserstate
│ │ │ └── WorkspaceSettings.xcsettings
│ └── xcshareddata
│ │ ├── WorkspaceSettings.xcsettings
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── swiftpm
│ │ └── Package.resolved
│ ├── xcuserdata
│ └── alxrguz.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
│ ├── xcshareddata
│ └── xcschemes
│ │ └── iOS App.xcscheme
│ └── project.pbxproj
├── Package.swift
├── .gitignore
├── ALRadioButtons.podspec
├── Sources
├── SupportingFiles
│ ├── Info.plist
│ └── RGRadioGroup.h
└── ALRadioButtons
│ ├── Model
│ ├── RadioGroupStyle.swift
│ └── RadioItem.swift
│ └── Views
│ ├── ALRadioIndicator.swift
│ ├── ALRadioGroupItem.swift
│ └── ALRadioGroup.swift
├── LICENSE
└── README.md
/Assets/about.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/Assets/about.png
--------------------------------------------------------------------------------
/Assets/socialPreview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/Assets/socialPreview.png
--------------------------------------------------------------------------------
/Assets/ALRadioButtons.sketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/Assets/ALRadioButtons.sketch
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-20x20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-20x20.png
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-29x29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-29x29.png
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-40x40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-40x40.png
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-76x76.png
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-20x20@2x.png
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-29x29@2x.png
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-40x40@2x.png
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-76x76@2x.png
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-20x20@2x.png
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-20x20@3x.png
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-29x29@2x.png
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-29x29@3x.png
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-40x40@2x.png
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-40x40@3x.png
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-60x60@2x.png
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/iphone-60x60@3x.png
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ipad-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ios-marketing-1024x1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/ios-marketing-1024x1024.png
--------------------------------------------------------------------------------
/ExampleApp/ALRadioButtons Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ExampleApp/ALRadioButtons Example.xcodeproj/xcuserdata/alxrguz.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/ExampleApp/ALRadioButtons Example.xcodeproj/project.xcworkspace/xcuserdata/alxrguz.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alxrguz/ALRadioButtons/HEAD/ExampleApp/ALRadioButtons Example.xcodeproj/project.xcworkspace/xcuserdata/alxrguz.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/ExampleApp/ALRadioButtons Example.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ExampleApp/ALRadioButtons Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ExampleApp/ALRadioButtons Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "SnapKit",
6 | "repositoryURL": "https://github.com/SnapKit/SnapKit",
7 | "state": {
8 | "branch": null,
9 | "revision": "d458564516e5676af9c70b4f4b2a9178294f1bc6",
10 | "version": "5.0.1"
11 | }
12 | }
13 | ]
14 | },
15 | "version": 1
16 | }
17 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.2
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "ALRadioButtons",
8 | platforms: [
9 | .iOS(.v10)
10 | ],
11 | products: [
12 | .library(name: "ALRadioButtons",targets: ["ALRadioButtons"]),
13 | ],
14 | targets: [
15 | .target(name: "ALRadioButtons")
16 | ]
17 | )
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # osX files
2 | .DS_Store
3 | .Trashes
4 | Icon
5 | Icon?
6 |
7 | ## Xcode Patch
8 | *.xcworkspace
9 | *.xcuserdata
10 | *.xcodeproj/*
11 | !*.xcodeproj/project.pbxproj
12 | !*.xcodeproj/xcshareddata/
13 | !*.xcworkspace/contents.xcworkspacedata
14 | /*.gcno
15 |
16 | ### Xcode Patch ###
17 | **/xcshareddata/WorkspaceSettings.xcsettings
18 |
19 | # Swift Package Manager
20 | .swiftpm
21 | /.build
22 |
23 |
24 | ## Build generated
25 | build/
26 | DerivedData/
27 |
28 | ## Various settings
29 | *.pbxuser
30 | !default.pbxuser
31 | *.mode1v3
32 | !default.mode1v3
33 | *.mode2v3
34 | !default.mode2v3
35 | *.perspectivev3
36 | !default.perspectivev3
37 | xcuserdata/
--------------------------------------------------------------------------------
/ExampleApp/iOS App/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | //
4 | // Created by Alexandr Guzenko on 17.08.2020.
5 | // Copyright © 2020. All rights reserved.
6 | //
7 |
8 | import UIKit
9 |
10 | @UIApplicationMain
11 | class AppDelegate: UIResponder, UIApplicationDelegate {
12 | var window: UIWindow?
13 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
14 | window = UIWindow()
15 | window?.rootViewController = UINavigationController(rootViewController: MainViewController())
16 | window?.makeKeyAndVisible()
17 | return true
18 | }
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/ExampleApp/ALRadioButtons Example.xcodeproj/project.xcworkspace/xcuserdata/alxrguz.xcuserdatad/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildLocationStyle
6 | UseAppPreferences
7 | CustomBuildLocationType
8 | RelativeToDerivedData
9 | DerivedDataLocationStyle
10 | Default
11 | IssueFilterStyle
12 | ShowActiveSchemeOnly
13 | LiveSourceIssuesEnabled
14 |
15 | ShowSharedSchemesAutomaticallyEnabled
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/ALRadioButtons.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |spec|
2 | spec.name = "ALRadioButtons"
3 | spec.version = "1.2.2"
4 | spec.summary = "RadioButtons for iOS. Inherited from UIControl, support 2 native styles, fully customizable."
5 |
6 | spec.homepage = "https://github.com/alxrguz/ALRadioButtons"
7 | spec.source = { :git => "https://github.com/alxrguz/ALRadioButtons.git", :tag => "#{spec.version}" }
8 | spec.license = { :type => "MIT", :file => "LICENSE" }
9 |
10 | spec.author = { "Alexandr Guzenko" => "alxrguz@icloud.com" }
11 |
12 | spec.platform = :ios
13 | spec.ios.framework = 'UIKit'
14 | spec.swift_version = ['4.2', '5.0']
15 | spec.ios.deployment_target = "10.0"
16 |
17 | spec.source_files = "Sources/ALRadioButtons/**/*.swift"
18 |
19 | end
--------------------------------------------------------------------------------
/Sources/SupportingFiles/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Alexandr Guzenko
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Sources/ALRadioButtons/Model/RadioGroupStyle.swift:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) 2020 Alexandr Guzenko (alxrguz@icloud.com)
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | import Foundation
24 |
25 | /**
26 | Стиль для `ALRadioButton`
27 | */
28 | public enum ALRadioButtonStyle {
29 | /// The style is similar to UITableView insertGrouped style
30 | case grouped
31 |
32 | /// The style is similar to UITableView plain style
33 | case standard
34 | }
35 |
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Views/ALHeaderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ALHeaderView.swift
3 | // iOS App
4 | //
5 | // Created by Alexandr Guzenko on 30.07.2021.
6 | // Copyright © 2021 alxrguz. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | final class ALHeaderView: UIView {
12 | // MARK: - UI Elements
13 | public lazy var titleLabel = UILabel()
14 | public lazy var subtitleLabel = UILabel()
15 |
16 | // MARK: - Life cycle
17 | init() {
18 | super.init(frame: .zero)
19 | setupView()
20 | setupConstraints()
21 | }
22 |
23 | required init?(coder: NSCoder) {
24 | fatalError("init(coder:) has not been implemented")
25 | }
26 | }
27 |
28 | // MARK: - Layout Setup
29 | private extension ALHeaderView {
30 | func setupColors() {
31 | titleLabel.textColor = .label
32 | subtitleLabel.textColor = .secondaryLabel
33 | }
34 |
35 | func setupView() {
36 | setupColors()
37 |
38 | titleLabel.font = .systemFont(ofSize: 20, weight: .bold)
39 | subtitleLabel.font = .systemFont(ofSize: 15, weight: .regular)
40 | }
41 |
42 | func setupConstraints() {
43 | addSubview(titleLabel)
44 | addSubview(subtitleLabel)
45 |
46 | titleLabel.snp.makeConstraints {
47 | $0.top.leading.trailing.equalToSuperview()
48 | }
49 |
50 | subtitleLabel.snp.makeConstraints {
51 | $0.top.equalTo(titleLabel.snp.bottom).offset(2)
52 | $0.bottom.leading.trailing.equalToSuperview()
53 | }
54 | }
55 | }
56 |
57 |
--------------------------------------------------------------------------------
/Sources/SupportingFiles/RGRadioGroup.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) 2020 Alexandr Guzenko (alxrguz@icloud.com)
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | #import
24 |
25 | //! Project version number for RGRadioGroup.
26 | FOUNDATION_EXPORT double RGRadioGroupVersionNumber;
27 |
28 | //! Project version string for RGRadioGroup.
29 | FOUNDATION_EXPORT const unsigned char RGRadioGroupVersionString[];
30 |
31 | // In this header, you should import all the public headers of your framework using statements like #import
32 |
33 |
34 |
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | ALRadioButtons
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Views/ALScrollView.swift:
--------------------------------------------------------------------------------
1 |
2 |
3 | import UIKit
4 | import SnapKit
5 |
6 | public class ALScrollView: UIScrollView {
7 | // MARK: UI Elements
8 | public lazy var contentView = UIView()
9 | public lazy var backgroundView = UIView()
10 |
11 | // MARK: Public Properties
12 | var orientation: NSLayoutConstraint.Axis = .vertical { didSet { remakeConstraints() } }
13 |
14 | // MARK: Lifecycle
15 | public init(orientation: NSLayoutConstraint.Axis = .vertical) {
16 | super.init(frame: .zero)
17 | delaysContentTouches = false
18 | self.orientation = orientation
19 |
20 | addSubview(backgroundView)
21 | addSubview(contentView)
22 | }
23 |
24 | public required init?(coder: NSCoder) {
25 | fatalError("init(coder:) has not been implemented")
26 | }
27 |
28 | public override func touchesShouldCancel(in view: UIView) -> Bool {
29 | if view is UIControl && (view is UISwitch) == false { return true }
30 | return super.touchesShouldCancel(in: view)
31 | }
32 |
33 | public override func didMoveToSuperview() {
34 | super.didMoveToSuperview()
35 | remakeConstraints()
36 | }
37 |
38 | public func remakeConstraints() {
39 | guard let superview = self.superview else { return}
40 | backgroundView.snp.remakeConstraints {
41 | $0.edges.equalTo(superview)
42 | }
43 |
44 | contentView.snp.remakeConstraints {
45 | $0.edges.equalToSuperview()
46 | if orientation == .vertical {
47 | $0.width.equalToSuperview()
48 | } else {
49 | $0.height.equalToSuperview()
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/ExampleApp/ALRadioButtons Example.xcodeproj/xcuserdata/alxrguz.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | SnapKitPlayground (Playground) 1.xcscheme
8 |
9 | isShown
10 |
11 | orderHint
12 | 3
13 |
14 | SnapKitPlayground (Playground) 2.xcscheme
15 |
16 | isShown
17 |
18 | orderHint
19 | 4
20 |
21 | SnapKitPlayground (Playground) 3.xcscheme
22 |
23 | isShown
24 |
25 | orderHint
26 | 4
27 |
28 | SnapKitPlayground (Playground) 4.xcscheme
29 |
30 | isShown
31 |
32 | orderHint
33 | 5
34 |
35 | SnapKitPlayground (Playground) 5.xcscheme
36 |
37 | isShown
38 |
39 | orderHint
40 | 6
41 |
42 | SnapKitPlayground (Playground).xcscheme
43 |
44 | isShown
45 |
46 | orderHint
47 | 1
48 |
49 | iOS App.xcscheme_^#shared#^_
50 |
51 | orderHint
52 | 0
53 |
54 |
55 | SuppressBuildableAutocreation
56 |
57 | BB3959F224EA71EE00B965F5
58 |
59 | primary
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Controllers/Launch/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Sources/ALRadioButtons/Model/RadioItem.swift:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) 2020 Alexandr Guzenko (alxrguz@icloud.com)
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | import Foundation
24 |
25 |
26 | /**
27 | Text content for `ALRadioButton`
28 | */
29 | public struct ALRadioItem {
30 | /// The main text that appears next to the indicator
31 | public let title: String
32 |
33 | /// Optional text under the heading
34 | public let subtitle: String?
35 |
36 | /// Optional text to the right of the title
37 | public let detail: String?
38 |
39 | public init(title: String, subtitle: String? = nil, detail: String? = nil) {
40 | self.title = title
41 | self.subtitle = subtitle
42 | self.detail = detail
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Sources/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "iphone-20x20@2x.png",
5 | "idiom" : "iphone",
6 | "scale" : "2x",
7 | "size" : "20x20"
8 | },
9 | {
10 | "filename" : "iphone-20x20@3x.png",
11 | "idiom" : "iphone",
12 | "scale" : "3x",
13 | "size" : "20x20"
14 | },
15 | {
16 | "filename" : "iphone-29x29@2x.png",
17 | "idiom" : "iphone",
18 | "scale" : "2x",
19 | "size" : "29x29"
20 | },
21 | {
22 | "filename" : "iphone-29x29@3x.png",
23 | "idiom" : "iphone",
24 | "scale" : "3x",
25 | "size" : "29x29"
26 | },
27 | {
28 | "filename" : "iphone-40x40@2x.png",
29 | "idiom" : "iphone",
30 | "scale" : "2x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "filename" : "iphone-40x40@3x.png",
35 | "idiom" : "iphone",
36 | "scale" : "3x",
37 | "size" : "40x40"
38 | },
39 | {
40 | "filename" : "iphone-60x60@2x.png",
41 | "idiom" : "iphone",
42 | "scale" : "2x",
43 | "size" : "60x60"
44 | },
45 | {
46 | "filename" : "iphone-60x60@3x.png",
47 | "idiom" : "iphone",
48 | "scale" : "3x",
49 | "size" : "60x60"
50 | },
51 | {
52 | "filename" : "ipad-20x20.png",
53 | "idiom" : "ipad",
54 | "scale" : "1x",
55 | "size" : "20x20"
56 | },
57 | {
58 | "filename" : "ipad-20x20@2x.png",
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "20x20"
62 | },
63 | {
64 | "filename" : "ipad-29x29.png",
65 | "idiom" : "ipad",
66 | "scale" : "1x",
67 | "size" : "29x29"
68 | },
69 | {
70 | "filename" : "ipad-29x29@2x.png",
71 | "idiom" : "ipad",
72 | "scale" : "2x",
73 | "size" : "29x29"
74 | },
75 | {
76 | "filename" : "ipad-40x40.png",
77 | "idiom" : "ipad",
78 | "scale" : "1x",
79 | "size" : "40x40"
80 | },
81 | {
82 | "filename" : "ipad-40x40@2x.png",
83 | "idiom" : "ipad",
84 | "scale" : "2x",
85 | "size" : "40x40"
86 | },
87 | {
88 | "filename" : "ipad-76x76.png",
89 | "idiom" : "ipad",
90 | "scale" : "1x",
91 | "size" : "76x76"
92 | },
93 | {
94 | "filename" : "ipad-76x76@2x.png",
95 | "idiom" : "ipad",
96 | "scale" : "2x",
97 | "size" : "76x76"
98 | },
99 | {
100 | "filename" : "ipad-83.5x83.5@2x.png",
101 | "idiom" : "ipad",
102 | "scale" : "2x",
103 | "size" : "83.5x83.5"
104 | },
105 | {
106 | "filename" : "ios-marketing-1024x1024.png",
107 | "idiom" : "ios-marketing",
108 | "scale" : "1x",
109 | "size" : "1024x1024"
110 | }
111 | ],
112 | "info" : {
113 | "author" : "xcode",
114 | "version" : 1
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/ExampleApp/ALRadioButtons Example.xcodeproj/xcshareddata/xcschemes/iOS App.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
45 |
51 |
52 |
53 |
54 |
60 |
62 |
68 |
69 |
70 |
71 |
73 |
74 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ALRadioButtons
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | ## Navigation
10 |
11 | - [Requirements](#requirements)
12 | - [Installation](#installation)
13 | - [Swift Package Manager](#Swift-Package-Manager)
14 | - [CocoaPods](#CocoaPods)
15 | - [Manually](#Manually)
16 | - [Usage](#usage)
17 | - [Quick Start](#Quick-Start)
18 | - [Customization](#Customization)
19 | - [Colors](#colors)
20 | - [Fonts](#Fonts)
21 | - [Layout](#Layout)
22 | - [License](https://github.com/SnapKit/SnapKit#license)
23 |
24 | ##
25 |
26 | ## Requirements
27 |
28 | - iOS 10.0 +
29 | - Swift 4.2 +
30 |
31 |
32 |
33 | ## Installation
34 |
35 | #### Swift Package Manager
36 |
37 | The [Swift Package Manager](https://swift.org/package-manager/) is a tool for managing the distribution of Swift code. It’s integrated with the Swift build system to automate the process of downloading, compiling, and linking dependencies.
38 |
39 | To integrate **ALRadioButtons** click `File -> Swift Package -> Add Package Dependency` and insert:
40 |
41 | ```ogdl
42 | https://github.com/alxrguz/ALRadioButtons
43 | ```
44 |
45 | #### CocoaPods
46 |
47 | **ALRadioButtons** is available through [CocoaPods](https://cocoapods.org/pods/ALRadioButtons). To install it, simply add the following line to your Podfile:
48 |
49 | ```ruby
50 | pod 'ALRadioButtons'
51 | ```
52 |
53 | #### Manually
54 |
55 | If you prefer not to use either of the aforementioned dependency managers, you can integrate ALRadioButtons into your project manually. Put `Source/ALRadioButtons` folder in your Xcode project.
56 |
57 |
58 |
59 | ## Usage
60 |
61 | #### Quick Start
62 |
63 | ```swift
64 | import ALRadioButtons
65 |
66 | class MyViewController: UIViewController {
67 |
68 | lazy var radioGroup = ALRadioGroup(items: [
69 | .init(title: "title1", subtitle: "subtitle1"),
70 | .init(title: "title2", subtitle: "subtitle2", detail: "Detail"),
71 | .init(title: "title3"),
72 | ], style: .grouped)
73 |
74 | override func viewDidLoad() {
75 | super.viewDidLoad()
76 |
77 | self.view.addSubview(radioGroup)
78 | // ... Setup layout
79 |
80 | radioGroup.selectedIndex = 0
81 | radioGroup.addTarget(self, action: #selector(radioGroupSelected(_:)), for: .valueChanged)
82 |
83 | // If the checkbox selection can be canceled, then set this property to true
84 | radioGroup.allowDeselection = true
85 | }
86 |
87 | @objc private func radioGroupSelected(_ sender: ALRadioGroup) {
88 | print(sender.selectedIndex)
89 | }
90 | }
91 | ```
92 |
93 |
94 |
95 | #### Customization
96 |
97 | #### Colors
98 |
99 | You can customize the buttons, headers and indicators colors depending on their state.
100 |
101 | ```swift
102 | radioGroup.selectedTitleColor = .systemBlue
103 | radioGroup.unselectedTitleColor = .black
104 | radioGroup.selectedDetailColor = .systemBlue
105 | radioGroup.unselectedDetailColor = .black
106 | radioGroup.selectedIndicatorColor = .systemBlue
107 | radioGroup.unselectedIndicatorColor = .systemBlue
108 | radioGroup.subtitleColor = .lightGray
109 | radioGroup.buttonBackgroundColor = .white
110 | radioGroup.separatorColor = .lightGray
111 | ```
112 |
113 |
114 |
115 | #### Font
116 |
117 | ```swift
118 | radioGroup.titleFont = .systemFont(ofSize: 16, weight: .medium)
119 | radioGroup.detailFont = .systemFont(ofSize: 16, weight: .regular)
120 | radioGroup.subtitleFont = .systemFont(ofSize: 13, weight: .regular)
121 | ```
122 |
123 |
124 |
125 | #### Layout
126 |
127 | ```swift
128 | radioGroup.cornerRadius = 14 // Button corner radius, actual for .grouped style
129 | radioGroup.buttonHeight = 50
130 | radioGroup.subtitleTopOffset = 8 // Subtitle offset from title bottom anchor
131 | radioGroup.separatorTopOffset = 8 // Separator offset from title or subtitle (if added) bottom anchor
132 | radioGroup.indicatorRingWidth = 2 // Outer ring width of indicator
133 | radioGroup.indicatorRingSize = 22 // Indicator outer ring size
134 | radioGroup.indicatorCircleInset = 5 // Indentation of the circle from the outer ring
135 | ```
136 |
137 |
138 |
139 | ## License
140 |
141 | **ALRadioButtons** is under MIT license. See the [LICENSE](https://github.com/alxrguz/ALRadioButtons/blob/master/LICENSE) file for more info.
142 |
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Controllers/RadioGroupViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SimpleExampleViewController.swift
3 | // iOS App
4 | //
5 | // Created by Alexandr Guzenko on 25.08.2021.
6 | // Copyright © 2021 alxrguz. All rights reserved.
7 | //
8 |
9 |
10 | import UIKit
11 | import ALRadioButtons
12 |
13 | class RadioGroupViewController: UIViewController {
14 |
15 | // MARK: - UIElements
16 |
17 | private lazy var contentView: UIView = {
18 | switch buttomType {
19 | case .simple(let style):
20 | return createSimpleView(style: style)
21 | case .subtitle(let style):
22 | return createSubtitleView(style: style)
23 | case .all(let style):
24 | return createSubtitleAndDetailView(style: style)
25 | }
26 | }()
27 |
28 | // MARK: - Private properties
29 |
30 | private var buttomType: MainViewController.RadioButtonType
31 |
32 | // MARK: - Life cycle
33 |
34 | init(type: MainViewController.RadioButtonType) {
35 | self.buttomType = type
36 | super.init(nibName: nil, bundle: nil)
37 | }
38 |
39 | required init?(coder: NSCoder) {
40 | fatalError("init(coder:) has not been implemented")
41 | }
42 |
43 |
44 | // MARK: - UIViewController
45 |
46 | override func viewDidLoad() {
47 | super.viewDidLoad()
48 | setupView()
49 | setupConstraints()
50 | }
51 | }
52 |
53 | // MARK: - Setup Layout
54 |
55 | private extension RadioGroupViewController {
56 | func setupColors() {
57 | view.backgroundColor = .systemGroupedBackground
58 | }
59 |
60 | func createSimpleView(style: ALRadioButtonStyle) -> UIView {
61 | let contentView = UIView()
62 | let group1HeaderView = ALHeaderView()
63 | group1HeaderView.subtitleLabel.text = "Vertical"
64 | let radioGroup1 = ALRadioGroup(
65 | items: [.init(title: "Variant 1"),
66 | .init(title: "Variant 2"),
67 | .init(title: "Variant 3")],
68 | style: style
69 | )
70 | radioGroup1.axis = .vertical
71 |
72 | let group2HeaderView = ALHeaderView()
73 | group2HeaderView.subtitleLabel.text = "Horizontal"
74 | let radioGroup2 = ALRadioGroup(
75 | items: [.init(title: "Variant 1"),
76 | .init(title: "Variant 2")],
77 | style: style
78 | )
79 | radioGroup2.separatorColor = .clear
80 | radioGroup2.axis = .horizontal
81 |
82 | contentView.addSubview(group1HeaderView)
83 | contentView.addSubview(radioGroup1)
84 | contentView.addSubview(group2HeaderView)
85 | contentView.addSubview(radioGroup2)
86 |
87 | group1HeaderView.snp.makeConstraints {
88 | $0.top.leading.trailing.equalToSuperview()
89 | }
90 |
91 | radioGroup1.snp.makeConstraints {
92 | $0.top.equalTo(group1HeaderView.snp.bottom).offset(10)
93 | $0.leading.trailing.equalToSuperview()
94 | }
95 |
96 | group2HeaderView.snp.makeConstraints {
97 | $0.top.equalTo(radioGroup1.snp.bottom).offset(30)
98 | $0.leading.trailing.equalToSuperview()
99 | }
100 |
101 | radioGroup2.snp.makeConstraints {
102 | $0.top.equalTo(group2HeaderView.snp.bottom).offset(10)
103 | $0.leading.trailing.equalToSuperview()
104 | $0.bottom.equalToSuperview()
105 | }
106 |
107 | return contentView
108 | }
109 |
110 | func createSubtitleView(style: ALRadioButtonStyle) -> UIView {
111 | return ALRadioGroup(
112 | items: [.init(title: "Variant 1", subtitle: "Description to the variant"),
113 | .init(title: "Variant 2", subtitle: "Description to the variant"),
114 | .init(title: "Variant 3", subtitle: "Description to the variant")],
115 | style: style
116 | )
117 | }
118 |
119 | func createSubtitleAndDetailView(style: ALRadioButtonStyle) -> UIView {
120 | return ALRadioGroup(
121 | items: [.init(title: "Variant 1", subtitle: "Description to the variant", detail: "Detail"),
122 | .init(title: "Variant 2", subtitle: "Description to the variant", detail: "Detail"),
123 | .init(title: "Variant 3", subtitle: "Description to the variant", detail: "Detail")],
124 | style: style
125 | )
126 | }
127 |
128 | func setupView() {
129 | setupColors()
130 |
131 | navigationItem.title = buttomType.title
132 | }
133 |
134 | func setupConstraints() {
135 | view.addSubview(contentView)
136 |
137 | contentView.snp.makeConstraints {
138 | $0.top.equalTo(view.safeAreaLayoutGuide).offset(20)
139 | $0.leading.trailing.equalToSuperview().inset(16)
140 | }
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/ExampleApp/iOS App/Controllers/MainViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | //
4 | // Created by Alexandr Guzenko on 17.08.2020.
5 | // Copyright © 2020. All rights reserved.
6 | //
7 |
8 | import UIKit
9 | import ALRadioButtons
10 |
11 | class MainViewController: UIViewController {
12 |
13 | // MARK: - UIElements
14 |
15 | private lazy var tableView = UITableView(frame: .zero, style: .insetGrouped)
16 |
17 | // MARK: - Private Properties
18 |
19 | let data: [SectionData] = [
20 | .init(title: "Simple", footer: "Regular radio buttons, no additional information", content: [.simple(style: .standard), .simple(style: .grouped)]),
21 | .init(title: "Subtitle", footer: "This example contains a description for each radio button", content: [.subtitle(style: .standard), .subtitle(style: .grouped)]),
22 | .init(title: "Subtitle & Detail", footer: "In addition to the description, detailed information is displayed on the right (like UITableViewCell)", content: [.all(style: .standard), .all(style: .grouped)])
23 | ]
24 |
25 | // MARK: - UIViewController
26 |
27 | override func viewDidLoad() {
28 | super.viewDidLoad()
29 | setupView()
30 | setupConstraints()
31 | }
32 | }
33 |
34 | // MARK: - UITableViewDelegate
35 |
36 | extension MainViewController: UITableViewDataSource {
37 | func numberOfSections(in tableView: UITableView) -> Int {
38 | data.count
39 | }
40 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
41 | data[section].content.count
42 | }
43 |
44 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
45 | let cell = UITableViewCell()
46 | let data = self.data[indexPath.section].content[indexPath.row].styleTitle
47 | cell.textLabel?.text = data
48 | cell.accessoryType = .disclosureIndicator
49 | return cell
50 | }
51 |
52 | func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
53 | data[section].title
54 | }
55 |
56 | func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
57 | data[section].footer
58 | }
59 | }
60 |
61 | // MARK: - UITableViewDelegate
62 |
63 | extension MainViewController: UITableViewDelegate {
64 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
65 | tableView.deselectRow(at: indexPath, animated: true)
66 | let data = self.data[indexPath.section].content[indexPath.row]
67 | let vc = RadioGroupViewController(type: data)
68 | self.navigationController?.pushViewController(vc, animated: true)
69 | }
70 | }
71 |
72 | // MARK: - Setup Layout
73 |
74 | private extension MainViewController {
75 | func setupColors() {
76 | view.backgroundColor = .systemGroupedBackground
77 | }
78 |
79 | func setupView() {
80 | setupColors()
81 |
82 | navigationItem.title = "ALRadioButtons"
83 |
84 | tableView.dataSource = self
85 | tableView.delegate = self
86 |
87 | }
88 |
89 | func setupConstraints() {
90 | view.addSubview(tableView)
91 |
92 | tableView.snp.makeConstraints {
93 | $0.edges.equalToSuperview()
94 | }
95 | }
96 | }
97 |
98 | // MARK: - Data Structures
99 |
100 | extension MainViewController {
101 | struct SectionData {
102 | var title: String?
103 | var footer: String?
104 | var content: [RadioButtonType]
105 | }
106 |
107 | enum RadioButtonType {
108 | case simple(style: ALRadioButtonStyle)
109 | case subtitle(style: ALRadioButtonStyle)
110 | case all(style: ALRadioButtonStyle)
111 |
112 | var styleTitle: String {
113 | switch self {
114 | case .simple(let style), .all(let style), .subtitle(let style):
115 | switch style {
116 | case .grouped:
117 | return "Grouped"
118 | case .standard:
119 | return "Standart"
120 | }
121 | }
122 | }
123 |
124 | var title: String {
125 | switch self {
126 | case .simple(let style):
127 | switch style {
128 | case .grouped:
129 | return "Simple (Grouped)"
130 | case .standard:
131 | return "Simple (Standart)"
132 | }
133 | case .all(let style):
134 | switch style {
135 | case .grouped:
136 | return "Subtitle & Detail (Grouped)"
137 | case .standard:
138 | return "Subtitle & Detail (Standart)"
139 | }
140 | case .subtitle(let style):
141 | switch style {
142 | case .grouped:
143 | return "Subtitle (Grouped)"
144 | case .standard:
145 | return "Subtitle (Standart)"
146 | }
147 | }
148 | }
149 | }
150 | }
151 |
152 |
153 |
154 |
155 |
--------------------------------------------------------------------------------
/Sources/ALRadioButtons/Views/ALRadioIndicator.swift:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) 2020 Alexandr Guzenko (alxrguz@icloud.com)
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | import UIKit
24 |
25 | public final class ALRadioIndicator: UIView {
26 | // MARK: - UI Elements
27 | private lazy var selectedView = UIView()
28 |
29 | // MARK: - Open Proporties
30 | public var isSelected: Bool = false {
31 | didSet {
32 | guard oldValue != isSelected else { return }
33 | if isSelected == true {
34 | selectedView.transform = .init(scaleX: 0.95, y: 0.95)
35 |
36 | UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.3) {
37 | self.setupColors()
38 | }
39 |
40 | UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.3) {
41 | self.selectedView.transform = .identity
42 | }
43 | } else {
44 | setupColors()
45 | }
46 | }
47 | }
48 |
49 | public var selectedColor: UIColor = .systemBlue {
50 | didSet {
51 | setupColors()
52 | }
53 | }
54 |
55 | public var unselectedColor: UIColor = .lightGray {
56 | didSet {
57 | setupColors()
58 | }
59 | }
60 |
61 | public var ringWidth: CGFloat = 2 {
62 | didSet {
63 | layer.borderWidth = ringWidth
64 | }
65 | }
66 |
67 | public var size: CGFloat = 22 {
68 | didSet {
69 | outRingHeightConstraint?.constant = size
70 | outRingWidthConstraint?.constant = size
71 | }
72 | }
73 |
74 | public var selectedViewInset: CGFloat = 5 {
75 | didSet {
76 | let selectedViewSize = size - selectedViewInset * 2
77 | selectedViewWidthConstraint?.constant = selectedViewSize
78 | selectedViewHeightConstraint?.constant = selectedViewSize
79 | }
80 | }
81 |
82 | // MARK: - Private Proporties
83 | var outRingHeightConstraint: NSLayoutConstraint?
84 | var outRingWidthConstraint: NSLayoutConstraint?
85 | var selectedViewWidthConstraint: NSLayoutConstraint?
86 | var selectedViewHeightConstraint: NSLayoutConstraint?
87 |
88 | // MARK: - Life cycle
89 | init() {
90 | super.init(frame: .zero)
91 | setupView()
92 | setupConstraints()
93 | }
94 |
95 | required init?(coder: NSCoder) {
96 | fatalError("init(coder:) has not been implemented")
97 | }
98 |
99 | public override func layoutSubviews() {
100 | super.layoutSubviews()
101 | setupFrame()
102 | }
103 | }
104 |
105 |
106 | // MARK: - Layout Setup
107 | private extension ALRadioIndicator {
108 | func setupColors() {
109 | if isSelected {
110 | layer.borderColor = selectedColor.cgColor
111 | selectedView.backgroundColor = selectedColor
112 | } else {
113 | layer.borderColor = unselectedColor.cgColor
114 | selectedView.backgroundColor = .clear
115 | }
116 | }
117 |
118 | func setupView() {
119 | setupColors()
120 | layer.borderWidth = ringWidth
121 | }
122 |
123 | func setupFrame() {
124 | selectedView.layer.cornerRadius = selectedView.frame.height / 2
125 | self.layer.cornerRadius = self.frame.height / 2
126 | }
127 |
128 | func setupConstraints() {
129 | self.translatesAutoresizingMaskIntoConstraints = false
130 | outRingHeightConstraint = self.heightAnchor.constraint(equalToConstant: size)
131 | outRingWidthConstraint = self.widthAnchor.constraint(equalToConstant: size)
132 | NSLayoutConstraint.activate([outRingHeightConstraint!, outRingWidthConstraint!])
133 |
134 | addSubview(selectedView)
135 | selectedView.translatesAutoresizingMaskIntoConstraints = false
136 | let selectedViewSize = size - selectedViewInset * 2
137 | selectedViewWidthConstraint = selectedView.widthAnchor.constraint(equalToConstant: selectedViewSize)
138 | selectedViewHeightConstraint = selectedView.heightAnchor.constraint(equalToConstant: selectedViewSize)
139 | NSLayoutConstraint.activate([
140 | selectedViewWidthConstraint!,
141 | selectedViewHeightConstraint!,
142 | selectedView.centerYAnchor.constraint(equalTo: self.centerYAnchor),
143 | selectedView.centerXAnchor.constraint(equalTo: self.centerXAnchor)
144 | ])
145 | }
146 | }
147 |
148 |
--------------------------------------------------------------------------------
/Sources/ALRadioButtons/Views/ALRadioGroupItem.swift:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) 2020 Alexandr Guzenko (alxrguz@icloud.com)
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | import UIKit
24 |
25 | public final class ALRadioButton: UIView {
26 | // MARK: - UI Elements
27 |
28 | /// Radio Item title label
29 | public lazy var titleLabel = UILabel()
30 |
31 | /// Radio Item detail label
32 | public lazy var detailLabel = UILabel()
33 |
34 | /// Radio Item subtitle label
35 | public lazy var subtitleLabel = UILabel()
36 |
37 | /// Round indicator
38 | public lazy var radioIndicator = ALRadioIndicator()
39 |
40 | /// View which is located `titleLabel` and `radioButton`
41 | public lazy var itemView = UIView()
42 |
43 | /// Horizontal stack for `titleLabel` and `radioButton`
44 | private lazy var titleStackView = UIStackView()
45 |
46 | /// Separator like `tableView` for `standard` style
47 | private lazy var separatorView = UIView()
48 |
49 | // MARK: - Open Proporties
50 |
51 | /**
52 | button state
53 | */
54 | public var isSelected: Bool = false {
55 | didSet {
56 | radioIndicator.isSelected = isSelected
57 | setupColors()
58 | }
59 | }
60 |
61 | /**
62 | `titleLabel` color when button selected
63 | */
64 | public var selectedTitleColor: UIColor = .systemBlue {
65 | didSet {
66 | setupColors()
67 | }
68 | }
69 |
70 | /**
71 | `titleLabel` color when button unselected
72 | */
73 | public var unselectedTitleColor: UIColor = .black {
74 | didSet {
75 | setupColors()
76 | }
77 | }
78 |
79 | /**
80 | `detailLabel` color when button selected
81 | */
82 | public var selectedDetailColor: UIColor = .systemBlue {
83 | didSet {
84 | setupColors()
85 | }
86 | }
87 |
88 | /**
89 | `detailLabel` color when button unselected
90 | */
91 | public var unselectedDetailColor: UIColor = .lightGray {
92 | didSet {
93 | setupColors()
94 | }
95 | }
96 |
97 | /**
98 | `radioIndicator` color when button selected
99 | */
100 | public var selectedIndicatorColor: UIColor = .systemBlue {
101 | didSet {
102 | radioIndicator.selectedColor = selectedIndicatorColor
103 | }
104 | }
105 |
106 | /**
107 | `radioIndicator` color when button unselected
108 | */
109 | public var unselectedIndicatorColor: UIColor = .systemBlue {
110 | didSet {
111 | radioIndicator.unselectedColor = unselectedIndicatorColor
112 | }
113 | }
114 |
115 | /**
116 | Separator color
117 |
118 | - Note: Actual for `ALRadioButtonStyle.standard` style
119 | */
120 | public var separatorColor: UIColor = .lightGray {
121 | didSet {
122 | separatorView.backgroundColor = separatorColor
123 | }
124 | }
125 |
126 | /**
127 | View height at which indicator and title are located, subtitle is not included here
128 | */
129 | public var buttonHeight: CGFloat = 50 {
130 | didSet {
131 | itemHeightConstraint?.constant = buttonHeight
132 | }
133 | }
134 |
135 | /**
136 | `subtitleLabel` offset from title bottom anchor
137 | */
138 | public var subtitleTopOffset: CGFloat = 8 {
139 | didSet {
140 | subtitleOffsetConstraint?.constant = subtitleTopOffset
141 | }
142 | }
143 |
144 | /**
145 | separator offset from title or subtitle (if added) bottom anchor
146 |
147 | - Note: Actual for `ALRadioButtonStyle.standard` style
148 | */
149 | public var separatorTopOffset: CGFloat = 8 {
150 | didSet {
151 | separatorOffsetConstraint?.constant = -separatorTopOffset
152 | }
153 | }
154 |
155 | // MARK: - Private Proporties
156 | private var itemHeightConstraint: NSLayoutConstraint?
157 | private var subtitleOffsetConstraint: NSLayoutConstraint?
158 | private var separatorOffsetConstraint: NSLayoutConstraint?
159 | private var style: ALRadioButtonStyle
160 |
161 | // MARK: - Life cycle
162 | public init(item: ALRadioItem, style: ALRadioButtonStyle) {
163 | self.style = style
164 | super.init(frame: .zero)
165 | titleLabel.text = item.title
166 | subtitleLabel.text = item.subtitle
167 | detailLabel.text = item.detail
168 | setupView()
169 | setupConstraints()
170 | }
171 |
172 | required init?(coder: NSCoder) {
173 | fatalError("init(coder:) has not been implemented")
174 | }
175 | }
176 |
177 | // MARK: - Layout Setup
178 | private extension ALRadioButton {
179 | func setupColors() {
180 | titleLabel.textColor = .black
181 | if isSelected {
182 | titleLabel.textColor = selectedTitleColor
183 | detailLabel.textColor = selectedDetailColor
184 | } else {
185 | titleLabel.textColor = unselectedTitleColor
186 | detailLabel.textColor = unselectedDetailColor
187 | }
188 |
189 | separatorView.backgroundColor = separatorColor
190 | }
191 |
192 | func setupView() {
193 | setupColors()
194 |
195 | layer.masksToBounds = true
196 | clipsToBounds = true
197 |
198 | titleStackView.axis = .horizontal
199 | titleStackView.alignment = .fill
200 | titleStackView.spacing = 12
201 | titleStackView.addArrangedSubview(radioIndicator)
202 | titleStackView.addArrangedSubview(titleLabel)
203 | titleStackView.addArrangedSubview(detailLabel)
204 |
205 | titleLabel.font = .systemFont(ofSize: 16, weight: .medium)
206 | titleLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
207 |
208 | detailLabel.font = .systemFont(ofSize: 16, weight: .regular)
209 | detailLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
210 |
211 | subtitleLabel.font = .systemFont(ofSize: 13, weight: .regular)
212 | subtitleLabel.numberOfLines = 0
213 | }
214 |
215 | func setupConstraints() {
216 | switch style {
217 | case .standard: standardStyle()
218 | case .grouped: groupedStyle()
219 | }
220 | }
221 |
222 | func standardStyle() {
223 | addSubview(itemView)
224 | itemView.addSubview(titleStackView)
225 |
226 | itemHeightConstraint = itemView.heightAnchor.constraint(equalToConstant: buttonHeight)
227 | itemView.translatesAutoresizingMaskIntoConstraints = false
228 | NSLayoutConstraint.activate([
229 | itemHeightConstraint!,
230 | itemView.topAnchor.constraint(equalTo: self.topAnchor),
231 | itemView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
232 | itemView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
233 | ])
234 |
235 | titleStackView.translatesAutoresizingMaskIntoConstraints = false
236 | NSLayoutConstraint.activate([
237 | titleStackView.centerYAnchor.constraint(equalTo: itemView.centerYAnchor),
238 | titleStackView.leadingAnchor.constraint(equalTo: itemView.leadingAnchor),
239 | titleStackView.trailingAnchor.constraint(equalTo: itemView.trailingAnchor),
240 | ])
241 |
242 | if subtitleLabel.text != nil {
243 | addSubview(subtitleLabel)
244 | subtitleOffsetConstraint = subtitleLabel.topAnchor.constraint(equalTo: itemView.bottomAnchor, constant: subtitleTopOffset)
245 | separatorOffsetConstraint = subtitleLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -separatorTopOffset)
246 | subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
247 | NSLayoutConstraint.activate([
248 | subtitleOffsetConstraint!,
249 | subtitleLabel.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor),
250 | subtitleLabel.trailingAnchor.constraint(equalTo: titleLabel.trailingAnchor),
251 | separatorOffsetConstraint!
252 | ])
253 | } else {
254 | separatorOffsetConstraint = itemView.bottomAnchor.constraint(lessThanOrEqualTo: self.bottomAnchor, constant: -separatorTopOffset)
255 | separatorOffsetConstraint?.isActive = true
256 | }
257 |
258 | addSubview(separatorView)
259 | separatorView.translatesAutoresizingMaskIntoConstraints = false
260 | NSLayoutConstraint.activate([
261 | separatorView.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor),
262 | separatorView.trailingAnchor.constraint(equalTo: titleStackView.trailingAnchor),
263 | separatorView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
264 | separatorView.heightAnchor.constraint(equalToConstant: 0.5)
265 | ])
266 | }
267 |
268 | func groupedStyle() {
269 | addSubview(itemView)
270 | itemView.addSubview(titleStackView)
271 |
272 | itemHeightConstraint = itemView.heightAnchor.constraint(equalToConstant: buttonHeight)
273 | itemView.translatesAutoresizingMaskIntoConstraints = false
274 | NSLayoutConstraint.activate([
275 | itemHeightConstraint!,
276 | itemView.topAnchor.constraint(equalTo: self.topAnchor),
277 | itemView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
278 | itemView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
279 | itemView.bottomAnchor.constraint(lessThanOrEqualTo: self.bottomAnchor)
280 | ])
281 |
282 | titleStackView.translatesAutoresizingMaskIntoConstraints = false
283 | NSLayoutConstraint.activate([
284 | titleStackView.centerYAnchor.constraint(equalTo: itemView.centerYAnchor),
285 | titleStackView.leadingAnchor.constraint(equalTo: itemView.leadingAnchor, constant: 15),
286 | titleStackView.trailingAnchor.constraint(equalTo: itemView.trailingAnchor, constant: -15),
287 | ])
288 |
289 | if subtitleLabel.text != nil {
290 | addSubview(subtitleLabel)
291 |
292 | subtitleOffsetConstraint = subtitleLabel.topAnchor.constraint(equalTo: itemView.bottomAnchor, constant: subtitleTopOffset)
293 | subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
294 | NSLayoutConstraint.activate([
295 | subtitleOffsetConstraint!,
296 | subtitleLabel.leadingAnchor.constraint(equalTo: radioIndicator.leadingAnchor),
297 | subtitleLabel.trailingAnchor.constraint(equalTo: titleLabel.trailingAnchor),
298 | subtitleLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor)
299 | ])
300 | }
301 | }
302 | }
303 |
--------------------------------------------------------------------------------
/Sources/ALRadioButtons/Views/ALRadioGroup.swift:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) 2020 Alexandr Guzenko (alxrguz@icloud.com)
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | import UIKit
24 |
25 |
26 | /// `UIControl` element that combines `ALRadioButton` in a group.
27 | public final class ALRadioGroup: UIControl {
28 | // MARK: - UI Elements
29 | private lazy var itemsStackView = UIStackView()
30 |
31 | // MARK: - Open Proporties
32 |
33 | /**
34 | Index of selected item
35 |
36 | Default is -1
37 |
38 | - Note: If you do not set this value, then no item from the group will be selected
39 | */
40 | public var selectedIndex: Int = -1 {
41 | didSet {
42 | item(at: oldValue)?.isSelected = false
43 | item(at: selectedIndex)?.isSelected = true
44 | }
45 | }
46 |
47 | /**
48 | If this parameter is set to true, then by tap on an already selected item, the selection will be canceled, and the `selectedIndex` will become `-1`. Default is false
49 | */
50 | public var allowDeselection: Bool = false
51 |
52 | /**
53 | Title color if `ALRadioButton` is selected
54 | */
55 | public var selectedTitleColor: UIColor = .systemBlue {
56 | didSet {
57 | items.forEach { $0.selectedTitleColor = selectedTitleColor }
58 | }
59 | }
60 |
61 | /**
62 | Title color if `ALRadioButton` is unselected
63 | */
64 | public var unselectedTitleColor: UIColor = .black {
65 | didSet {
66 | items.forEach { $0.unselectedTitleColor = unselectedTitleColor }
67 | }
68 | }
69 |
70 | /**
71 | Detail color if `ALRadioButton` is selected
72 | */
73 | public var selectedDetailColor: UIColor = .systemBlue {
74 | didSet {
75 | items.forEach { $0.selectedDetailColor = selectedDetailColor }
76 | }
77 | }
78 |
79 | /**
80 | Detail color if `ALRadioButton` is unselected
81 | */
82 | public var unselectedDetailColor: UIColor = .lightGray {
83 | didSet {
84 | items.forEach { $0.unselectedDetailColor = unselectedDetailColor }
85 | }
86 | }
87 |
88 | /**
89 | Indicator (round ring with dot at center) color if `ALRadioButton` is selected
90 | */
91 | public var selectedIndicatorColor: UIColor = .systemBlue {
92 | didSet {
93 | items.forEach { $0.selectedIndicatorColor = selectedIndicatorColor }
94 | }
95 | }
96 |
97 | /**
98 | Indicator (round ring with dot at center) color if `ALRadioButton` is unselected
99 | */
100 | public var unselectedIndicatorColor: UIColor = .lightGray {
101 | didSet {
102 | items.forEach { $0.unselectedIndicatorColor = unselectedIndicatorColor }
103 | }
104 | }
105 |
106 | /**
107 | `ALRadioButton` subtitle color
108 | */
109 | public var subtitleColor: UIColor = .lightGray {
110 | didSet {
111 | items.forEach { $0.subtitleLabel.textColor = subtitleColor }
112 | }
113 | }
114 |
115 | /**
116 | `ALRadioButton` background color
117 |
118 | - Note: Actual for `ALRadioButtonStyle.grouped` style
119 | */
120 | public var buttonBackgroundColor: UIColor = .clear {
121 | didSet {
122 | items.forEach { $0.itemView.backgroundColor = buttonBackgroundColor }
123 | }
124 | }
125 |
126 | /**
127 | Separator color
128 |
129 | - Note: Actual for `ALRadioButtonStyle.standard` style
130 | */
131 | public var separatorColor: UIColor = .lightGray {
132 | didSet {
133 | let lastItem = items.last
134 | items.forEach {
135 | $0.separatorColor = $0 == lastItem ? .clear : separatorColor
136 | }
137 | }
138 | }
139 |
140 | /**
141 | `ALRadioButton` title font
142 | */
143 | public var titleFont: UIFont = .systemFont(ofSize: 16, weight: .medium) {
144 | didSet {
145 | items.forEach { $0.titleLabel.font = titleFont }
146 | }
147 | }
148 |
149 |
150 | /**
151 | `ALRadioButton` detail font
152 | */
153 | public var detailFont: UIFont = .systemFont(ofSize: 16, weight: .regular) {
154 | didSet {
155 | items.forEach { $0.detailLabel.font = detailFont }
156 | }
157 | }
158 |
159 | /**
160 | `ALRadioButton` subtitle font
161 | */
162 | public var subtitleFont: UIFont = .systemFont(ofSize: 13, weight: .regular) {
163 | didSet {
164 | items.forEach { $0.subtitleLabel.font = subtitleFont }
165 | }
166 | }
167 |
168 | /**
169 | `ALRadioButton` corner radius
170 |
171 | - Note: Actual for `ALRadioButtonStyle.grouped` style
172 | */
173 | public var cornerRadius: CGFloat = 14 {
174 | didSet {
175 | items.forEach { $0.itemView.layer.cornerRadius = cornerRadius }
176 | }
177 | }
178 |
179 | /**
180 | `ALRadioButton` height
181 |
182 | - Note: View height at which indicator and title are located, subtitle is not included here
183 | */
184 | public var buttonHeight: CGFloat = 50 {
185 | didSet {
186 | items.forEach { $0.buttonHeight = buttonHeight }
187 | }
188 | }
189 |
190 | /**
191 | `ALRadioButton` subtitle offset from title bottom anchor
192 | */
193 | public var subtitleTopOffset: CGFloat = 8 {
194 | didSet {
195 | items.forEach { $0.subtitleTopOffset = subtitleTopOffset }
196 | }
197 | }
198 |
199 | /**
200 | `ALRadioButton` separator offset from title or subtitle (if added) bottom anchor
201 |
202 | - Note: Actual for `ALRadioButtonStyle.standard` style
203 | */
204 | public var separatorTopOffset: CGFloat = 8 {
205 | didSet {
206 | items.forEach { $0.separatorTopOffset = separatorTopOffset }
207 | }
208 | }
209 |
210 | /**
211 | Outer ring width of indicator
212 | */
213 | public var indicatorRingWidth: CGFloat = 2 {
214 | didSet {
215 | items.forEach { $0.radioIndicator.ringWidth = indicatorRingWidth }
216 | }
217 | }
218 |
219 | /**
220 | Indicator outer ring size
221 | */
222 | public var indicatorRingSize: CGFloat = 22 {
223 | didSet {
224 | items.forEach { $0.radioIndicator.size = indicatorRingSize }
225 | }
226 | }
227 |
228 | /**
229 | Indentation of the circle from the outer ring
230 | */
231 | public var indicatorCircleInset: CGFloat = 5 {
232 | didSet {
233 | items.forEach { $0.radioIndicator.selectedViewInset = indicatorCircleInset }
234 | }
235 | }
236 |
237 | /**
238 | `ALRadioButton` spacing
239 | */
240 | public var spacing: CGFloat = 15 {
241 | didSet {
242 | itemsStackView.spacing = spacing
243 | }
244 | }
245 |
246 | /**
247 | The axis on which the `ALRadioButton` are located
248 | */
249 | public var axis: NSLayoutConstraint.Axis = .vertical {
250 | didSet {
251 | itemsStackView.axis = axis
252 |
253 | switch axis {
254 | case .horizontal:
255 | itemsStackView.distribution = .fillEqually
256 | case .vertical:
257 | itemsStackView.distribution = .fill
258 | @unknown default:
259 | break
260 | }
261 | }
262 | }
263 |
264 | /**
265 | Haptic response when user clicks a button
266 | */
267 | public var hapticResponse: Bool = true
268 |
269 | // MARK: - Private Proporties
270 | private var items: [ALRadioButton] {
271 | return itemsStackView.arrangedSubviews.compactMap { $0 as? ALRadioButton }
272 | }
273 |
274 | private let haptic = UISelectionFeedbackGenerator()
275 |
276 | // MARK: - Life cycle
277 |
278 | /// `ALRadioGroup` init
279 | /// - Parameters:
280 | /// - items: Array of `ALRadioItem`, one `ALRadioItem` is equivalent to one `ALRadioButton`
281 | /// - style: `ALRadioButton` style
282 | public init(items: [ALRadioItem], style: ALRadioButtonStyle = .standard) {
283 | super.init(frame: .zero)
284 | setupView(items: items, style: style)
285 | setupConstraints()
286 | }
287 |
288 | required init?(coder: NSCoder) {
289 | fatalError("init(coder:) has not been implemented")
290 | }
291 | }
292 |
293 | // MARK: - Handlers
294 | private extension ALRadioGroup {
295 | @objc func radioItemTapped(_ sender: UITapGestureRecognizer) {
296 | guard let radioItem = (sender.view as? ALRadioButton),
297 | var index = items.firstIndex(of: radioItem) else { return }
298 |
299 | if index == selectedIndex {
300 | if allowDeselection {
301 | index = -1
302 | } else {
303 | return
304 | }
305 | }
306 |
307 | if hapticResponse { haptic.selectionChanged() }
308 | selectedIndex = index
309 | sendActions(for: .valueChanged)
310 | }
311 | }
312 |
313 | // MARK: - Private Methods
314 | private extension ALRadioGroup {
315 | private func item(at index: Int) -> ALRadioButton? {
316 | guard index >= 0 && index < itemsStackView.arrangedSubviews.count else { return nil }
317 | return itemsStackView.arrangedSubviews[index] as? ALRadioButton
318 | }
319 | }
320 |
321 | // MARK: - Layout Setup
322 | private extension ALRadioGroup {
323 | func setupView(items: [ALRadioItem], style: ALRadioButtonStyle) {
324 | axis = .vertical
325 | itemsStackView.spacing = spacing
326 |
327 | items.forEach {
328 | let radioItem = ALRadioButton(item: $0, style: style)
329 | let tap = UITapGestureRecognizer(target: self, action: #selector(radioItemTapped(_:)))
330 | self.addGestureRecognizer(tap)
331 | radioItem.addGestureRecognizer(tap)
332 | itemsStackView.addArrangedSubview(radioItem)
333 | }
334 |
335 | basicAppearance(style: style)
336 | }
337 |
338 | func basicAppearance(style: ALRadioButtonStyle) {
339 | switch style {
340 | case .grouped: groupedAppearance()
341 | case .standard: standardAppearance()
342 | }
343 |
344 | }
345 |
346 | func groupedAppearance() {
347 | selectedIndicatorColor = .systemBlue
348 | selectedTitleColor = .systemBlue
349 |
350 | if #available(iOS 13.0, *) {
351 | buttonBackgroundColor = .secondarySystemGroupedBackground
352 | subtitleColor = .secondaryLabel
353 | unselectedIndicatorColor = .quaternaryLabel
354 | unselectedTitleColor = .label
355 | unselectedDetailColor = .secondaryLabel
356 | } else {
357 | buttonBackgroundColor = .white
358 | subtitleColor = .lightGray
359 | unselectedIndicatorColor = .lightGray
360 | unselectedTitleColor = .black
361 | unselectedDetailColor = .lightGray
362 | }
363 |
364 | titleFont = .systemFont(ofSize: 16, weight: .medium)
365 | subtitleFont = .systemFont(ofSize: 13, weight: .regular)
366 | subtitleTopOffset = 8
367 | cornerRadius = 14
368 | buttonHeight = 50
369 |
370 | indicatorRingSize = 22
371 | indicatorRingWidth = 2
372 | indicatorCircleInset = 5
373 | }
374 |
375 | func standardAppearance() {
376 | selectedIndicatorColor = .systemBlue
377 | selectedTitleColor = .systemBlue
378 | buttonBackgroundColor = .clear
379 |
380 | if #available(iOS 13.0, *) {
381 | subtitleColor = .secondaryLabel
382 | unselectedIndicatorColor = .quaternaryLabel
383 | separatorColor = .quaternaryLabel
384 | unselectedTitleColor = .label
385 | unselectedDetailColor = .secondaryLabel
386 | } else {
387 | subtitleColor = .lightGray
388 | unselectedIndicatorColor = .lightGray
389 | unselectedTitleColor = .black
390 | separatorColor = .lightGray
391 | unselectedDetailColor = .lightGray
392 | }
393 |
394 | titleFont = .systemFont(ofSize: 16, weight: .medium)
395 | subtitleFont = .systemFont(ofSize: 13, weight: .regular)
396 | subtitleTopOffset = 0
397 | cornerRadius = 0
398 | buttonHeight = 30
399 | separatorTopOffset = 8
400 |
401 | indicatorRingSize = 22
402 | indicatorRingWidth = 2
403 | indicatorCircleInset = 5
404 | }
405 |
406 | func setupConstraints() {
407 | addSubview(itemsStackView)
408 |
409 | itemsStackView.translatesAutoresizingMaskIntoConstraints = false
410 | NSLayoutConstraint.activate ([
411 | itemsStackView.topAnchor.constraint(equalTo: self.topAnchor),
412 | itemsStackView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
413 | itemsStackView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
414 | itemsStackView.trailingAnchor.constraint(equalTo: self.trailingAnchor)
415 | ])
416 | }
417 | }
418 |
--------------------------------------------------------------------------------
/ExampleApp/ALRadioButtons Example.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 52;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | BB19EEBF26B3F0E100CF3FE6 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = BB19EEBE26B3F0E100CF3FE6 /* SnapKit */; };
11 | BB19EEC126B3F11A00CF3FE6 /* ALHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB19EEC026B3F11A00CF3FE6 /* ALHeaderView.swift */; };
12 | BB3959F724EA71EE00B965F5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB3959F624EA71EE00B965F5 /* AppDelegate.swift */; };
13 | BB3959FB24EA71EE00B965F5 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB3959FA24EA71EE00B965F5 /* MainViewController.swift */; };
14 | BB395A0024EA71F200B965F5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BB3959FF24EA71F200B965F5 /* Assets.xcassets */; };
15 | BB395A0324EA71F200B965F5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BB395A0124EA71F200B965F5 /* LaunchScreen.storyboard */; };
16 | BB4DE09026D69E7600EE0206 /* RadioGroupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB4DE08F26D69E7600EE0206 /* RadioGroupViewController.swift */; };
17 | BBFF0BE026B3EA2800E7A4D0 /* ALRadioButtons in Frameworks */ = {isa = PBXBuildFile; productRef = BBFF0BDF26B3EA2800E7A4D0 /* ALRadioButtons */; };
18 | BBFF0BE526B3EB7900E7A4D0 /* ALScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBFF0BE426B3EB7900E7A4D0 /* ALScrollView.swift */; };
19 | /* End PBXBuildFile section */
20 |
21 | /* Begin PBXFileReference section */
22 | BB19EEC026B3F11A00CF3FE6 /* ALHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ALHeaderView.swift; sourceTree = ""; };
23 | BB3959F324EA71EE00B965F5 /* iOS App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iOS App.app"; sourceTree = BUILT_PRODUCTS_DIR; };
24 | BB3959F624EA71EE00B965F5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
25 | BB3959FA24EA71EE00B965F5 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; };
26 | BB3959FF24EA71F200B965F5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
27 | BB395A0224EA71F200B965F5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
28 | BB395A0424EA71F200B965F5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
29 | BB4DE08F26D69E7600EE0206 /* RadioGroupViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioGroupViewController.swift; sourceTree = ""; };
30 | BBFF0BDE26B3EA0E00E7A4D0 /* ALRadioButtons */ = {isa = PBXFileReference; lastKnownFileType = folder; name = ALRadioButtons; path = ..; sourceTree = ""; };
31 | BBFF0BE426B3EB7900E7A4D0 /* ALScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ALScrollView.swift; sourceTree = ""; };
32 | /* End PBXFileReference section */
33 |
34 | /* Begin PBXFrameworksBuildPhase section */
35 | BB3959F024EA71EE00B965F5 /* Frameworks */ = {
36 | isa = PBXFrameworksBuildPhase;
37 | buildActionMask = 2147483647;
38 | files = (
39 | BBFF0BE026B3EA2800E7A4D0 /* ALRadioButtons in Frameworks */,
40 | BB19EEBF26B3F0E100CF3FE6 /* SnapKit in Frameworks */,
41 | );
42 | runOnlyForDeploymentPostprocessing = 0;
43 | };
44 | /* End PBXFrameworksBuildPhase section */
45 |
46 | /* Begin PBXGroup section */
47 | BB3959EA24EA71EE00B965F5 = {
48 | isa = PBXGroup;
49 | children = (
50 | BBFF0BDE26B3EA0E00E7A4D0 /* ALRadioButtons */,
51 | BB3959F524EA71EE00B965F5 /* iOS App */,
52 | BB3959F424EA71EE00B965F5 /* Products */,
53 | BB65E1FD266669ED009B68D4 /* Frameworks */,
54 | );
55 | sourceTree = "";
56 | };
57 | BB3959F424EA71EE00B965F5 /* Products */ = {
58 | isa = PBXGroup;
59 | children = (
60 | BB3959F324EA71EE00B965F5 /* iOS App.app */,
61 | );
62 | name = Products;
63 | sourceTree = "";
64 | };
65 | BB3959F524EA71EE00B965F5 /* iOS App */ = {
66 | isa = PBXGroup;
67 | children = (
68 | BB3959F624EA71EE00B965F5 /* AppDelegate.swift */,
69 | BB395A0A24EA722A00B965F5 /* Controllers */,
70 | BB395A0B24EA723800B965F5 /* Sources */,
71 | );
72 | path = "iOS App";
73 | sourceTree = "";
74 | };
75 | BB395A0A24EA722A00B965F5 /* Controllers */ = {
76 | isa = PBXGroup;
77 | children = (
78 | BB395A0D24EA727000B965F5 /* Launch */,
79 | BB3959FA24EA71EE00B965F5 /* MainViewController.swift */,
80 | BB4DE08F26D69E7600EE0206 /* RadioGroupViewController.swift */,
81 | );
82 | path = Controllers;
83 | sourceTree = "";
84 | };
85 | BB395A0B24EA723800B965F5 /* Sources */ = {
86 | isa = PBXGroup;
87 | children = (
88 | BBFF0BE326B3EB4F00E7A4D0 /* Views */,
89 | BB3959FF24EA71F200B965F5 /* Assets.xcassets */,
90 | BB395A0424EA71F200B965F5 /* Info.plist */,
91 | );
92 | path = Sources;
93 | sourceTree = "";
94 | };
95 | BB395A0D24EA727000B965F5 /* Launch */ = {
96 | isa = PBXGroup;
97 | children = (
98 | BB395A0124EA71F200B965F5 /* LaunchScreen.storyboard */,
99 | );
100 | path = Launch;
101 | sourceTree = "";
102 | };
103 | BB65E1FD266669ED009B68D4 /* Frameworks */ = {
104 | isa = PBXGroup;
105 | children = (
106 | );
107 | name = Frameworks;
108 | sourceTree = "";
109 | };
110 | BBFF0BE326B3EB4F00E7A4D0 /* Views */ = {
111 | isa = PBXGroup;
112 | children = (
113 | BB19EEC026B3F11A00CF3FE6 /* ALHeaderView.swift */,
114 | BBFF0BE426B3EB7900E7A4D0 /* ALScrollView.swift */,
115 | );
116 | path = Views;
117 | sourceTree = "";
118 | };
119 | /* End PBXGroup section */
120 |
121 | /* Begin PBXNativeTarget section */
122 | BB3959F224EA71EE00B965F5 /* iOS App */ = {
123 | isa = PBXNativeTarget;
124 | buildConfigurationList = BB395A0724EA71F200B965F5 /* Build configuration list for PBXNativeTarget "iOS App" */;
125 | buildPhases = (
126 | BB3959EF24EA71EE00B965F5 /* Sources */,
127 | BB3959F024EA71EE00B965F5 /* Frameworks */,
128 | BB3959F124EA71EE00B965F5 /* Resources */,
129 | );
130 | buildRules = (
131 | );
132 | dependencies = (
133 | );
134 | name = "iOS App";
135 | packageProductDependencies = (
136 | BBFF0BDF26B3EA2800E7A4D0 /* ALRadioButtons */,
137 | BB19EEBE26B3F0E100CF3FE6 /* SnapKit */,
138 | );
139 | productName = "Panda School";
140 | productReference = BB3959F324EA71EE00B965F5 /* iOS App.app */;
141 | productType = "com.apple.product-type.application";
142 | };
143 | /* End PBXNativeTarget section */
144 |
145 | /* Begin PBXProject section */
146 | BB3959EB24EA71EE00B965F5 /* Project object */ = {
147 | isa = PBXProject;
148 | attributes = {
149 | LastSwiftUpdateCheck = 1160;
150 | LastUpgradeCheck = 1160;
151 | ORGANIZATIONNAME = alxrguz;
152 | TargetAttributes = {
153 | BB3959F224EA71EE00B965F5 = {
154 | CreatedOnToolsVersion = 11.6;
155 | };
156 | };
157 | };
158 | buildConfigurationList = BB3959EE24EA71EE00B965F5 /* Build configuration list for PBXProject "ALRadioButtons Example" */;
159 | compatibilityVersion = "Xcode 9.3";
160 | developmentRegion = en;
161 | hasScannedForEncodings = 0;
162 | knownRegions = (
163 | en,
164 | Base,
165 | );
166 | mainGroup = BB3959EA24EA71EE00B965F5;
167 | packageReferences = (
168 | BB19EEBD26B3F0E100CF3FE6 /* XCRemoteSwiftPackageReference "SnapKit" */,
169 | );
170 | productRefGroup = BB3959F424EA71EE00B965F5 /* Products */;
171 | projectDirPath = "";
172 | projectRoot = "";
173 | targets = (
174 | BB3959F224EA71EE00B965F5 /* iOS App */,
175 | );
176 | };
177 | /* End PBXProject section */
178 |
179 | /* Begin PBXResourcesBuildPhase section */
180 | BB3959F124EA71EE00B965F5 /* Resources */ = {
181 | isa = PBXResourcesBuildPhase;
182 | buildActionMask = 2147483647;
183 | files = (
184 | BB395A0324EA71F200B965F5 /* LaunchScreen.storyboard in Resources */,
185 | BB395A0024EA71F200B965F5 /* Assets.xcassets in Resources */,
186 | );
187 | runOnlyForDeploymentPostprocessing = 0;
188 | };
189 | /* End PBXResourcesBuildPhase section */
190 |
191 | /* Begin PBXSourcesBuildPhase section */
192 | BB3959EF24EA71EE00B965F5 /* Sources */ = {
193 | isa = PBXSourcesBuildPhase;
194 | buildActionMask = 2147483647;
195 | files = (
196 | BB3959FB24EA71EE00B965F5 /* MainViewController.swift in Sources */,
197 | BB3959F724EA71EE00B965F5 /* AppDelegate.swift in Sources */,
198 | BB4DE09026D69E7600EE0206 /* RadioGroupViewController.swift in Sources */,
199 | BB19EEC126B3F11A00CF3FE6 /* ALHeaderView.swift in Sources */,
200 | BBFF0BE526B3EB7900E7A4D0 /* ALScrollView.swift in Sources */,
201 | );
202 | runOnlyForDeploymentPostprocessing = 0;
203 | };
204 | /* End PBXSourcesBuildPhase section */
205 |
206 | /* Begin PBXVariantGroup section */
207 | BB395A0124EA71F200B965F5 /* LaunchScreen.storyboard */ = {
208 | isa = PBXVariantGroup;
209 | children = (
210 | BB395A0224EA71F200B965F5 /* Base */,
211 | );
212 | name = LaunchScreen.storyboard;
213 | sourceTree = "";
214 | };
215 | /* End PBXVariantGroup section */
216 |
217 | /* Begin XCBuildConfiguration section */
218 | BB395A0524EA71F200B965F5 /* Debug */ = {
219 | isa = XCBuildConfiguration;
220 | buildSettings = {
221 | ALWAYS_SEARCH_USER_PATHS = NO;
222 | CLANG_ANALYZER_NONNULL = YES;
223 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
224 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
225 | CLANG_CXX_LIBRARY = "libc++";
226 | CLANG_ENABLE_MODULES = YES;
227 | CLANG_ENABLE_OBJC_ARC = YES;
228 | CLANG_ENABLE_OBJC_WEAK = YES;
229 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
230 | CLANG_WARN_BOOL_CONVERSION = YES;
231 | CLANG_WARN_COMMA = YES;
232 | CLANG_WARN_CONSTANT_CONVERSION = YES;
233 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
234 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
235 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
236 | CLANG_WARN_EMPTY_BODY = YES;
237 | CLANG_WARN_ENUM_CONVERSION = YES;
238 | CLANG_WARN_INFINITE_RECURSION = YES;
239 | CLANG_WARN_INT_CONVERSION = YES;
240 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
241 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
242 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
243 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
244 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
245 | CLANG_WARN_STRICT_PROTOTYPES = YES;
246 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
247 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
248 | CLANG_WARN_UNREACHABLE_CODE = YES;
249 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
250 | COPY_PHASE_STRIP = NO;
251 | DEBUG_INFORMATION_FORMAT = dwarf;
252 | ENABLE_STRICT_OBJC_MSGSEND = YES;
253 | ENABLE_TESTABILITY = YES;
254 | GCC_C_LANGUAGE_STANDARD = gnu11;
255 | GCC_DYNAMIC_NO_PIC = NO;
256 | GCC_NO_COMMON_BLOCKS = YES;
257 | GCC_OPTIMIZATION_LEVEL = 0;
258 | GCC_PREPROCESSOR_DEFINITIONS = (
259 | "DEBUG=1",
260 | "$(inherited)",
261 | );
262 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
263 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
264 | GCC_WARN_UNDECLARED_SELECTOR = YES;
265 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
266 | GCC_WARN_UNUSED_FUNCTION = YES;
267 | GCC_WARN_UNUSED_VARIABLE = YES;
268 | IPHONEOS_DEPLOYMENT_TARGET = 13.6;
269 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
270 | MTL_FAST_MATH = YES;
271 | ONLY_ACTIVE_ARCH = YES;
272 | SDKROOT = iphoneos;
273 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
274 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
275 | };
276 | name = Debug;
277 | };
278 | BB395A0624EA71F200B965F5 /* Release */ = {
279 | isa = XCBuildConfiguration;
280 | buildSettings = {
281 | ALWAYS_SEARCH_USER_PATHS = NO;
282 | CLANG_ANALYZER_NONNULL = YES;
283 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
284 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
285 | CLANG_CXX_LIBRARY = "libc++";
286 | CLANG_ENABLE_MODULES = YES;
287 | CLANG_ENABLE_OBJC_ARC = YES;
288 | CLANG_ENABLE_OBJC_WEAK = YES;
289 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
290 | CLANG_WARN_BOOL_CONVERSION = YES;
291 | CLANG_WARN_COMMA = YES;
292 | CLANG_WARN_CONSTANT_CONVERSION = YES;
293 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
294 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
295 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
296 | CLANG_WARN_EMPTY_BODY = YES;
297 | CLANG_WARN_ENUM_CONVERSION = YES;
298 | CLANG_WARN_INFINITE_RECURSION = YES;
299 | CLANG_WARN_INT_CONVERSION = YES;
300 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
301 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
302 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
303 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
304 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
305 | CLANG_WARN_STRICT_PROTOTYPES = YES;
306 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
307 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
308 | CLANG_WARN_UNREACHABLE_CODE = YES;
309 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
310 | COPY_PHASE_STRIP = NO;
311 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
312 | ENABLE_NS_ASSERTIONS = NO;
313 | ENABLE_STRICT_OBJC_MSGSEND = YES;
314 | GCC_C_LANGUAGE_STANDARD = gnu11;
315 | GCC_NO_COMMON_BLOCKS = YES;
316 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
317 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
318 | GCC_WARN_UNDECLARED_SELECTOR = YES;
319 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
320 | GCC_WARN_UNUSED_FUNCTION = YES;
321 | GCC_WARN_UNUSED_VARIABLE = YES;
322 | IPHONEOS_DEPLOYMENT_TARGET = 13.6;
323 | MTL_ENABLE_DEBUG_INFO = NO;
324 | MTL_FAST_MATH = YES;
325 | SDKROOT = iphoneos;
326 | SWIFT_COMPILATION_MODE = wholemodule;
327 | SWIFT_OPTIMIZATION_LEVEL = "-O";
328 | VALIDATE_PRODUCT = YES;
329 | };
330 | name = Release;
331 | };
332 | BB395A0824EA71F200B965F5 /* Debug */ = {
333 | isa = XCBuildConfiguration;
334 | buildSettings = {
335 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
336 | CODE_SIGN_STYLE = Automatic;
337 | DEVELOPMENT_TEAM = RW83L2JUQB;
338 | INFOPLIST_FILE = "iOS App/Sources/Info.plist";
339 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
340 | LD_RUNPATH_SEARCH_PATHS = (
341 | "$(inherited)",
342 | "@executable_path/Frameworks",
343 | );
344 | PRODUCT_BUNDLE_IDENTIFIER = alxrguz.alradiobuttons.example;
345 | PRODUCT_NAME = "$(TARGET_NAME)";
346 | SWIFT_VERSION = 5.0;
347 | TARGETED_DEVICE_FAMILY = "1,2";
348 | };
349 | name = Debug;
350 | };
351 | BB395A0924EA71F200B965F5 /* Release */ = {
352 | isa = XCBuildConfiguration;
353 | buildSettings = {
354 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
355 | CODE_SIGN_STYLE = Automatic;
356 | DEVELOPMENT_TEAM = RW83L2JUQB;
357 | INFOPLIST_FILE = "iOS App/Sources/Info.plist";
358 | IPHONEOS_DEPLOYMENT_TARGET = 13.0;
359 | LD_RUNPATH_SEARCH_PATHS = (
360 | "$(inherited)",
361 | "@executable_path/Frameworks",
362 | );
363 | PRODUCT_BUNDLE_IDENTIFIER = alxrguz.alradiobuttons.example;
364 | PRODUCT_NAME = "$(TARGET_NAME)";
365 | SWIFT_VERSION = 5.0;
366 | TARGETED_DEVICE_FAMILY = "1,2";
367 | };
368 | name = Release;
369 | };
370 | /* End XCBuildConfiguration section */
371 |
372 | /* Begin XCConfigurationList section */
373 | BB3959EE24EA71EE00B965F5 /* Build configuration list for PBXProject "ALRadioButtons Example" */ = {
374 | isa = XCConfigurationList;
375 | buildConfigurations = (
376 | BB395A0524EA71F200B965F5 /* Debug */,
377 | BB395A0624EA71F200B965F5 /* Release */,
378 | );
379 | defaultConfigurationIsVisible = 0;
380 | defaultConfigurationName = Release;
381 | };
382 | BB395A0724EA71F200B965F5 /* Build configuration list for PBXNativeTarget "iOS App" */ = {
383 | isa = XCConfigurationList;
384 | buildConfigurations = (
385 | BB395A0824EA71F200B965F5 /* Debug */,
386 | BB395A0924EA71F200B965F5 /* Release */,
387 | );
388 | defaultConfigurationIsVisible = 0;
389 | defaultConfigurationName = Release;
390 | };
391 | /* End XCConfigurationList section */
392 |
393 | /* Begin XCRemoteSwiftPackageReference section */
394 | BB19EEBD26B3F0E100CF3FE6 /* XCRemoteSwiftPackageReference "SnapKit" */ = {
395 | isa = XCRemoteSwiftPackageReference;
396 | repositoryURL = "https://github.com/SnapKit/SnapKit";
397 | requirement = {
398 | kind = upToNextMajorVersion;
399 | minimumVersion = 5.0.1;
400 | };
401 | };
402 | /* End XCRemoteSwiftPackageReference section */
403 |
404 | /* Begin XCSwiftPackageProductDependency section */
405 | BB19EEBE26B3F0E100CF3FE6 /* SnapKit */ = {
406 | isa = XCSwiftPackageProductDependency;
407 | package = BB19EEBD26B3F0E100CF3FE6 /* XCRemoteSwiftPackageReference "SnapKit" */;
408 | productName = SnapKit;
409 | };
410 | BBFF0BDF26B3EA2800E7A4D0 /* ALRadioButtons */ = {
411 | isa = XCSwiftPackageProductDependency;
412 | productName = ALRadioButtons;
413 | };
414 | /* End XCSwiftPackageProductDependency section */
415 | };
416 | rootObject = BB3959EB24EA71EE00B965F5 /* Project object */;
417 | }
418 |
--------------------------------------------------------------------------------