Created
July 14, 2020 12:52
-
-
Save agelessman/9b1c71b7799790f04270d083ee426de7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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