Skip to content

Commit bba68e6

Browse files
Dariusch Didebanmrylmz
authored andcommitted
Initial commit
0 parents  commit bba68e6

32 files changed

+2153
-0
lines changed

.gitignore

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Xcode
2+
#
3+
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4+
5+
## Build generated
6+
build/
7+
DerivedData/
8+
9+
## Various settings
10+
*.pbxuser
11+
!default.pbxuser
12+
*.mode1v3
13+
!default.mode1v3
14+
*.mode2v3
15+
!default.mode2v3
16+
*.perspectivev3
17+
!default.perspectivev3
18+
xcuserdata/
19+
20+
## Other
21+
*.moved-aside
22+
*.xccheckout
23+
*.xcscmblueprint
24+
25+
## Obj-C/Swift specific
26+
*.hmap
27+
*.ipa
28+
*.dSYM.zip
29+
*.dSYM
30+
31+
## Playgrounds
32+
timeline.xctimeline
33+
playground.xcworkspace
34+
35+
# Swift Package Manager
36+
#
37+
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38+
# Packages/
39+
# Package.pins
40+
# Package.resolved
41+
.build/
42+
43+
# CocoaPods
44+
#
45+
# We recommend against adding the Pods directory to your .gitignore. However
46+
# you should judge for yourself, the pros and cons are mentioned at:
47+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
48+
#
49+
# Pods/
50+
#
51+
# Add this line if you want to avoid checking in source code from the Xcode workspace
52+
# *.xcworkspace
53+
# *.xcodeproj
54+
55+
# Carthage
56+
#
57+
# Add this line if you want to avoid checking in source code from Carthage dependencies.
58+
# Carthage/Checkouts
59+
60+
Carthage/Build
61+
62+
# Accio dependency management
63+
Dependencies/
64+
.accio/
65+
66+
# fastlane
67+
#
68+
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
69+
# screenshots whenever they are needed.
70+
# For more information about the recommended setup visit:
71+
# https://docs.fastlane.tools/best-practices/source-control/#source-control
72+
73+
fastlane/report.xml
74+
fastlane/Preview.html
75+
fastlane/screenshots/**/*.png
76+
fastlane/test_output
77+
78+
# Code Injection
79+
#
80+
# After new code Injection tools there's a generated folder /iOSInjectionProject
81+
# https://github.com/johnno1962/injectionforxcode
82+
83+
iOSInjectionProject/
84+
85+
# Xcode Swift PM
86+
.swiftpm

JamitFoundation.xcodeproj/project.pbxproj

Lines changed: 508 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>IDEDidComputeMac32BitWarning</key>
6+
<true/>
7+
</dict>
8+
</plist>
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Scheme
3+
LastUpgradeVersion = "1100"
4+
version = "1.3">
5+
<BuildAction
6+
parallelizeBuildables = "YES"
7+
buildImplicitDependencies = "YES">
8+
<BuildActionEntries>
9+
<BuildActionEntry
10+
buildForTesting = "YES"
11+
buildForRunning = "YES"
12+
buildForProfiling = "YES"
13+
buildForArchiving = "YES"
14+
buildForAnalyzing = "YES">
15+
<BuildableReference
16+
BuildableIdentifier = "primary"
17+
BlueprintIdentifier = "C5CB31BF2371D0BE00E25E4B"
18+
BuildableName = "JamitFoundation.framework"
19+
BlueprintName = "JamitFoundation"
20+
ReferencedContainer = "container:JamitFoundation.xcodeproj">
21+
</BuildableReference>
22+
</BuildActionEntry>
23+
</BuildActionEntries>
24+
</BuildAction>
25+
<TestAction
26+
buildConfiguration = "Debug"
27+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
28+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29+
shouldUseLaunchSchemeArgsEnv = "YES">
30+
<Testables>
31+
</Testables>
32+
</TestAction>
33+
<LaunchAction
34+
buildConfiguration = "Debug"
35+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
36+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
37+
launchStyle = "0"
38+
useCustomWorkingDirectory = "NO"
39+
ignoresPersistentStateOnLaunch = "NO"
40+
debugDocumentVersioning = "YES"
41+
debugServiceExtension = "internal"
42+
allowLocationSimulation = "YES">
43+
</LaunchAction>
44+
<ProfileAction
45+
buildConfiguration = "Release"
46+
shouldUseLaunchSchemeArgsEnv = "YES"
47+
savedToolIdentifier = ""
48+
useCustomWorkingDirectory = "NO"
49+
debugDocumentVersioning = "YES">
50+
<MacroExpansion>
51+
<BuildableReference
52+
BuildableIdentifier = "primary"
53+
BlueprintIdentifier = "C5CB31BF2371D0BE00E25E4B"
54+
BuildableName = "JamitFoundation.framework"
55+
BlueprintName = "JamitFoundation"
56+
ReferencedContainer = "container:JamitFoundation.xcodeproj">
57+
</BuildableReference>
58+
</MacroExpansion>
59+
</ProfileAction>
60+
<AnalyzeAction
61+
buildConfiguration = "Debug">
62+
</AnalyzeAction>
63+
<ArchiveAction
64+
buildConfiguration = "Release"
65+
revealArchiveInOrganizer = "YES">
66+
</ArchiveAction>
67+
</Scheme>

