I'm having a hard time matching the correct grid cell when zooming out from the corresponding tab.
Just starting to learn and I should definitely go through a few more tutorials. If you want to lend a hand here, thank you in advance.
This is the code:
Models
struct Thumbnail: Identifiable {
let id = UUID()
var name: String
}
struct Wallpaper: Identifiable {
var id = UUID()
var name: String
}
let thumbnailSet = (1...50).map { Thumbnail(name: "iridisfera-thumbnail-\($0)") }
let wallpaperSet = (1...50).map { Wallpaper(name: "iridisfera-wallpaper-\($0)") }
Gallery (I removed my selectedTab experiments so you can directly insert your code)
struct GalleryView: View {
@Namespace var namespace
@State private var fullScreen: Int? = nil
@State private var selectedTab = 0
let columns = [GridItem(.flexible(), spacing: 2), GridItem(.flexible())]
var body: some View {
ZStack {
// MARK: GRID VIEW
ScrollView(showsIndicators: false) {
LazyVGrid(columns: columns, spacing: 2) {
ForEach(thumbnailSet.indices) { index in
let fullscreenIndex = fullScreen
if index == fullscreenIndex {
Color.clear
} else {
Image(thumbnailSet[index].name)
.resizable()
.aspectRatio(0.5, contentMode: .fit)
.matchedGeometryEffect(id: index, in: namespace)
.onTapGesture {
withAnimation(.interpolatingSpring(mass: 0.2, stiffness: 34, damping: 4)) {fullScreen = index}
}
}
}
}
}
.ignoresSafeArea()
// MARK: FULL SCREEN VIEW
if let fullscreenIndex = fullScreen {
TabView(selection: $selectedTab) {
ForEach(wallpaperSet.indices) { index in
Image(wallpaperSet[index].name)
.resizable()
.ignoresSafeArea()
.scaledToFill()
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.matchedGeometryEffect(id: fullscreenIndex, in: namespace)
.ignoresSafeArea()
.zIndex(1)
.onTapGesture {
withAnimation(.interpolatingSpring(mass: 0.1, stiffness: 28, damping: 4)) {fullScreen = nil}
}
}
}
}
}
CodePudding user response:
This code shows the general approach. The TabView has to be in a separate struct, so it can be initialized with the already selected tab. The images in TabView need to have .tag()
to identify them for the selection.
It does not work perfectly yet, as your images are not identifiable, but it should give you the direction. Working with the indices alone is unsafe, so you should put them in an Identifiable struct and select by the id.
struct GalleryView: View {
@Namespace var namespace
@State private var fullScreen: Int? = nil
let columns = [GridItem(.flexible(), spacing: 2), GridItem(.flexible())]
var body: some View {
ZStack {
// MARK: GRID VIEW
ScrollView(showsIndicators: false) {
LazyVGrid(columns: columns, spacing: 2) {
ForEach(thumbnailSet.indices) { index in
if index == fullScreen {
Color.clear
} else {
Image(thumbnailSet[index].name)
.resizable()
.aspectRatio(1, contentMode: .fill)
.matchedGeometryEffect(id: index, in: namespace)
.onTapGesture {
withAnimation {
fullScreen = index
}
}
}
}
}
}
.ignoresSafeArea()
// MARK: FULL SCREEN VIEW
if fullScreen != nil {
FullscreenTabView(selectedImage: $fullScreen, ns: namespace)
}
}
}
}
struct FullscreenTabView: View {
@Binding var selectedImage: Int?
var ns: Namespace.ID
init(selectedImage: Binding<Int?>, ns: Namespace.ID) {
self._selectedImage = selectedImage
self.ns = ns
// initialize selctedTab to selectedImage
self._selectedTab = State(initialValue: selectedImage.wrappedValue ?? 0)
}
@State private var selectedTab: Int
var body: some View {
TabView(selection: $selectedTab) {
ForEach(wallpaperSet.indices) { index in
Image(wallpaperSet[index].name)
.resizable()
.tag(index) // << the images in TabView need tags to identify
.ignoresSafeArea()
.matchedGeometryEffect(id: index == selectedTab ? selectedTab : 0,
in: ns, isSource: true)
.onTapGesture {
withAnimation {
selectedImage = nil
}
}
}
}
.ignoresSafeArea()
.tabViewStyle(.page(indexDisplayMode: .never) )
}
}