Merge pull request #23 from xmartlabs/reviewMartin

Code Review + update public API + many improvements.
This commit is contained in:
Martin Barreto 2021-07-28 12:09:07 -03:00 committed by GitHub
commit 97a429ac9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 291 additions and 277 deletions

View File

@ -753,7 +753,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.xmartlabs.Example;
PRODUCT_BUNDLE_IDENTIFIER = com.xmartlabs.PagerTabStrip;
PRODUCT_NAME = Example;
SDKROOT = iphoneos;
SWIFT_VERSION = 5.0;
@ -775,7 +775,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.xmartlabs.Example;
PRODUCT_BUNDLE_IDENTIFIER = com.xmartlabs.PagerTabStrip;
PRODUCT_NAME = Example;
SDKROOT = iphoneos;
SWIFT_VERSION = 5.0;

View File

@ -2,7 +2,7 @@
// ImageCache.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 14/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
// Adapted by Xmartlabs from https://github.com/SchwiftyUI/NewsApiApp/blob/master/NewsApp/Model/UrlImageModel.swift
//

View File

@ -2,7 +2,7 @@
// URLImageModel.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 14/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
// Adapted by Xmartlabs from https://github.com/SchwiftyUI/NewsApiApp/blob/master/NewsApp/Model/UrlImageModel.swift
//

View File

@ -2,7 +2,7 @@
// URLImageView.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 14/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
// Adapted by Xmartlabs from https://github.com/SchwiftyUI/NewsApiApp/blob/master/NewsApp/Model/UrlImageModel.swift
//

View File

