I have one view which I am using in another view, and i want to access property from one view to another...but i am not sure how to do it.. (My main problem with below code - I really appreciate if I got solution for this - is I am having dropdown even when it is collapsed its expanding when i clicked outside of it..which is wrong)
import SwiftUI
public struct TopSheet<Content>: View where Content : View {
private var content: () -> Content
let minHeight: CGFloat = 50.0
let startHeight: CGFloat = 50.0
let maxOpacity: CGFloat = 0.8
let maxArrowOffset: CGFloat = 8.0
let minimumContentHeight = 61.0
@State private var currentHeight: CGFloat = 0
@State private var contentHeight: CGFloat = 0
@State private var backgroundColor: Color = Color.clear
@State private var expand = false
@State private var arrowOffset: Double = 0
public init(@ViewBuilder content: @escaping () -> Content) { self.content = content }
public func expandRatio() -> Double { return max((currentHeight - minHeight) / contentHeight, 0) }
private func isTopSheetExpandable() -> Bool {
return contentHeight > minimumContentHeight
}
public var body: some View {
let tap = TapGesture()
.onEnded { _ in
expand.toggle()
if expand {
withAnimation(Animation.easeOut) {
currentHeight = max(contentHeight, minHeight)
self.backgroundColor = Color.grey
}
}
else {
withAnimation(Animation.easeOut) {
currentHeight = minHeight
self.backgroundColor = Color.clear
}
}
self.arrowOffset = expandRatio() * maxArrowOffset
}
let drag = DragGesture()
.onChanged { value in
currentHeight = value.translation.height
currentHeight = max(currentHeight, minHeight)
let opacity = min(expandRatio() * maxOpacity, maxOpacity)
self.backgroundColor = Color.gray.opacity(opacity)
self.arrowOffset = expandRatio() * maxArrowOffset
}
.onEnded { value in
expand.toggle()
if expand {
withAnimation(Animation.easeOut) {
currentHeight = max(contentHeight, minHeight)
self.backgroundColor = Color.gray.opacity(maxOpacity)
}
}
else {
withAnimation(Animation.easeOut) {
currentHeight = minHeight
self.backgroundColor = Color.clear
}
}
self.arrowOffset = expandRatio() * maxArrowOffset
}
VStack(spacing: 0) {
VStack(alignment: .leading, spacing: 0) {
VStack(spacing: 0) {
GeometryReader { geo in
content()
.viewHeight()
.fixedSize(horizontal: false, vertical: true)
}.onPreferenceChange(ViewHeightPreferenceKey.self) { height in
contentHeight = height
currentHeight = startHeight
}.clipped()
Spacer(minLength: 0)
}
.frame(height: currentHeight)
HStack(alignment: .center) {
Spacer()
if isTopSheetExpandable() {
Arrow(offset: arrowOffset)
.stroke(Color.gray, style: StrokeStyle(lineWidth: 4, lineCap: .round, lineJoin: .round))
.frame(width: 30, height: 4)
.padding(.bottom, 10)
.padding(.top, 10)
}
Spacer()
}
// .contentShape(Rectangle())
.gesture(drag)
.gesture(tap)
.animation(.easeInOut, value: 2)
}
.background(Color.white)
Spacer()
}
.background(self.backgroundColor.edgesIgnoringSafeArea([.vertical, .horizontal, .leading, .trailing, .top, .bottom]))
.simultaneousGesture(isTopSheetExpandable() ? tap : nil)
}
}
fileprivate struct Arrow: Shape {
var offset: Double
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: .zero)
path.addLine(to: CGPoint(x: rect.width/2, y: -offset))
path.move(to: CGPoint(x: rect.width/2, y: -offset))
path.addLine(to: CGPoint(x: rect.width, y: 0))
return path
}
}
Another view
import SwiftUI
struct NextView: View {
@State private var backgroundColor: Color = Color.gray
@State private var users: [String] = ["abc", "xyz", "pqr", "mno", "pqr", "ert", ""]
var accessibilityID: String
var body: some View {
ZStack {
Rectangle()
.fill()
.foregroundColor(backgroundColor)
TopSheet {
VStack(spacing: 0) {
ForEach($users, id: \.self) { user in
HStack {
Text(user.wrappedValue)
.padding(.vertical, 10)
Spacer()
}
.contentShape(Rectangle())
.simultaneousGesture(TapGesture().onEnded {
self.users = [user.wrappedValue] self.users.filter { $0 != user.wrappedValue }
switch self.users.first.unsafelyUnwrapped {
case "Joe Black": self.backgroundColor = .black
case "Eva Green": self.backgroundColor = .green
case "Jared Leto": self.backgroundColor = .red
default: self.backgroundColor = .gray
}
})
}
}
.padding(.horizontal, 10)
}
}
}
}
I might try some trick if I can able to access "expand" property from "TopSheet" and access it in "NextView"
CodePudding user response:
You should store expand
as @State
in your parent View
and pass it via Binding
. Simplified example:
public struct TopSheet<Content>: View where Content : View {
@Binding var expand : Bool
var content: () -> Content
//declare other properties private so they don't get used in the generated init
public var body: some View {
Text("Here")
}
}
struct NextView: View {
@State private var expand = false
var body: some View {
TopSheet(expand: $expand) {
Text("Content")
}
}
}
If you really want/need your explicit init
, you can do this:
public struct TopSheet<Content>: View where Content : View {
@Binding private var expand : Bool
private var content: () -> Content
public init(expand: Binding<Bool>, @ViewBuilder content: @escaping () -> Content) {
self._expand = expand
self.content = content
}
public var body: some View {
Text("Here")
}
}