Skip to content

Commit b951902

Browse files
committed
feat: experimental falback gradient for pure commons categories without main image
1 parent 1910e67 commit b951902

2 files changed

Lines changed: 111 additions & 9 deletions

File tree

CommonsFinder/Views/Reusable Views/CategoryTeaserBackground.swift

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,20 @@ struct CategoryTeaserBackground: View {
2424
Color.clear
2525
}
2626
}
27+
} else {
28+
ProceduralBackground(categoryName: category.commonsCategory ?? category.wikidataId ?? "")
29+
.blur(radius: 5)
30+
.clipped()
2731
}
2832
}
2933
.overlay {
30-
if category.thumbnailImage != nil {
31-
LinearGradient(
32-
stops: [
33-
.init(color: .init(white: 0, opacity: 0), location: 0),
34-
.init(color: .init(white: 0, opacity: 0.1), location: 0.35),
35-
.init(color: .init(white: 0, opacity: 0.2), location: 0.5),
36-
.init(color: .init(white: 0, opacity: 0.8), location: 1),
37-
], startPoint: .top, endPoint: .bottom)
38-
}
34+
LinearGradient(
35+
stops: [
36+
.init(color: .init(white: 0, opacity: 0), location: 0),
37+
.init(color: .init(white: 0, opacity: 0.1), location: 0.35),
38+
.init(color: .init(white: 0, opacity: 0.2), location: 0.5),
39+
.init(color: .init(white: 0, opacity: 0.8), location: 1),
40+
], startPoint: .top, endPoint: .bottom)
3941
}
4042
}
4143
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
//
2+
// ProceduralBackground.swift
3+
// CommonsFinder
4+
//
5+
// Created by Tom Brewe on 27.08.25.
6+
//
7+
8+
import Algorithms
9+
import NaturalLanguage
10+
import RegexBuilder
11+
import SwiftUI
12+
13+
struct ProceduralBackground: View {
14+
let categoryName: String
15+
var body: some View {
16+
proceduralMeshGradient(from: categoryName)
17+
}
18+
}
19+
20+
@ViewBuilder
21+
private func proceduralMeshGradient(from string: String) -> MeshGradient {
22+
let colors = categoryColors(for: string)
23+
let hues = categoryHues(for: string)
24+
let points: [SIMD2<Float>] = hues.adjacentPairs()
25+
.map({ (x, y) in
26+
.init(x: Float(x), y: Float(y))
27+
})
28+
let dim = Int(sqrt(Double(points.count)))
29+
30+
31+
MeshGradient(width: dim, height: dim, points: points, colors: colors, background: colors.first?.opacity(0.8) ?? .clear, smoothsColors: true)
32+
}
33+
34+
35+
private func categoryHues(for string: String) -> [Double] {
36+
string
37+
.lowercased()
38+
.adjacentPairs()
39+
.map { [$0.0, $0.1] }
40+
.map { $0.prefix(2).hashValue }
41+
.map { v in
42+
(Double(v) - Double(Int.min)) / Double(UInt.max)
43+
}
44+
}
45+
46+
private func categoryColors(for string: String) -> [Color] {
47+
let colors: [Color] = categoryHues(for: string)
48+
.map { hue in
49+
.init(hue: hue, saturation: 0.6, brightness: 0.5, opacity: 1)
50+
}
51+
return colors
52+
}
53+
54+
55+
#Preview {
56+
let categorieNames = [
57+
"apples of the forrest",
58+
"people of france",
59+
"people of Paris",
60+
"sheep walking",
61+
"people with sheep",
62+
"aaa aaa",
63+
"Ananas food",
64+
"Banana Food",
65+
"Banana dessert",
66+
"Berlin-Adlershof",
67+
"Berlin-Kreuzberg",
68+
"Berlin-Friedrichshain",
69+
"Berlin-Zehlendorf",
70+
"Potsdam-Babelsberg",
71+
"Potsdam-Jägervorstadt",
72+
"Potsdam-West",
73+
"Potsdam-Drewitz",
74+
"zzz zzz",
75+
"3 continents",
76+
"Streets of Berlin",
77+
"Streets of New York City in the 1990s",
78+
"Berlin",
79+
"Berlin Wall",
80+
"Sundown in Paris",
81+
"cities in Africa",
82+
"cities in America",
83+
"Cities in Europe",
84+
"Flux Compensator",
85+
"Fluvial geomorphology",
86+
87+
]
88+
89+
ScrollView(.vertical) {
90+
LazyVGrid(columns: [.init(), .init(), .init()]) {
91+
ForEach(categorieNames, id: \.self) { categoryName in
92+
ProceduralBackground(categoryName: categoryName)
93+
.frame(width: 100, height: 100)
94+
.overlay {
95+
Text(categoryName)
96+
}
97+
}
98+
}
99+
}
100+
}

0 commit comments

Comments
 (0)