@ -2,12 +2,10 @@
// Post.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 6/22/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import Foundation
import SwiftUI
import CoreLocation
struct Post: Hashable, Codable, Identifiable {
var id: Int

View File

@ -2,11 +2,10 @@
// ModelData.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 6/22/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import Foundation
import Combine
struct PostsFactory {
static let shared = PostsFactory()

View File

@ -2,13 +2,10 @@
// User.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 6/22/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import Foundation
import SwiftUI
import CoreLocation
import Combine
struct User: Hashable, Codable, Identifiable {
var id: Int

View File

@ -2,7 +2,7 @@
// InstagramNav.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 6/22/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import SwiftUI

View File

@ -2,20 +2,23 @@
// InstagramView.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 7/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import SwiftUI
import PagerTabStrip
struct InstagramView: View {
@State var selection = 1
@ObservedObject var galleryModel = GalleryModel()
@ObservedObject var listModel = ListModel()
@ObservedObject var likedModel = LikedModel()
@ObservedObject var savedModel = SavedModel()
var body: some View {
XLPagerView(selection: 0, pagerSettings: PagerSettings(tabItemSpacing: 0, tabItemHeight: 50, indicatorBarColor: .black)) {
PagerTabStripView(selection: $selection) {
PostsList(isLoading: $galleryModel.isLoading, items: galleryModel.posts).pagerTabItem {
galleryModel.navBarItem
}.onPageAppear {
@ -52,6 +55,7 @@ struct InstagramView: View {
}
}
}
.pagerTabStripViewStyle(PagerTabViewStyle(tabItemSpacing: 0, tabItemHeight: 50, indicatorBarColor: .black))
.frame(alignment: .center)
}
}

View File

@ -2,7 +2,7 @@
// GalleryModel.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 24/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import Foundation

View File

@ -2,7 +2,7 @@
// LikedModel.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 24/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import Foundation

View File

@ -2,7 +2,7 @@
// ListModel.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 24/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import Foundation

View File

@ -2,7 +2,7 @@
// SavedModel.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 24/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import Foundation

View File

@ -2,7 +2,7 @@
// PostDetail.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 7/5/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import SwiftUI

View File

@ -2,7 +2,7 @@
// PostRow.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 6/22/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import SwiftUI

View File

@ -2,7 +2,7 @@
// PostsList.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 6/22/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import Foundation
@ -24,6 +24,7 @@ struct PostsList: View {
VStack {
if isLoading {
ProgressView()
.padding(EdgeInsets(top: 20, leading: 0, bottom: 0, trailing: 0))
}
List {
ForEach(items) { item in
@ -31,6 +32,7 @@ struct PostsList: View {
}
}
}
.animation(.default, value: isLoading)
}
}

View File

@ -2,7 +2,7 @@
// LikesModel.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 24/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import Foundation

View File

@ -2,7 +2,7 @@
// MediaModel.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 24/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import Foundation

View File

@ -2,7 +2,7 @@
// TweetsModel.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 24/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import Foundation

View File

@ -2,7 +2,7 @@
// TwitterNav.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 6/22/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import SwiftUI

View File

@ -2,19 +2,21 @@
// TwitterView.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 7/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import SwiftUI
import PagerTabStrip
struct TwitterView: View {
@State var selection = 2
@ObservedObject var tweetsModel = TweetsModel()
@ObservedObject var mediaModel = MediaModel()
@ObservedObject var likesModel = LikesModel()
var body: some View {
XLPagerView(selection: 0, pagerSettings: PagerSettings(tabItemSpacing: 0, tabItemHeight: 50, indicatorBarColor: .blue)) {
PagerTabStripView(selection: $selection) {
PostsList(isLoading: $tweetsModel.isLoading, items: tweetsModel.posts).pagerTabItem {
tweetsModel.navBarItem
}.onPageAppear {
@ -43,6 +45,7 @@ struct TwitterView: View {
}
}
.frame(alignment: .center)
.pagerTabStripViewStyle(PagerTabViewStyle(tabItemSpacing: 0, tabItemHeight: 50, indicatorBarColor: .blue))
}
}

View File

@ -2,7 +2,7 @@
// AccountModel.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 23/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import Foundation

View File

@ -2,7 +2,7 @@
// HomeModel.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 23/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import Foundation

View File

@ -2,7 +2,7 @@
// TrendingModel.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 23/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import Foundation

View File

@ -2,7 +2,7 @@
// YoutubeNavWithTitle.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 6/22/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import Combine

View File

@ -2,19 +2,20 @@
// YoutubeView.swift
// Example (iOS)
//
// Created by Milena Zabaleta on 7/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import SwiftUI
import PagerTabStrip
struct YoutubeView: View {
@ObservedObject var homeModel = HomeModel()
@ObservedObject var trendingModel = TrendingModel()
@ObservedObject var accountModel = AccountModel()
var body: some View {
XLPagerView(selection: 0, pagerSettings: PagerSettings(tabItemSpacing: 0, tabItemHeight: 70, indicatorBarHeight: 7, indicatorBarColor: selectedColor)) {
PagerTabStripView() {
PostsList(isLoading: $homeModel.isLoading, items: homeModel.posts).pagerTabItem {
homeModel.navBarItem
}.onPageAppear {
@ -26,7 +27,8 @@ struct YoutubeView: View {
PostsList(isLoading: $trendingModel.isLoading, items: trendingModel.posts, withDescription: false).pagerTabItem {
trendingModel.navBarItem
}.onPageAppear {
}
.onPageAppear {
trendingModel.isLoading = true
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
trendingModel.isLoading = false
@ -37,6 +39,7 @@ struct YoutubeView: View {
accountModel.navBarItem
}
}
.pagerTabStripViewStyle(PagerTabViewStyle(tabItemSpacing: 0, tabItemHeight: 80, indicatorBarHeight: 7, indicatorBarColor: selectedColor))
.frame(alignment: .center)
}
}

View File

@ -2,7 +2,7 @@
// Tests_iOS.swift
// Tests iOS
//
// Created by Martin Barreto on 7/17/20.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import XCTest

View File

@ -2,7 +2,7 @@
// Tests_macOS.swift
// Tests macOS
//
// Created by Martin Barreto on 7/17/20.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import XCTest

View File

@ -9,6 +9,8 @@
</dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>PagerTabStrip</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>

View File

@ -10,7 +10,6 @@
287D0A6E1C4B73BD004566D6 /* PagerTabStripTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287D0A6D1C4B73BD004566D6 /* PagerTabStripTests.swift */; };
28F828811C494B2C00330CF4 /* PagerTabStrip.h in Headers */ = {isa = PBXBuildFile; fileRef = 28F828801C494B2C00330CF4 /* PagerTabStrip.h */; settings = {ATTRIBUTES = (Public, ); }; };
28F828881C494B2C00330CF4 /* PagerTabStrip.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28F8287D1C494B2C00330CF4 /* PagerTabStrip.framework */; };
28F8289A1C494B4200330CF4 /* iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28F828991C494B4200330CF4 /* iOS.swift */; };
C7A02C01269377D400BFE31B /* DataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7A02C00269377D400BFE31B /* DataStore.swift */; };
E07FB07A26AF36B400CDCFD8 /* PagerSetAppearModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = E07FB07926AF36B400CDCFD8 /* PagerSetAppearModifier.swift */; };
E07FB07C26AF373600CDCFD8 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E07FB07B26AF373600CDCFD8 /* Extensions.swift */; };
@ -18,8 +17,8 @@
E07FB08026AF39C300CDCFD8 /* NavBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E07FB07F26AF39C300CDCFD8 /* NavBarItem.swift */; };
E07FB08226AF3B7200CDCFD8 /* PagerTabViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E07FB08126AF3B7200CDCFD8 /* PagerTabViewDelegate.swift */; };
E07FB08526AF3CAA00CDCFD8 /* NavBarModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = E07FB08426AF3CAA00CDCFD8 /* NavBarModifier.swift */; };
E1FA589924C8BC1B0081FBE7 /* XLPagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FA589824C8BC1B0081FBE7 /* XLPagerView.swift */; };
E1FA589A24C8BC1B0081FBE7 /* XLPagerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FA589824C8BC1B0081FBE7 /* XLPagerView.swift */; };
E1FA589924C8BC1B0081FBE7 /* PagerTabStripView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FA589824C8BC1B0081FBE7 /* PagerTabStripView.swift */; };
E1FA589A24C8BC1B0081FBE7 /* PagerTabStripView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FA589824C8BC1B0081FBE7 /* PagerTabStripView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -39,7 +38,6 @@
28F828821C494B2C00330CF4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
28F828871C494B2C00330CF4 /* PagerTabStripTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PagerTabStripTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
28F8288E1C494B2C00330CF4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
28F828991C494B4200330CF4 /* iOS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = iOS.swift; sourceTree = "<group>"; };
C7A02C00269377D400BFE31B /* DataStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStore.swift; sourceTree = "<group>"; };
E07FB07926AF36B400CDCFD8 /* PagerSetAppearModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PagerSetAppearModifier.swift; sourceTree = "<group>"; };
E07FB07B26AF373600CDCFD8 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
@ -47,7 +45,7 @@
E07FB07F26AF39C300CDCFD8 /* NavBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavBarItem.swift; sourceTree = "<group>"; };
E07FB08126AF3B7200CDCFD8 /* PagerTabViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PagerTabViewDelegate.swift; sourceTree = "<group>"; };
E07FB08426AF3CAA00CDCFD8 /* NavBarModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavBarModifier.swift; sourceTree = "<group>"; };
E1FA589824C8BC1B0081FBE7 /* XLPagerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XLPagerView.swift; sourceTree = "<group>"; };
E1FA589824C8BC1B0081FBE7 /* PagerTabStripView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PagerTabStripView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -101,8 +99,7 @@
isa = PBXGroup;
children = (
E07FB08326AF3C6000CDCFD8 /* Modifiers */,
28F828991C494B4200330CF4 /* iOS.swift */,
E1FA589824C8BC1B0081FBE7 /* XLPagerView.swift */,
E1FA589824C8BC1B0081FBE7 /* PagerTabStripView.swift */,
C7A02C00269377D400BFE31B /* DataStore.swift */,
E07FB07B26AF373600CDCFD8 /* Extensions.swift */,
E07FB07F26AF39C300CDCFD8 /* NavBarItem.swift */,
@ -244,11 +241,10 @@
E07FB07C26AF373600CDCFD8 /* Extensions.swift in Sources */,
E07FB08226AF3B7200CDCFD8 /* PagerTabViewDelegate.swift in Sources */,
E07FB07E26AF377D00CDCFD8 /* PagerTabItemModifier.swift in Sources */,
E1FA589924C8BC1B0081FBE7 /* XLPagerView.swift in Sources */,
E1FA589924C8BC1B0081FBE7 /* PagerTabStripView.swift in Sources */,
C7A02C01269377D400BFE31B /* DataStore.swift in Sources */,
E07FB08526AF3CAA00CDCFD8 /* NavBarModifier.swift in Sources */,
E07FB07A26AF36B400CDCFD8 /* PagerSetAppearModifier.swift in Sources */,
28F8289A1C494B4200330CF4 /* iOS.swift in Sources */,
E07FB08026AF39C300CDCFD8 /* NavBarItem.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -257,7 +253,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E1FA589A24C8BC1B0081FBE7 /* XLPagerView.swift in Sources */,
E1FA589A24C8BC1B0081FBE7 /* PagerTabStripView.swift in Sources */,
287D0A6E1C4B73BD004566D6 /* PagerTabStripTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -1,21 +1,23 @@
//
// NavBarModifier.swift
// Extensions.swift
// PagerTabStrip
//
// Created by Cecilia Pirotto on 26/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import SwiftUI
///
/// Public Source Code
///
extension View {
public func pagerTabItem<V>(@ViewBuilder _ pagerTabView: @escaping () -> V) -> some View where V: View {
return self.modifier(PagerTabItem(navTabView: pagerTabView))
return self.modifier(PagerTabItemModifier(navTabView: pagerTabView))
}
public func onPageAppear(perform action: (() -> Void)?) -> some View {
return self.modifier(PagerSetAppearItem(onPageAppear: action ?? {}))
return self.modifier(PagerSetAppearItemModifier(onPageAppear: action ?? {}))
}
public func pagerTabStripViewStyle(_ style: PagerTabViewStyle) -> some View {
return self.environment(\.pagerTabViewStyle, style)
}
}

View File

@ -2,54 +2,52 @@
// NavBarModifier.swift
// PagerTabStrip
//
// Created by Cecilia Pirotto on 26/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import SwiftUI
internal struct NavBarModifier: ViewModifier {
@EnvironmentObject var pagerSettings: PagerSettings
@Binding private var indexSelected: Int
struct NavBarModifier: ViewModifier {
@Binding private var selection: Int
@Binding private var itemCount: Int
private var navBarItemWidth: CGFloat {
let totalItemWidth = (pagerSettings.width - (pagerSettings.tabItemSpacing * CGFloat(itemCount - 1)))
let totalItemWidth = (settings.width - (style.tabItemSpacing * CGFloat(itemCount - 1)))
return totalItemWidth / CGFloat(itemCount)
}
public init(itemCount: Binding<Int>, selection: Binding<Int>) {
self._indexSelected = selection
self._selection = selection
self._itemCount = itemCount
}
func body(content: Content) -> some View {
VStack(alignment: .leading, spacing: 0) {
HStack(spacing: pagerSettings.tabItemSpacing) {
if itemCount > 0 && pagerSettings.width > 0 {
HStack(spacing: style.tabItemSpacing) {
if itemCount > 0 && settings.width > 0 {
ForEach(0...itemCount-1, id: \.self) { idx in
NavBarItem(id: idx, selection: $indexSelected)
.frame(height: pagerSettings.tabItemHeight)
NavBarItem(id: idx, selection: $selection)
.frame(height: style.tabItemHeight)
}
}
}
.frame(height: pagerSettings.tabItemHeight)
.frame(height: style.tabItemHeight)
HStack {
if let width = navBarItemWidth, width > 0, width <= pagerSettings.width {
let x = -self.pagerSettings.contentOffset / CGFloat(itemCount) + width / 2
if let width = navBarItemWidth, width > 0, width <= settings.width {
let x = -settings.contentOffset / CGFloat(itemCount) + width / 2
Rectangle()
.fill(pagerSettings.indicatorBarColor)
.fill(style.indicatorBarColor)
.animation(.default)
.frame(width: width)
.position(x: x, y: 0)
}
}
.frame(height: pagerSettings.indicatorBarHeight)
.frame(height: style.indicatorBarHeight)
content
}
}
}
internal extension GeometryReader {
func navBar(itemCount: Binding<Int>, selection: Binding<Int>) -> some View {
return self.modifier(NavBarModifier(itemCount: itemCount, selection: selection))
}
@Environment(\.pagerTabViewStyle) var style: PagerTabViewStyle
@EnvironmentObject private var settings: PagerSettings
}

View File

@ -2,17 +2,15 @@
// PagerSetAppearModifier.swift
// PagerTabStrip
//
// Created by Cecilia Pirotto on 26/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import SwiftUI
internal struct PagerSetAppearItem: ViewModifier {
@EnvironmentObject var navContentViews : DataStore
@EnvironmentObject var pagerSettings: PagerSettings
var onPageAppear: () -> Void
@State var index = -1
struct PagerSetAppearItemModifier: ViewModifier {
private var onPageAppear: () -> Void
init(onPageAppear: @escaping () -> Void) {
self.onPageAppear = onPageAppear
}
@ -24,12 +22,17 @@ internal struct PagerSetAppearItem: ViewModifier {
Color.clear
.onAppear {
DispatchQueue.main.async {
let frame = reader.frame(in: .named("XLPagerViewScrollView"))
index = Int(round((frame.minX - pagerSettings.contentOffset) / pagerSettings.width))
navContentViews.setAppear(callback: onPageAppear, at: index)
let frame = reader.frame(in: .named("PagerViewScrollView"))
index = Int(round((frame.minX - settings.contentOffset) / settings.width))
dataStore.setAppear(callback: onPageAppear, at: index)
}
}
}
)
}
@EnvironmentObject private var dataStore: DataStore
@EnvironmentObject private var settings: PagerSettings
@Environment(\.pagerTabViewStyle) var style: PagerTabViewStyle
@State private var index = -1
}

View File

@ -2,20 +2,17 @@
// PagerTabItemModifier.swift
// PagerTabStrip
//
// Created by Cecilia Pirotto on 26/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import SwiftUI
internal struct PagerTabItem<NavTabView: View> : ViewModifier {
@EnvironmentObject var navContentViews : DataStore
@EnvironmentObject var pagerSettings: PagerSettings
var navTabView: () -> NavTabView
@State var index = -1
struct PagerTabItemModifier<NavTabView: View> : ViewModifier {
private var navTabView: () -> NavTabView
init(navTabView: @escaping () -> NavTabView) {
self.navTabView = navTabView
self.index = index
}
func body(content: Content) -> some View {
@ -25,19 +22,24 @@ internal struct PagerTabItem<NavTabView: View> : ViewModifier {
Color.clear
.onAppear {
DispatchQueue.main.async {
let frame = reader.frame(in: .named("XLPagerViewScrollView"))
index = Int(round((frame.minX - pagerSettings.contentOffset) / pagerSettings.width))
let frame = reader.frame(in: .named("PagerViewScrollView"))
index = Int(round((frame.minX - settings.contentOffset) / settings.width))
let tabView = navTabView()
let tabViewDelegate = navTabView() as? PagerTabViewDelegate
navContentViews.setView(AnyView(tabView),
let tabViewDelegate = tabView as? PagerTabViewDelegate
dataStore.setView(AnyView(tabView),
tabViewDelegate: tabViewDelegate,
at: index)
}
}.onDisappear {
navContentViews.items.value[index]?.tabViewDelegate?.setState(state: .normal)
navContentViews.remove(at: index)
dataStore.items.value[index]?.tabViewDelegate?.setState(state: .normal)
dataStore.remove(at: index)
}
}
)
}
@EnvironmentObject private var dataStore: DataStore
@EnvironmentObject private var settings: PagerSettings
@Environment(\.pagerTabViewStyle) var style: PagerTabViewStyle
@State private var index = -1
}

View File

@ -2,13 +2,13 @@
// NavBarItem.swift
// PagerTabStrip
//
// Created by Cecilia Pirotto on 26/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import SwiftUI
internal struct NavBarItem: View {
@EnvironmentObject var navContentViews: DataStore
struct NavBarItem: View {
@EnvironmentObject private var dataStore: DataStore
@Binding private var currentIndex: Int
private var id: Int
@ -18,14 +18,14 @@ internal struct NavBarItem: View {
}
var body: some View {
if id < navContentViews.items.value.keys.count {
if id < dataStore.items.value.keys.count {
Button(action: {
self.currentIndex = id
}, label: {
navContentViews.items.value[id]?.view
dataStore.items.value[id]?.view
}).buttonStyle(PlainButtonStyle())
.onLongPressGesture(minimumDuration: 0, maximumDistance: .infinity) { pressing in
navContentViews.items.value[id]?.tabViewDelegate?.setState(state: pressing ? .highlighted : .selected)
dataStore.items.value[id]?.tabViewDelegate?.setState(state: pressing ? .highlighted : .selected)
} perform: {}
}
}

View File

@ -0,0 +1,174 @@
//
// PagerView.swift
// PagerTabStrip
//
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import SwiftUI
public struct PagerTabViewStyle {
var tabItemSpacing: CGFloat
var tabItemHeight: CGFloat
var indicatorBarHeight: CGFloat
var indicatorBarColor: Color
public init(tabItemSpacing: CGFloat = 0, tabItemHeight: CGFloat = 100, indicatorBarHeight: CGFloat = 2, indicatorBarColor: Color = .blue){
self.tabItemSpacing = tabItemSpacing
self.tabItemHeight = tabItemHeight
self.indicatorBarHeight = indicatorBarHeight
self.indicatorBarColor = indicatorBarColor
}
}
private struct PagerTabViewStyleKey: EnvironmentKey {
static let defaultValue = PagerTabViewStyle()
}
extension EnvironmentValues {
var pagerTabViewStyle: PagerTabViewStyle {
get { self[PagerTabViewStyleKey.self] }
set { self[PagerTabViewStyleKey.self] = newValue }
}
}
class PagerSettings: ObservableObject {
@Published var width: CGFloat = 0
@Published var contentOffset: CGFloat = 0
}
@available(iOS 14.0, *)
public struct PagerTabStripView<Content> : View where Content: View {
private var content: () -> Content
@Binding private var selectionBiding: Int
@State private var selectionState = 0
@StateObject private var settings: PagerSettings
private var useBinding: Bool
public init(selection: Binding<Int>? = nil,
@ViewBuilder content: @escaping () -> Content) {
self.content = content
if let selection = selection {
useBinding = true
self._selectionBiding = selection
}
else{
useBinding = false
self._selectionBiding = .constant(0)
}
self._settings = StateObject(wrappedValue: PagerSettings())
}
public var body: some View {
WrapperPagerTabStripView(selection: useBinding ? $selectionBiding : $selectionState, content: content)
.environmentObject(self.settings)
}
}
private struct WrapperPagerTabStripView<Content> : View where Content: View {
private var content: () -> Content
@StateObject private var dataStore = DataStore()
@Environment(\.pagerTabViewStyle) var style: PagerTabViewStyle
@EnvironmentObject private var settings: PagerSettings
@Binding var selection: Int
@State private var currentOffset: CGFloat = 0 {
didSet {
self.settings.contentOffset = currentOffset
}
}
@State private var itemCount : Int = 0
@GestureState private var translation: CGFloat = 0
public init(selection: Binding<Int>,
@ViewBuilder content: @escaping () -> Content) {
self.content = content
self._selection = selection
}
public var body: some View {
GeometryReader { gproxy in
HStack(spacing: 0) {
content()
.frame(width: gproxy.size.width)
}
.offset(x: -CGFloat(self.selection) * settings.width)
.offset(x: self.translation)
.animation(.interactiveSpring(response: 0.5, dampingFraction: 1.00, blendDuration: 0.25), value: selection)
.animation(.interactiveSpring(response: 0.5, dampingFraction: 1.00, blendDuration: 0.25), value: translation)
.gesture(
DragGesture().updating(self.$translation) { value, state, _ in
if (selection == 0 && value.translation.width > 0) {
let valueWidth = value.translation.width
let normTrans = valueWidth / (gproxy.size.width + 50)
let logValue = log(1 + normTrans)
state = gproxy.size.width/1.5 * logValue
} else if (selection == itemCount - 1 && value.translation.width < 0) {
let valueWidth = -value.translation.width
let normTrans = valueWidth / (gproxy.size.width + 50)
let logValue = log(1 + normTrans)
state = -gproxy.size.width / 1.5 * logValue
} else {
state = value.translation.width
}
}.onEnded { value in
let offset = value.predictedEndTranslation.width / gproxy.size.width
let newPredictedIndex = (CGFloat(self.selection) - offset).rounded()
let newIndex = min(max(Int(newPredictedIndex), 0), self.itemCount - 1)
if abs(self.selection - newIndex) > 1 {
self.selection = newIndex > self.selection ? self.selection + 1 : self.selection - 1
} else {
self.selection = newIndex
}
if translation > 0 {
self.currentOffset = translation
}
}
)
.onChange(of: gproxy.frame(in: .local), perform: { geo in
self.settings.width = geo.width
})
.onChange(of: self.selection) { [selection] newIndex in
self.currentOffset = self.offsetFor(index: newIndex)
if let callback = dataStore.items.value[newIndex]?.appearCallback {
callback()
}
if let tabViewDelegate = dataStore.items.value[selection]?.tabViewDelegate {
tabViewDelegate.setState(state: .normal)
}
if let tabViewDelegate = dataStore.items.value[newIndex]?.tabViewDelegate, newIndex != selection {
tabViewDelegate.setState(state: .selected)
}
}
.onChange(of: translation) { _ in
self.settings.contentOffset = translation - CGFloat(selection)*settings.width
}
.onChange(of: settings.width) { _ in
self.currentOffset = self.offsetFor(index: self.selection)
}
.onChange(of: itemCount) { _ in
self.selection = selection >= itemCount ? itemCount - 1 : selection
}
.onAppear {
settings.width = gproxy.size.width
}
}
.modifier(NavBarModifier(itemCount: $itemCount, selection: $selection))
.environmentObject(self.dataStore)
.onReceive(self.dataStore.items.throttle(for: 0.05, scheduler: DispatchQueue.main, latest: true)) { items in
self.itemCount = items.keys.count
if let tabViewDelegate = dataStore.items.value[selection]?.tabViewDelegate {
tabViewDelegate.setState(state: .selected)
}
}
.clipped()
}
private func offsetFor(index: Int) -> CGFloat {
return -(CGFloat(index) * settings.width)
}
}

View File

@ -2,7 +2,7 @@
// PagerTabViewDelegate.swift
// PagerTabStrip
//
// Created by Cecilia Pirotto on 26/7/21.
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import SwiftUI

View File

@ -1,161 +0,0 @@
//
// XLPagerView.swift
// PagerTabStrip
//
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import SwiftUI
///
/// Public Source Code
///
public class PagerSettings: ObservableObject {
@Published var width: CGFloat = 0
@Published var tabItemSpacing: CGFloat
@Published var tabItemHeight: CGFloat
@Published var indicatorBarHeight: CGFloat
@Published var indicatorBarColor: Color
@Published var contentOffset: CGFloat = 0
public init(tabItemSpacing: CGFloat = 5, tabItemHeight: CGFloat = 100, indicatorBarHeight: CGFloat = 1.5, indicatorBarColor: Color = .blue) {
self.tabItemSpacing = tabItemSpacing
self.tabItemHeight = tabItemHeight
self.indicatorBarHeight = indicatorBarHeight
self.indicatorBarColor = indicatorBarColor
}
}
@available(iOS 14.0, *)
public struct XLPagerView<Content> : View where Content : View {
private var content: () -> Content
@StateObject private var navContentViews = DataStore()
@StateObject private var pagerSettings = PagerSettings()
@State private var currentIndex: Int
@State private var currentOffset: CGFloat = 0 {
didSet {
self.pagerSettings.contentOffset = currentOffset
}
}
@State private var itemCount : Int = 0
@GestureState private var translation: CGFloat = 0
public init(selection: Int = 0,
pagerSettings: PagerSettings = PagerSettings(),
@ViewBuilder content: @escaping () -> Content) {
self.content = content
self._currentIndex = State(initialValue: selection)
self._pagerSettings = StateObject(wrappedValue: pagerSettings)
if let callback = navContentViews.items.value[currentIndex]?.appearCallback {
callback()
}
}
public var body: some View {
// PagerContainerView {
GeometryReader { gproxy in
HStack(spacing: 0) {
content()
.frame(width: gproxy.size.width)
}
.offset(x: -CGFloat(self.currentIndex) * pagerSettings.width)
.offset(x: self.translation)
.animation(.interactiveSpring(response: 0.5, dampingFraction: 1.00, blendDuration: 0.25), value: currentIndex)
.animation(.interactiveSpring(response: 0.5, dampingFraction: 1.00, blendDuration: 0.25), value: translation)
.gesture(
DragGesture().updating(self.$translation) { value, state, _ in
if (currentIndex == 0 && value.translation.width > 0) {
let valueWidth = value.translation.width
let normTrans = valueWidth / (gproxy.size.width + 50)
let logValue = log(1 + normTrans)
state = gproxy.size.width/1.5 * logValue
} else if (currentIndex == itemCount - 1 && value.translation.width < 0) {
let valueWidth = -value.translation.width
let normTrans = valueWidth / (gproxy.size.width + 50)
let logValue = log(1 + normTrans)
state = -gproxy.size.width / 1.5 * logValue
} else {
state = value.translation.width
}
}.onEnded { value in
let offset = value.predictedEndTranslation.width / gproxy.size.width
let newPredictedIndex = (CGFloat(self.currentIndex) - offset).rounded()
let newIndex = min(max(Int(newPredictedIndex), 0), self.itemCount - 1)
if abs(self.currentIndex - newIndex) > 1 {
self.currentIndex = newIndex > currentIndex ? currentIndex + 1 : currentIndex - 1
} else {
self.currentIndex = newIndex
}
if translation > 0 {
self.currentOffset = translation
}
}
)
.onChange(of: gproxy.frame(in: .local), perform: { geo in
pagerSettings.width = geo.width
})
.onChange(of: self.currentIndex) { [currentIndex] newIndex in
self.currentOffset = self.offsetForPageIndex(newIndex)
if let callback = navContentViews.items.value[newIndex]?.appearCallback {
callback()
}
if let tabViewDelegate = navContentViews.items.value[currentIndex]?.tabViewDelegate {
tabViewDelegate.setState(state: .normal)
}
if let tabViewDelegate = navContentViews.items.value[newIndex]?.tabViewDelegate, newIndex != currentIndex {
tabViewDelegate.setState(state: .selected)
}
}
.onChange(of: translation) { _ in
self.pagerSettings.contentOffset = translation - CGFloat(currentIndex)*pagerSettings.width
}
.onChange(of: pagerSettings.width) { _ in
self.currentOffset = self.offsetForPageIndex(self.currentIndex)
}
.onChange(of: itemCount) { _ in
currentIndex = currentIndex >= itemCount ? itemCount - 1 : currentIndex
}
.onAppear {
pagerSettings.width = gproxy.size.width
}
// }
}
.navBar(itemCount: $itemCount, selection: $currentIndex)
.environmentObject(self.navContentViews)
.environmentObject(self.pagerSettings)
.onReceive(self.navContentViews.items.throttle(for: 0.05, scheduler: DispatchQueue.main, latest: true)) { items in
self.itemCount = items.keys.count
if let tabViewDelegate = navContentViews.items.value[currentIndex]?.tabViewDelegate {
tabViewDelegate.setState(state: .selected)
}
}
.clipped()
}
private func offsetForPageIndex(_ index: Int) -> CGFloat {
let value = (CGFloat(index) * pagerSettings.width) * -1.0
return value
}
}
//private struct PagerContainerView<Content: View>: View {
// let content: () -> Content
//
// init(@ViewBuilder content: @escaping () -> Content) {
// self.content = content
// }
//
// var body: some View {
// content()
// }
//}
//
//extension PagerContainerView {
// @available(iOS 14.0, *)
// internal func navBar(itemCount: Binding<Int>, selection: Binding<Int>) -> some View {
// return self.modifier(NavBarModifier(itemCount: itemCount, selection: selection))
// }
//}

View File

@ -1,8 +0,0 @@
//
// iOS.swift
// PagerTabStrip
//
// Copyright © 2021 Xmartlabs SRL. All rights reserved.
//
import Foundation