LICENSE

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//
2+
// MIT License
3+
//
4+
// Copyright (c) 2019 Jamit Labs GmbH
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in all
14+
// copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
// SOFTWARE.
23+
//

Modules/PageView/PageView.swift

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import Foundation
2+
import JamitFoundation
3+
import UIKit
4+
5+
/// A stateful view which adds paged scroll behaviour to an embedded `ContentView`.
6+
///
7+
/// Example:
8+
/// ```swift
9+
/// // Represents an image view with paged scroll behaviour.
10+
/// let imageSourceURLs: [URL] = <#image sources#>
11+
/// let contentView = PageView<ImageView>.instantiate()
12+
///
13+
/// contentView.model = .init(pages: imageSourceURLs.map { .url($0) })
14+
/// ```
15+
public final class PageView<ContentView: StatefulViewProtocol>: StatefulView<PageViewModel<ContentView.Model>>, UIScrollViewDelegate {
16+
private lazy var scrollView: UIScrollView = .instantiate()
17+
private lazy var contentViews: [ContentView] = []
18+
private var contentViewIndex: Int = 0
19+
20+
public override func viewDidLoad() {
21+
super.viewDidLoad()
22+
23+
scrollView.delegate = self
24+
scrollView.isPagingEnabled = true
25+
scrollView.showsHorizontalScrollIndicator = false
26+
scrollView.showsVerticalScrollIndicator = false
27+
scrollView.translatesAutoresizingMaskIntoConstraints = false
28+
addSubview(scrollView)
29+
scrollView.topAnchor.constraint(equalTo: topAnchor).isActive = true
30+
scrollView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
31+
scrollView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
32+
scrollView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
33+
}
34+
35+
public override func didChangeModel() {
36+
super.didChangeModel()
37+
38+
switch model.axis {
39+
case .horizontal:
40+
scrollView.alwaysBounceHorizontal = true
41+
scrollView.alwaysBounceVertical = false
42+
43+
case .vertical:
44+
scrollView.alwaysBounceHorizontal = false
45+
scrollView.alwaysBounceVertical = true
46+
}
47+
48+
if model.pages.count < contentViews.count {
49+
let removedIndices = model.pages.count ..< contentViews.count
50+
contentViews[removedIndices].forEach { $0.removeFromSuperview() }
51+
contentViews.removeSubrange(removedIndices)
52+
} else if contentViews.count < model.pages.count {
53+
let addedIndices = contentViews.count ..< model.pages.count
54+
addedIndices.forEach { _ in
55+
let view: ContentView = .instantiate()
56+
contentViews.append(view)
57+
scrollView.addSubview(view)
58+
}
59+
}
60+
61+
contentViews.enumerated().forEach { index, view in
62+
view.model = model.pages[index]
63+
}
64+
65+
updateContentLayout()
66+
}
67+
68+
private func updateContentLayout() {
69+
let totalContentDimension = CGFloat(contentViews.count) * min(bounds.width, bounds.height)
70+
scrollView.contentSize = CGSize(
71+
width: model.axis == .horizontal ? totalContentDimension : bounds.width,
72+
height: model.axis == .vertical ? totalContentDimension : bounds.height
73+
)
74+
75+
let initialFrame = bounds
76+
contentViews.enumerated().forEach { index, view in
77+
view.frame = initialFrame.offsetBy(
78+
dx: model.axis == .horizontal ? CGFloat(index) * initialFrame.width : 0,
79+
dy: model.axis == .vertical ? CGFloat(index) * initialFrame.height : 0
80+
)
81+
}
82+
}
83+
84+
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
85+
let newContentViewIndex: Int
86+
switch model.axis {
87+
case .horizontal:
88+
newContentViewIndex = bounds.width > 0 ? Int(round(scrollView.contentOffset.x / bounds.width)) : 0
89+
90+
case .vertical:
91+
newContentViewIndex = bounds.height > 0 ? Int(round(scrollView.contentOffset.y / bounds.height)) : 0
92+
}
93+
94+
if contentViewIndex != newContentViewIndex {
95+
contentViewIndex = newContentViewIndex
96+
model.onPageIndexChanged(contentViewIndex)
97+
}
98+
}
99+
}

