** EDIT ** Suspect the UIKit view that this SwiftUI view is embedded in (via UIHostingController) is the problem, as @eXcore's solution works for me in preview. This is the UIKit layout code:
var searchBox: UIHostingController<WKMapDropdownView> = UIHostingController(rootView: WKMapDropdownView(activityTypes: []))
searchBox.view.backgroundColor = UIColor.clear
self.view.addSubview(searchBox.view)
searchBox.view.translatesAutoresizingMaskIntoConstraints = false
let constraints = [
searchBox.view.topAnchor.constraint(equalTo: self.mapView!.topAnchor, constant: 30),
searchBox.view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20),
searchBox.view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20),
]
NSLayoutConstraint.activate(constraints)
========
I have a SwiftUI View which contains a VStack with some children that are shown/hidden when the first child view is tapped.
The animation looks a bit weird as animating in causes the already-visible child to jump to the middle then expand back to the top. I want it to happen so the already-visible child remains in place and the hidden options slide down underneath.
I'm thinking a height animation for the children might be better, or maybe the VStack itself is the cause of the issue.
Can someone help please?
ViewModel:
import Foundation
enum WKMapDropdownState {
case optionsCollapsed
case optionsExpanded
case dropdownCollapsed
}
enum WKMapSearchType {
case postcode
case name
case activityType
}
class WKMapDropdownViewModel : ObservableObject {
@Published var optionsExpanded : Bool = false
@Published var titleVisible : Bool = true
@Published var backArrowVisible : Bool = false
private var currentState : WKMapDropdownState = .optionsCollapsed
func toggleDropdown() {
switch (currentState) {
case .optionsCollapsed:
expandOptions()
case .optionsExpanded:
collapseOptions()
case .dropdownCollapsed:
// self.delegate?.dropdownBackTapped() -> Hide everything
expandDropdown()
}
}
func searchTypeButtonTapped(searchType: WKMapSearchType) {
collapseDropdown()
}
private func showDropdownOptions() {
optionsExpanded = true
}
private func hideDropdownOptions() {
optionsExpanded = false
}
private func showCollapsedOpenviewButton() {
titleVisible = true
backArrowVisible = true
}
private func showExpandedOpenviewButton() {
titleVisible = true
backArrowVisible = false
}
private func expandOptions() {
showDropdownOptions()
currentState = .optionsExpanded
}
private func collapseOptions() {
hideDropdownOptions()
currentState = .optionsCollapsed
}
private func expandDropdown() {
hideDropdownOptions()
showExpandedOpenviewButton()
currentState = .optionsCollapsed
}
private func collapseDropdown() {
hideDropdownOptions()
showCollapsedOpenviewButton()
currentState = .dropdownCollapsed
}
}
View:
struct WKMapDropdownView: View {
@ObservedObject var viewModel = WKMapDropdownViewModel()
var body: some View {
VStack {
HStack {
Button(action: {
withAnimation {
self.viewModel.toggleDropdown()
}
}) {
HStack {
Text("I want to search...")
.opacity(self.viewModel.titleVisible ? 1 : 0)
.foregroundColor(.white)
.font(.custom("Roboto-Regular", size: 19))
.frame(maxWidth: .infinity, alignment: .leading)
Image(self.viewModel.optionsExpanded ? "searchBoxUpArrow" : "searchBoxDownArrow")
.frame(maxWidth: .infinity, alignment: .trailing)
.animation(nil)
}
.frame(maxWidth: .infinity)
.padding(EdgeInsets(top: 19, leading: 22, bottom: 18, trailing: 25))
}
.background(Color.init("Tealish"))
}
.frame(maxHeight: .infinity, alignment: .top)
if self.viewModel.optionsExpanded {
VStack {
Button(action: { self.viewModel.searchTypeButtonTapped(searchType: .postcode) }) {
Text("By Postcode")
.font(.custom("Roboto-Regular", size: 19))
.foregroundColor(Color.init("Tealish"))
.background(Color.clear)
.padding(EdgeInsets(top: 19, leading: 22, bottom: 18, trailing: 25))
.frame(maxWidth: .infinity, alignment: .leading)
}
.transition(.identity)
Button(action: { self.viewModel.searchTypeButtonTapped(searchType: .name) }) {
Text("All Wiki Places")
.font(.custom("Roboto-Regular", size: 19))
.foregroundColor(Color.init("Tealish"))
.background(Color.clear)
.padding(EdgeInsets(top: 19, leading: 22, bottom: 18, trailing: 25))
.frame(maxWidth: .infinity, alignment: .leading)
}
.transition(.identity)
Button(action: { self.viewModel.searchTypeButtonTapped(searchType: .activityType) }) {
Text("By Activity Type")
.font(.custom("Roboto-Regular", size: 19))
.foregroundColor(Color.init("Tealish"))
.background(Color.clear)
.padding(EdgeInsets(top: 19, leading: 22, bottom: 18, trailing: 25))
.frame(maxWidth: .infinity, alignment: .leading)
}
.transition(.identity)
}
.frame(maxHeight: .infinity, alignment: .top)
}
}
.frame(maxHeight: .infinity, alignment: .top)
.background(Color(UIColor.systemBackground))
.clipShape(RoundedRectangle(cornerRadius:10))
}
}
struct WKMapDropdownView_Previews: PreviewProvider {
static var previews: some View {
WKMapDropdownView()
}
}