├── .swift-version ├── Assets └── code.png ├── .swiftlint.yml ├── .swiftpm └── xcode │ └── package.xcworkspace │ └── contents.xcworkspacedata ├── Sources └── SlidableImage │ ├── Constants.swift │ ├── Triangle.swift │ ├── Arrows.swift │ └── SlidableImage.swift ├── .github └── workflows │ ├── PublishDocumentation.yml │ ├── swiftlint.yml │ └── ContinuousIntegration.yml ├── Package.swift ├── LICENSE ├── README.md └── .gitignore /.swift-version: -------------------------------------------------------------------------------- 1 | 5.5 2 | 3 | -------------------------------------------------------------------------------- /Assets/code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quver/SlidableImage/HEAD/Assets/code.png -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - trailing_whitespace 3 | line_length: 140 4 | excluded: 5 | - Sources 6 | - SlidableImage 7 | - SlidableImageTests 8 | excluded: 9 | - Pods 10 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Sources/SlidableImage/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // SlidableImage 4 | // 5 | // Created by Pawel Bednorz on 02/02/2025. 6 | // 7 | 8 | import SwiftUI 9 | 10 | enum Constants { 11 | static let arrowSize: CGFloat = 64 12 | } 13 | -------------------------------------------------------------------------------- /.github/workflows/PublishDocumentation.yml: -------------------------------------------------------------------------------- 1 | name: PublishDocumentation 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | deploy_docs: 9 | runs-on: macOS-latest 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Publish Jazzy Docs 13 | uses: steven0351/publish-jazzy-docs@v1.1.2 14 | with: 15 | personal_access_token: ${{ secrets.ACCESS_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/swiftlint.yml: -------------------------------------------------------------------------------- 1 | name: SwiftLint 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - '.github/workflows/SwiftLint.yml' 7 | - '.swiftlint.yml' 8 | - '**/*.swift' 9 | 10 | jobs: 11 | SwiftLint: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v1 15 | - name: GitHub Action for SwiftLint 16 | uses: norio-nomura/action-swiftlint@3.2.1 17 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.10.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "SlidableImage", 7 | platforms: [ 8 | .iOS(.v15), 9 | .macOS(.v12) 10 | ], 11 | products: [ 12 | .library( 13 | name: "SlidableImage", 14 | targets: ["SlidableImage"] 15 | ) 16 | ], 17 | targets: [ 18 | .target(name: "SlidableImage"), 19 | 20 | ] 21 | ) 22 | -------------------------------------------------------------------------------- /.github/workflows/ContinuousIntegration.yml: -------------------------------------------------------------------------------- 1 | name: iOS CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ '*' ] 8 | 9 | jobs: 10 | build: 11 | name: Build and Test default scheme using any available iPhone simulator 12 | runs-on: macos-latest 13 | 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v2 17 | - name: Build 18 | run: | 19 | swift build --verbose 20 | shell: bash -------------------------------------------------------------------------------- /Sources/SlidableImage/Triangle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Triangle.swift 3 | // SlidableImage 4 | // 5 | 6 | import SwiftUI 7 | 8 | struct Triangle: Shape { 9 | func path(in rect: CGRect) -> Path { 10 | Path { path in 11 | path.move(to: CGPoint(x: rect.minX, y: rect.minY)) 12 | path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY)) 13 | path.addLine(to: CGPoint(x: rect.maxX, y: rect.midY)) 14 | path.closeSubpath() 15 | } 16 | } 17 | } 18 | 19 | struct Triangle_Previews: PreviewProvider { 20 | static var previews: some View { 21 | Triangle() 22 | .frame(width: 100, height: 100) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Paweł Bednorz 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SlidableImage 2 | ![iOS CI](https://github.com/quver/SlidableImage/workflows/iOS%20CI/badge.svg) 3 | [![GitHub license](https://img.shields.io/github/license/quver/SlidableImage.svg)]() 4 | [![Swift Package Manager compatible](https://img.shields.io/badge/SPM-compatible-brightgreen.svg)](https://swift.org/package-manager) 5 | 6 | Easy to use library for before & after images. One-line initialization and SwiftUI. 7 | 8 | ## Requirements 9 | 10 | - iOS 15 11 | - Swift 5 12 | 13 | ## Instalation 14 | 15 | This library support 16 | 17 | - Swift Package Manager 18 | 19 | ## Using 20 | 21 | ![Code example](Assets/code.png) 22 | 23 | ### Constructor 24 | 25 | ```swfit 26 | init(@ViewBuilder arrows: @escaping () -> ArrowsIcon, 27 | @ViewBuilder leftView: @escaping () -> LeftView, 28 | @ViewBuilder rightView: @escaping () -> RightView) 29 | ``` 30 | 31 | ### Arrows 32 | 33 | ```swift 34 | init(arrowColor: Color = .white, backgroundColor: Color = .gray) 35 | ``` 36 | 37 | ## Author 38 | 39 | Paweł Bednorz, Quver 40 | 41 | ## License 42 | 43 | SlidableImage Lib and Slider graphic are available under the MIT license. 44 | Check the LICENSE file for more information. 45 | -------------------------------------------------------------------------------- /Sources/SlidableImage/Arrows.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Arrows.swift 3 | // SlidableImage 4 | // 5 | 6 | import SwiftUI 7 | 8 | public struct Arrows: View { 9 | private let arrowColor: Color 10 | private let backgroundColor: Color 11 | 12 | public init(arrowColor: Color = .white, backgroundColor: Color = .gray) { 13 | self.arrowColor = arrowColor 14 | self.backgroundColor = backgroundColor 15 | } 16 | 17 | public var body: some View { 18 | HStack(spacing: 5) { 19 | Triangle() 20 | .fill(arrowColor) 21 | .frame(width: 20, height: Constants.arrowSize / 2) 22 | .rotationEffect(.degrees(180)) 23 | Triangle() 24 | .fill(arrowColor) 25 | .frame(width: 20, height: Constants.arrowSize / 2) 26 | } 27 | .frame(width: Constants.arrowSize, height: Constants.arrowSize) 28 | .background(backgroundColor) 29 | .cornerRadius(Constants.arrowSize / 2) 30 | } 31 | } 32 | 33 | struct ArrowsPreview: PreviewProvider { 34 | static var previews: some View { 35 | VStack { 36 | Arrows() 37 | Arrows(arrowColor: .green, backgroundColor: .orange) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/SlidableImage/SlidableImage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SlidableImage.swift 3 | // SlidableImage 4 | // 5 | 6 | import SwiftUI 7 | 8 | public struct SlidableImage: View { 9 | @State private var location: CGPoint? 10 | private let arrows: () -> ArrowsIcon 11 | private let leftView: () -> LeftView 12 | private let rightView: () -> RightView 13 | 14 | public init(@ViewBuilder arrows: @escaping () -> ArrowsIcon, 15 | @ViewBuilder leftView: @escaping () -> LeftView, 16 | @ViewBuilder rightView: @escaping () -> RightView) { 17 | self.arrows = arrows 18 | self.leftView = leftView 19 | self.rightView = rightView 20 | } 21 | 22 | public var body: some View { 23 | GeometryReader { geometry in 24 | ZStack(alignment: .leading) { 25 | rightView() 26 | leftView() 27 | .mask { 28 | HStack { 29 | Color.black 30 | Spacer(minLength: maskSize(width: geometry.size.width)) 31 | } 32 | } 33 | 34 | arrows() 35 | .frame(width: Constants.arrowSize, height: Constants.arrowSize) 36 | .padding(.leading, location?.x ?? geometry.size.width / 2 - Constants.arrowSize / 2) 37 | .gesture( 38 | DragGesture() 39 | .onChanged { value in 40 | guard value.location.x <= geometry.size.width - Constants.arrowSize else { return } 41 | 42 | location = CGPoint(x: value.location.x, y: geometry.size.height / 2) 43 | } 44 | ) 45 | } 46 | } 47 | } 48 | 49 | private func maskSize(width: CGFloat) -> CGFloat { 50 | guard let location = location?.x else { 51 | return width / 2 52 | } 53 | 54 | return width - location - Constants.arrowSize / 2 55 | } 56 | } 57 | 58 | struct SlidableImagePreview: PreviewProvider { 59 | static var previews: some View { 60 | SlidableImage(arrows: { Arrows() }, 61 | leftView: { Color.red }, 62 | rightView: { Color.green }) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/xcode,macos,objective-c,swift,swiftpackagemanager,swiftpm 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=xcode,macos,objective-c,swift,swiftpackagemanager,swiftpm 3 | 4 | ### macOS ### 5 | # General 6 | .DS_Store 7 | .AppleDouble 8 | .LSOverride 9 | 10 | # Icon must end with two \r 11 | Icon 12 | 13 | 14 | # Thumbnails 15 | ._* 16 | 17 | # Files that might appear in the root of a volume 18 | .DocumentRevisions-V100 19 | .fseventsd 20 | .Spotlight-V100 21 | .TemporaryItems 22 | .Trashes 23 | .VolumeIcon.icns 24 | .com.apple.timemachine.donotpresent 25 | 26 | # Directories potentially created on remote AFP share 27 | .AppleDB 28 | .AppleDesktop 29 | Network Trash Folder 30 | Temporary Items 31 | .apdisk 32 | 33 | ### macOS Patch ### 34 | # iCloud generated files 35 | *.icloud 36 | 37 | ### Objective-C ### 38 | # Xcode 39 | # 40 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 41 | 42 | ## User settings 43 | xcuserdata/ 44 | 45 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 46 | *.xcscmblueprint 47 | *.xccheckout 48 | 49 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 50 | build/ 51 | DerivedData/ 52 | *.moved-aside 53 | *.pbxuser 54 | !default.pbxuser 55 | *.mode1v3 56 | !default.mode1v3 57 | *.mode2v3 58 | !default.mode2v3 59 | *.perspectivev3 60 | !default.perspectivev3 61 | 62 | ## Obj-C/Swift specific 63 | *.hmap 64 | 65 | ## App packaging 66 | *.ipa 67 | *.dSYM.zip 68 | *.dSYM 69 | 70 | # CocoaPods 71 | # We recommend against adding the Pods directory to your .gitignore. However 72 | # you should judge for yourself, the pros and cons are mentioned at: 73 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 74 | # Pods/ 75 | # Add this line if you want to avoid checking in source code from the Xcode workspace 76 | # *.xcworkspace 77 | 78 | # Carthage 79 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 80 | # Carthage/Checkouts 81 | 82 | Carthage/Build/ 83 | 84 | # fastlane 85 | # It is recommended to not store the screenshots in the git repo. 86 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 87 | # For more information about the recommended setup visit: 88 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 89 | 90 | fastlane/report.xml 91 | fastlane/Preview.html 92 | fastlane/screenshots/**/*.png 93 | fastlane/test_output 94 | 95 | # Code Injection 96 | # After new code Injection tools there's a generated folder /iOSInjectionProject 97 | # https://github.com/johnno1962/injectionforxcode 98 | 99 | iOSInjectionProject/ 100 | 101 | ### Objective-C Patch ### 102 | 103 | ### Swift ### 104 | # Xcode 105 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 106 | 107 | 108 | 109 | 110 | 111 | 112 | ## Playgrounds 113 | timeline.xctimeline 114 | playground.xcworkspace 115 | 116 | # Swift Package Manager 117 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 118 | # Packages/ 119 | # Package.pins 120 | # Package.resolved 121 | # *.xcodeproj 122 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 123 | # hence it is not needed unless you have added a package configuration file to your project 124 | # .swiftpm 125 | 126 | .build/ 127 | 128 | # CocoaPods 129 | # We recommend against adding the Pods directory to your .gitignore. However 130 | # you should judge for yourself, the pros and cons are mentioned at: 131 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 132 | # Pods/ 133 | # Add this line if you want to avoid checking in source code from the Xcode workspace 134 | # *.xcworkspace 135 | 136 | # Carthage 137 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 138 | # Carthage/Checkouts 139 | 140 | 141 | # Accio dependency management 142 | Dependencies/ 143 | .accio/ 144 | 145 | # fastlane 146 | # It is recommended to not store the screenshots in the git repo. 147 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 148 | # For more information about the recommended setup visit: 149 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 150 | 151 | 152 | # Code Injection 153 | # After new code Injection tools there's a generated folder /iOSInjectionProject 154 | # https://github.com/johnno1962/injectionforxcode 155 | 156 | 157 | ### SwiftPackageManager ### 158 | Packages 159 | xcuserdata 160 | *.xcodeproj 161 | 162 | 163 | ### SwiftPM ### 164 | 165 | 166 | ### Xcode ### 167 | 168 | ## Xcode 8 and earlier 169 | 170 | ### Xcode Patch ### 171 | *.xcodeproj/* 172 | !*.xcodeproj/project.pbxproj 173 | !*.xcodeproj/xcshareddata/ 174 | !*.xcworkspace/contents.xcworkspacedata 175 | /*.gcno 176 | **/xcshareddata/WorkspaceSettings.xcsettings 177 | 178 | 179 | # Bundler 180 | /.bundle 181 | /vendor/bundle 182 | --------------------------------------------------------------------------------