Modules/PageView/PageViewModel.swift

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import Foundation
2+
import JamitFoundation
3+
import UIKit
4+
5+
/// The state view model for `PageView`.
6+
public struct PageViewModel<Content: ViewModelProtocol>: ViewModelProtocol {
7+
/// A closure type definition for the page index change callback.
8+
public typealias PageIndexChangeCallback = (Int) -> Void
9+
10+
/// The scroll axis definition for the `PageView` paging behaviour.
11+
public enum Axis {
12+
/// Describes a horizontal paging behaviour for the `PageView`.
13+
case horizontal
14+
15+
/// Describes a vertical paging behaviour for the `PageView`.
16+
case vertical
17+
}
18+
19+
/// The scroll axis for the paging behaviour.
20+
public let axis: Axis
21+
22+
/// The content view models for the paginated content views.
23+
public let pages: [Content]
24+
25+
/// The callback for page index changes.
26+
public let onPageIndexChanged: PageIndexChangeCallback
27+
28+
/// The default initializer of `PageViewModel`.
29+
///
30+
/// - Parameter axis: The scroll axis for the paging behaviour.
31+
/// - Parameter pages: The paginated content view models.
32+
/// - Parameter onPageIndexChanged: The content view models for the paginated content views.
33+
public init(
34+
axis: Axis = PageViewModel.default.axis,
35+
pages: [Content] = PageViewModel.default.pages,
36+
onPageIndexChanged: @escaping PageIndexChangeCallback = PageViewModel.default.onPageIndexChanged
37+
) {
38+
self.axis = axis
39+
self.pages = pages
40+
self.onPageIndexChanged = onPageIndexChanged
41+
}
42+
}
43+
44+
extension PageViewModel {
45+
/// The default state of `PageViewModel`.
46+
public static var `default`: PageViewModel<Content> {
47+
return .init(
48+
axis: .horizontal,
49+
pages: [],
50+
onPageIndexChanged: { _ in }
51+
)
52+
}
53+
}

Package.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// swift-tools-version:5.0
2+
3+
import PackageDescription
4+
5+
let package = Package(
6+
name: "JamitFoundation",
7+
platforms: [.iOS(.v10)],
8+
products: [
9+
.library(name: "JamitFoundation", targets: ["JamitFoundation"]),
10+
.library(name: "PageView", targets: ["PageView"])
11+
],
12+
dependencies: [],
13+
targets: [
14+
.target(
15+
name: "JamitFoundation",
16+
dependencies: [],
17+
path: "Sources"
18+
),
19+
.target(
20+
name: "PageView",
21+
dependencies: ["JamitFoundation"],
22+
path: "Modules/PageView"
23+
)
24+
]
25+
)
26+

0 commit comments

Comments
 (0)