Fix image carousel to limit number of dots to not spill screen beyond visible margins

Changelog-Fixed: Fix image carousel to limit number of dots to not spill screen beyond visible margins
Closes: https://github.com/damus-io/damus/issues/1227
Closes: https://github.com/damus-io/damus/issues/1295
This commit is contained in:
2023-07-14 23:00:10 -04:00
parent 29140d956b
commit 488ec8e009
4 changed files with 73 additions and 38 deletions

View File

@@ -31,6 +31,7 @@
3AAA95CA298DF87B00F3D526 /* TranslationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAA95C9298DF87B00F3D526 /* TranslationService.swift */; };
3AAA95CC298E07E900F3D526 /* DeepLPlan.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAA95CB298E07E900F3D526 /* DeepLPlan.swift */; };
3AAC7A022A60FE72002B50DF /* LocalizationUtilTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAC7A012A60FE72002B50DF /* LocalizationUtilTests.swift */; };
3AAC7A042A626A75002B50DF /* CarouselDotsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AAC7A032A626A75002B50DF /* CarouselDotsView.swift */; };
3AB72AB9298ECF30004BB58C /* Translator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB72AB8298ECF30004BB58C /* Translator.swift */; };
3ACB685C297633BC00C46468 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3ACB685A297633BC00C46468 /* InfoPlist.strings */; };
3ACB685F297633BC00C46468 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3ACB685D297633BC00C46468 /* Localizable.strings */; };
@@ -441,6 +442,7 @@
3AAA95C9298DF87B00F3D526 /* TranslationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TranslationService.swift; sourceTree = "<group>"; };
3AAA95CB298E07E900F3D526 /* DeepLPlan.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLPlan.swift; sourceTree = "<group>"; };
3AAC7A012A60FE72002B50DF /* LocalizationUtilTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationUtilTests.swift; sourceTree = "<group>"; };
3AAC7A032A626A75002B50DF /* CarouselDotsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarouselDotsView.swift; sourceTree = "<group>"; };
3AB5B86A2986D8A3006599D2 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = "<group>"; };
3AB5B86B2986D8A3006599D2 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
3AB5B86C2986D8A3006599D2 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
@@ -1435,6 +1437,7 @@
4C8D00C929DF80350036AF10 /* TruncatedText.swift */,
4C28595F2A12A2BE004746F7 /* SupporterBadge.swift */,
5C6E1DAC2A193EC2008FC15A /* GradientButtonStyle.swift */,
3AAC7A032A626A75002B50DF /* CarouselDotsView.swift */,
);
path = Components;
sourceTree = "<group>";
@@ -1876,6 +1879,7 @@
4CB883B6297730E400DC99E7 /* LNUrls.swift in Sources */,
4C7FF7D52823313F009601DB /* Mentions.swift in Sources */,
4C633350283D40E500B1C9C3 /* HomeModel.swift in Sources */,
3AAC7A042A626A75002B50DF /* CarouselDotsView.swift in Sources */,
4C987B57283FD07F0042CE38 /* FollowersModel.swift in Sources */,
3AB72AB9298ECF30004BB58C /* Translator.swift in Sources */,
4C363A9028247A1D006E126D /* NostrLink.swift in Sources */,

View File

@@ -0,0 +1,65 @@
//
// CarouselDotsView.swift
// damus
//
// Created by Terry Yiu on 7/15/23.
//
import SwiftUI
struct CarouselDotsView: View {
let maxCount: Int
let maxVisibleCount: Int
@Binding var selectedIndex: Int
var body: some View {
if maxCount > 1 {
HStack {
let visibleRange = visibleRange()
ForEach(0 ..< maxCount, id: \.self) { index in
if visibleRange.contains(index) {
Circle()
.fill(index == selectedIndex ? Color("DamusPurple") : Color("DamusLightGrey"))
.frame(width: 10, height: 10)
.onTapGesture {
selectedIndex = index
}
}
}
}
.padding(.top, CGFloat(8))
.id(UUID())
}
}
private func visibleRange() -> ClosedRange<Int> {
let visibleCount = min(maxCount, maxVisibleCount)
let half = Int(visibleCount / 2)
// Keep the selected dot in the middle of the visible dots when possible.
var minVisibleIndex: Int
var maxVisibleIndex: Int
if visibleCount % 2 == 0 {
minVisibleIndex = max(0, selectedIndex - half)
maxVisibleIndex = min(maxCount - 1, selectedIndex + half - 1)
} else {
minVisibleIndex = max(0, selectedIndex - half)
maxVisibleIndex = min(maxCount - 1, selectedIndex + half)
}
// Adjust min and max to be within the bounds of what is visibly allowed.
if (maxVisibleIndex - minVisibleIndex + 1) < visibleCount {
if minVisibleIndex == 0 {
maxVisibleIndex = visibleCount - 1
} else if maxVisibleIndex == maxCount - 1 {
minVisibleIndex = maxVisibleIndex - visibleCount + 1
}
} else if (maxVisibleIndex - minVisibleIndex + 1) > visibleCount {
minVisibleIndex = maxVisibleIndex - maxVisibleCount + 1
}
return minVisibleIndex...maxVisibleIndex
}
}

View File

@@ -209,30 +209,9 @@ struct ImageCarousel: View {
.onTapGesture { }
// This is our custom carousel image indicator
CarouselDotsView(urls: urls, selectedIndex: $selectedIndex)
}
}
}
// MARK: - Custom Carousel
struct CarouselDotsView<T>: View {
let urls: [T]
@Binding var selectedIndex: Int
var body: some View {
if urls.count > 1 {
HStack {
ForEach(urls.indices, id: \.self) { index in
Circle()
.fill(index == selectedIndex ? Color("DamusPurple") : Color("DamusLightGrey"))
.frame(width: 10, height: 10)
.onTapGesture {
selectedIndex = index
}
}
}
.padding(.top, CGFloat(8))
.id(UUID())
// A maximum of 18 should be visible. Any more than that and it starts to push the frame of the parent view
// causing adjacent views to disort in dimensions.
CarouselDotsView(maxCount: urls.count, maxVisibleCount: 18, selectedIndex: $selectedIndex)
}
}
}

View File

@@ -18,19 +18,6 @@ struct ImageView: View {
let disable_animation: Bool
var tabViewIndicator: some View {
HStack(spacing: 10) {
ForEach(urls.indices, id: \.self) { index in
Capsule()
.fill(index == selectedIndex ? Color(UIColor.label) : Color.secondary)
.frame(width: 7, height: 7)
}
}
.padding()
.background(.regularMaterial)
.clipShape(Capsule())
}
var body: some View {
ZStack {
Color(.systemBackground)
@@ -66,7 +53,7 @@ struct ImageView: View {
Spacer()
if (urls.count > 1) {
tabViewIndicator
CarouselDotsView(maxCount: urls.count, maxVisibleCount: 18, selectedIndex: $selectedIndex)
}
}
}