Skip to content

Instantly share code, notes, and snippets.

@agelessman
Created July 14, 2020 12:52
Show Gist options
  • Save agelessman/9b1c71b7799790f04270d083ee426de7 to your computer and use it in GitHub Desktop.
Save agelessman/9b1c71b7799790f04270d083ee426de7 to your computer and use it in GitHub Desktop.
//
// ContentView.swift
// NestedViewsDemo
//
// Created by MC on 2020/7/14.
//
import SwiftUI
struct ContentView: View {
var body: some View {
Example()
}
}
struct Example1: View {
var body: some View {
HStack {
RoundedRectangle(cornerRadius: 5)
.foregroundColor(.green)
.frame(width: 200, height: 100)
RoundedRectangle(cornerRadius: 5)
.foregroundColor(.blue)
.frame(width: 200, height: 100)
RoundedRectangle(cornerRadius: 5)
.foregroundColor(.orange)
.frame(width: 200, height: 100)
}
.padding(.horizontal, 50)
.padding(.vertical, 100)
.background(Color.purple.opacity(0.5))
}
}
struct Example: View {
var body: some View {
HStack {
RoundedRectangle(cornerRadius: 5)
.foregroundColor(Color.gray.opacity(0.5))
.frame(width: 250, height: 300)
.anchorPreference(key: MypreferenceKey.self, value: .bounds) { anchor in
[MyPreferenceData(viewType: .miniMapArea, bounds: anchor)]
}
ZStack(alignment: .topLeading) {
VStack {
HStack {
DragableView(color: .green)
DragableView(color: .blue)
DragableView(color: .pink)
}
HStack {
DragableView(color: .black)
DragableView(color: .white)
DragableView(color: .purple)
}
}
}
.frame(width: 550, height: 300)
.background(Color.orange.opacity(0.5))
.transformAnchorPreference(key: MypreferenceKey.self, value: .bounds, transform: {
$0.append(contentsOf: [MyPreferenceData(viewType: .parent, bounds: $1)])
})
}
.overlayPreferenceValue(MypreferenceKey.self) { value in
GeometryReader { proxy in
MiniMap(geometry: proxy, preferences: value)
}
}
}
}
struct MiniMap: View {
let geometry: GeometryProxy
let preferences: [MyPreferenceData]
var body: some View {
guard let parentAnchor = preferences.first(where: { $0.viewType == .parent })?.bounds else {
return AnyView(EmptyView())
}
guard let miniMapAreaAnchor = preferences.first(where: { $0.viewType == .miniMapArea })?.bounds else {
return AnyView(EmptyView())
}
let factor = geometry[parentAnchor].width / (geometry[miniMapAreaAnchor].width - 10)
let miniMapAreaPosition = CGPoint(x: geometry[miniMapAreaAnchor].minX, y: geometry[miniMapAreaAnchor].minY)
let parentPosition = CGPoint(x: geometry[parentAnchor].minX, y: geometry[parentAnchor].minY)
return AnyView(miniMapView(factor, miniMapAreaPosition, parentPosition))
}
func miniMapView(_ factor: CGFloat,
_ miniMapAreaPosition: CGPoint,
_ parentPosition: CGPoint) -> some View {
ZStack(alignment: .topLeading) {
ForEach(preferences.reversed()) { pref in
if pref.show() {
self.rectangleView(pref, factor, miniMapAreaPosition, parentPosition)
}
}
}
.padding(5)
}
func rectangleView(_ pref: MyPreferenceData,
_ factor: CGFloat,
_ miniMapAreaPosition: CGPoint,
_ parentPosition: CGPoint) -> some View {
return Rectangle()
.fill(pref.getColor())
.frame(width: self.geometry[pref.bounds].width / factor,
height: self.geometry[pref.bounds].height / factor)
.offset(x: (self.geometry[pref.bounds].minX - parentPosition.x) / factor + miniMapAreaPosition.x,
y: (self.geometry[pref.bounds].minY - parentPosition.y) / factor + miniMapAreaPosition.y)
}
}
struct DragableView: View {
let color: Color
@State private var currentOffset: CGSize = CGSize.zero
@State private var preOffset: CGSize = CGSize(width: 100, height: 100)
var w: CGFloat {
self.currentOffset.width + self.preOffset.width
}
var h: CGFloat {
self.currentOffset.height + self.preOffset.height
}
var body: some View {
RoundedRectangle(cornerRadius: 5)
.foregroundColor(color)
.frame(width: w, height: h)
.anchorPreference(key: MypreferenceKey.self, value: .bounds) { anchor in
[MyPreferenceData(viewType: .son(color), bounds: anchor)]
}
.gesture(
DragGesture()
.onChanged { (value: DragGesture.Value) in
self.currentOffset = value.translation
}
.onEnded { _ in
self.preOffset = CGSize(width: w,
height: h)
self.currentOffset = CGSize.zero
}
)
}
}
struct MyPreferenceData: Identifiable {
let id = UUID()
let viewType: ViewType
let bounds: Anchor<CGRect>
func getColor() -> Color {
switch self.viewType {
case .parent:
return Color.orange.opacity(0.5)
case .son(let c):
return c
default:
return Color.gray.opacity(0.3)
}
}
func show() -> Bool {
switch self.viewType {
case .parent:
return true
case .son:
return true
default:
return false
}
}
}
struct MypreferenceKey: PreferenceKey {
typealias Value = [MyPreferenceData]
static var defaultValue: Value = []
static func reduce(value: inout [MyPreferenceData], nextValue: () -> [MyPreferenceData]) {
value.append(contentsOf: nextValue())
}
}
enum ViewType: Equatable {
case parent
case son(Color)
case miniMapArea
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment