Skip to content

Instantly share code, notes, and snippets.

@CodeSlicing
Created August 5, 2020 23:23
Show Gist options
  • Save CodeSlicing/cfb024c2b899d6545f3db66b2d425621 to your computer and use it in GitHub Desktop.
Save CodeSlicing/cfb024c2b899d6545f3db66b2d425621 to your computer and use it in GitHub Desktop.
Shows and example of maintaining the position of an overlay using offsets
//
// KeepPositionOnRotation.swift
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//
// Created by Adam Fordyce on 06/08/2020.
// Copyright © 2020 Adam Fordyce. All rights reserved.
//
import SwiftUI
import PureSwiftUI
private let numColumns = 5
private let numRows = 5
struct KeepPositionOnRotation: View {
@Environment(\.horizontalSizeClass) var sizeClass
@State private var locations = [CGPoint](repeating: .zero, count: numColumns * numRows)
@State private var currentLocation = CGPoint.zero
@State private var selectedIndex = 0
var body: some View {
VStack {
ZStack {
VStack {
ForEach(0..<numRows) { row in
Spacer()
HStack {
ForEach(0..<numColumns) { column in
Spacer()
Frame(10)
.primaryDotStyle()
.geometryReader { (geo: GeometryProxy) in
let index = self.indexFor(row, column)
self.locations[index] = geo.globalCenter
if index == self.selectedIndex {
self.currentLocation = self.locations[index]
}
}
.onTapGesture {
withAnimation(.easeInOut(duration: 0.5)) {
let index = self.indexFor(row, column)
self.selectedIndex = index
self.currentLocation = self.locations[index]
}
}
Spacer()
}
}
}
Spacer()
}
// the trick here is that we need to make sure the relation
// between the overlay dot and the other dots remains the same
// when the device is rotated so the offsets work in both orientations
// so I've placed it initially in a ZStack over the dots.
Frame(20)
.secondaryDotStyle()
.offsetToPosition(self.currentLocation)
}
.id(sizeClass)
// we need to set an id to the size class so it rerenders the view
// and causes the geometry readers to run again to set up the new
// locations
}
}
private func indexFor(_ row: Int, _ column: Int) -> Int {
row * numColumns + column
}
}
private extension View {
func primaryDotStyle() -> some View {
self.clipCircleWithStroke(Color.black, lineWidth: 1, fill: Color.red)
}
func secondaryDotStyle() -> some View {
self.clipCircleWithStroke(Color.black, lineWidth: 1, fill: Color.yellow.opacity(0.75))
}
}
struct KeepPositionOnRotation_Previews: PreviewProvider {
struct KeepPositionOnRotation_Harness: View {
var body: some View {
KeepPositionOnRotation()
}
}
static var previews: some View {
KeepPositionOnRotation_Harness()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment