From 2e43d1e98fc988946fcb9b7bf4a3b49cd1fc44ee Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 10 Dec 2019 21:17:01 +0400 Subject: [PATCH] Add option to request venues around arbitrary location --- .../Sources/LocationMapHeaderNode.swift | 67 +++++++++++++++--- .../Sources/LocationPickerController.swift | 12 +++- .../LocationPickerControllerNode.swift | 69 ++++++++++++++++--- .../Sources/LocationViewControllerNode.swift | 2 +- 4 files changed, 126 insertions(+), 24 deletions(-) diff --git a/submodules/LocationUI/Sources/LocationMapHeaderNode.swift b/submodules/LocationUI/Sources/LocationMapHeaderNode.swift index 619add57c5..30bf055109 100644 --- a/submodules/LocationUI/Sources/LocationMapHeaderNode.swift +++ b/submodules/LocationUI/Sources/LocationMapHeaderNode.swift @@ -7,19 +7,16 @@ private let panelInset: CGFloat = 4.0 private let panelSize = CGSize(width: 46.0, height: 90.0) private func generateBackgroundImage(theme: PresentationTheme) -> UIImage? { - return generateImage(CGSize(width: panelSize.width + panelInset * 2.0, height: panelSize.height + panelInset * 2.0)) { size, context in + let cornerRadius: CGFloat = 9.0 + return generateImage(CGSize(width: (cornerRadius + panelInset) * 2.0, height: (cornerRadius + panelInset) * 2.0)) { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) context.setShadow(offset: CGSize(), blur: 10.0, color: UIColor(rgb: 0x000000, alpha: 0.2).cgColor) context.setFillColor(theme.rootController.navigationBar.backgroundColor.cgColor) - let path = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: panelInset, y: panelInset), size: panelSize), cornerRadius: 9.0) + let path = UIBezierPath(roundedRect: CGRect(origin: CGPoint(x: panelInset, y: panelInset), size: CGSize(width: cornerRadius * 2.0, height: cornerRadius * 2.0)), cornerRadius: cornerRadius) context.addPath(path.cgPath) context.fillPath() - - context.setShadow(offset: CGSize(), blur: 0.0, color: nil) - context.setFillColor(theme.rootController.navigationBar.separatorColor.cgColor) - context.fill(CGRect(x: panelInset, y: panelInset + floorToScreenPixels(panelSize.height / 2.0), width: panelSize.width, height: UIScreenPixel)) - } + }?.stretchableImage(withLeftCapWidth: Int(cornerRadius + panelInset), topCapHeight: Int(cornerRadius + panelInset)) } private func generateShadowImage(theme: PresentationTheme, highlighted: Bool) -> UIImage? { @@ -38,26 +35,39 @@ final class LocationMapHeaderNode: ASDisplayNode { private var presentationData: PresentationData private let toggleMapModeSelection: () -> Void private let goToUserLocation: () -> Void + private let showPlacesInThisArea: () -> Void + + private var displayingPlacesButton = false let mapNode: LocationMapNode private let optionsBackgroundNode: ASImageNode + private let optionsSeparatorNode: ASDisplayNode private let infoButtonNode: HighlightableButtonNode private let locationButtonNode: HighlightableButtonNode + private let placesBackgroundNode: ASImageNode + private let placesButtonNode: HighlightableButtonNode private let shadowNode: ASImageNode - init(presentationData: PresentationData, toggleMapModeSelection: @escaping () -> Void, goToUserLocation: @escaping () -> Void) { + private var validLayout: (ContainerViewLayout, CGFloat, CGFloat, CGFloat, CGSize)? + + init(presentationData: PresentationData, toggleMapModeSelection: @escaping () -> Void, goToUserLocation: @escaping () -> Void, showPlacesInThisArea: @escaping () -> Void = {}) { self.presentationData = presentationData self.toggleMapModeSelection = toggleMapModeSelection self.goToUserLocation = goToUserLocation + self.showPlacesInThisArea = showPlacesInThisArea self.mapNode = LocationMapNode() self.optionsBackgroundNode = ASImageNode() + self.optionsBackgroundNode.contentMode = .scaleToFill self.optionsBackgroundNode.displaysAsynchronously = false self.optionsBackgroundNode.displayWithoutProcessing = true self.optionsBackgroundNode.image = generateBackgroundImage(theme: presentationData.theme) self.optionsBackgroundNode.isUserInteractionEnabled = true + self.optionsSeparatorNode = ASDisplayNode() + self.optionsSeparatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor + self.infoButtonNode = HighlightableButtonNode() self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal) self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .selected) @@ -66,6 +76,16 @@ final class LocationMapHeaderNode: ASDisplayNode { self.locationButtonNode = HighlightableButtonNode() self.locationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/TrackIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal) + self.placesBackgroundNode = ASImageNode() + self.placesBackgroundNode.contentMode = .scaleToFill + self.placesBackgroundNode.displaysAsynchronously = false + self.placesBackgroundNode.displayWithoutProcessing = true + self.placesBackgroundNode.image = generateBackgroundImage(theme: presentationData.theme) + self.placesBackgroundNode.isUserInteractionEnabled = true + + self.placesButtonNode = HighlightableButtonNode() + self.placesButtonNode.setTitle("Places In This Area", with: Font.regular(17.0), with: presentationData.theme.rootController.navigationBar.buttonColor, for: .normal) + self.shadowNode = ASImageNode() self.shadowNode.contentMode = .scaleToFill self.shadowNode.displaysAsynchronously = false @@ -78,42 +98,65 @@ final class LocationMapHeaderNode: ASDisplayNode { self.addSubnode(self.mapNode) self.addSubnode(self.optionsBackgroundNode) + self.optionsBackgroundNode.addSubnode(self.optionsSeparatorNode) self.optionsBackgroundNode.addSubnode(self.infoButtonNode) self.optionsBackgroundNode.addSubnode(self.locationButtonNode) + self.addSubnode(self.placesBackgroundNode) + self.placesBackgroundNode.addSubnode(self.placesButtonNode) self.addSubnode(self.shadowNode) self.infoButtonNode.addTarget(self, action: #selector(self.infoPressed), forControlEvents: .touchUpInside) self.locationButtonNode.addTarget(self, action: #selector(self.locationPressed), forControlEvents: .touchUpInside) + self.placesButtonNode.addTarget(self, action: #selector(self.placesPressed), forControlEvents: .touchUpInside) } - func updateState(mapMode: LocationMapMode, displayingMapModeOptions: Bool) { + func updateState(mapMode: LocationMapMode, displayingMapModeOptions: Bool, displayingPlacesButton: Bool, animated: Bool) { self.mapNode.mapMode = mapMode self.infoButtonNode.isSelected = displayingMapModeOptions + + let updateLayout = self.displayingPlacesButton != displayingPlacesButton + self.displayingPlacesButton = displayingPlacesButton + + if updateLayout, let (layout, navigationBarHeight, topPadding, offset, size) = self.validLayout { + let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.3, curve: .spring) : .immediate + self.updateLayout(layout: layout, navigationBarHeight: navigationBarHeight, topPadding: topPadding, offset: offset, size: size, transition: transition) + } } func updatePresentationData(_ presentationData: PresentationData) { self.presentationData = presentationData self.optionsBackgroundNode.image = generateBackgroundImage(theme: presentationData.theme) + self.optionsSeparatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal) self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .selected) self.infoButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/InfoActiveIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: [.selected, .highlighted]) self.locationButtonNode.setImage(generateTintedImage(image: UIImage(bundleImageName: "Location/TrackIcon"), color: presentationData.theme.rootController.navigationBar.buttonColor), for: .normal) + self.placesBackgroundNode.image = generateBackgroundImage(theme: presentationData.theme) self.shadowNode.image = generateShadowImage(theme: presentationData.theme, highlighted: false) } func updateLayout(layout: ContainerViewLayout, navigationBarHeight: CGFloat, topPadding: CGFloat, offset: CGFloat, size: CGSize, transition: ContainedViewLayoutTransition) { + self.validLayout = (layout, navigationBarHeight, topPadding, offset, size) + let mapHeight: CGFloat = floor(layout.size.height * 1.5) let mapFrame = CGRect(x: 0.0, y: floorToScreenPixels((size.height - mapHeight + navigationBarHeight) / 2.0) + offset, width: size.width, height: mapHeight) transition.updateFrame(node: self.mapNode, frame: mapFrame) self.mapNode.updateLayout(size: mapFrame.size) + let inset: CGFloat = 6.0 + + let placesButtonSize = CGSize(width: 180.0 + panelInset * 2.0, height: 45.0 + panelInset * 2.0) + let placesButtonFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - placesButtonSize.width) / 2.0), y: self.displayingPlacesButton ? navigationBarHeight + topPadding + inset : 0.0), size: placesButtonSize) + transition.updateFrame(node: self.placesBackgroundNode, frame: placesButtonFrame) + transition.updateFrame(node: self.placesButtonNode, frame: CGRect(origin: CGPoint(), size: placesButtonSize)) + transition.updateFrame(node: self.shadowNode, frame: CGRect(x: 0.0, y: size.height - 14.0, width: size.width, height: 14.0)) - let inset: CGFloat = 6.0 transition.updateFrame(node: self.optionsBackgroundNode, frame: CGRect(x: size.width - inset - panelSize.width - panelInset * 2.0, y: navigationBarHeight + topPadding + inset, width: panelSize.width + panelInset * 2.0, height: panelSize.height + panelInset * 2.0)) transition.updateFrame(node: self.infoButtonNode, frame: CGRect(x: panelInset, y: panelInset, width: panelSize.width, height: panelSize.height / 2.0)) transition.updateFrame(node: self.locationButtonNode, frame: CGRect(x: panelInset, y: panelInset + panelSize.height / 2.0, width: panelSize.width, height: panelSize.height / 2.0)) + transition.updateFrame(node: self.optionsSeparatorNode, frame: CGRect(x: panelInset, y: panelInset + panelSize.height / 2.0, width: panelSize.width, height: UIScreenPixel)) let alphaTransition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut) let optionsAlpha: CGFloat = size.height > 160.0 + navigationBarHeight ? 1.0 : 0.0 @@ -131,4 +174,8 @@ final class LocationMapHeaderNode: ASDisplayNode { @objc private func locationPressed() { self.goToUserLocation() } + + @objc private func placesPressed() { + self.showPlacesInThisArea() + } } diff --git a/submodules/LocationUI/Sources/LocationPickerController.swift b/submodules/LocationUI/Sources/LocationPickerController.swift index 4b44398615..e60f9e19ba 100644 --- a/submodules/LocationUI/Sources/LocationPickerController.swift +++ b/submodules/LocationUI/Sources/LocationPickerController.swift @@ -32,8 +32,9 @@ class LocationPickerInteraction { let dismissInput: () -> Void let updateSendActionHighlight: (Bool) -> Void let openHomeWorkInfo: () -> Void + let showPlacesInThisArea: () -> Void - init(sendLocation: @escaping (CLLocationCoordinate2D) -> Void, sendLiveLocation: @escaping (CLLocationCoordinate2D) -> Void, sendVenue: @escaping (TelegramMediaMap) -> Void, toggleMapModeSelection: @escaping () -> Void, updateMapMode: @escaping (LocationMapMode) -> Void, goToUserLocation: @escaping () -> Void, goToCoordinate: @escaping (CLLocationCoordinate2D) -> Void, openSearch: @escaping () -> Void, updateSearchQuery: @escaping (String) -> Void, dismissSearch: @escaping () -> Void, dismissInput: @escaping () -> Void, updateSendActionHighlight: @escaping (Bool) -> Void, openHomeWorkInfo: @escaping () -> Void) { + init(sendLocation: @escaping (CLLocationCoordinate2D) -> Void, sendLiveLocation: @escaping (CLLocationCoordinate2D) -> Void, sendVenue: @escaping (TelegramMediaMap) -> Void, toggleMapModeSelection: @escaping () -> Void, updateMapMode: @escaping (LocationMapMode) -> Void, goToUserLocation: @escaping () -> Void, goToCoordinate: @escaping (CLLocationCoordinate2D) -> Void, openSearch: @escaping () -> Void, updateSearchQuery: @escaping (String) -> Void, dismissSearch: @escaping () -> Void, dismissInput: @escaping () -> Void, updateSendActionHighlight: @escaping (Bool) -> Void, openHomeWorkInfo: @escaping () -> Void, showPlacesInThisArea: @escaping ()-> Void) { self.sendLocation = sendLocation self.sendLiveLocation = sendLiveLocation self.sendVenue = sendVenue @@ -47,6 +48,7 @@ class LocationPickerInteraction { self.dismissInput = dismissInput self.updateSendActionHighlight = updateSendActionHighlight self.openHomeWorkInfo = openHomeWorkInfo + self.showPlacesInThisArea = showPlacesInThisArea } } @@ -198,6 +200,7 @@ public final class LocationPickerController: ViewController { var state = state state.displayingMapModeOptions = false state.selectedLocation = .none + state.searchingVenuesAround = false return state } }, goToCoordinate: { [weak self] coordinate in @@ -208,6 +211,7 @@ public final class LocationPickerController: ViewController { var state = state state.displayingMapModeOptions = false state.selectedLocation = .location(coordinate, nil) + state.searchingVenuesAround = false return state } }, openSearch: { [weak self] in @@ -259,9 +263,13 @@ public final class LocationPickerController: ViewController { guard let strongSelf = self else { return } - let controller = textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.Map_HomeAndWorkTitle, text: strongSelf.presentationData.strings.Map_HomeAndWorkInfo, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]) strongSelf.present(controller, in: .window(.root)) + }, showPlacesInThisArea: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.controllerNode.requestPlacesAtSelectedLocation() }) self.scrollToTop = { [weak self] in diff --git a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift index e72313ea03..a7fc4e163c 100644 --- a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift +++ b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift @@ -227,12 +227,14 @@ struct LocationPickerState { var displayingMapModeOptions: Bool var selectedLocation: LocationPickerLocation var forceSelection: Bool + var searchingVenuesAround: Bool init() { self.mapMode = .map self.displayingMapModeOptions = false self.selectedLocation = .none self.forceSelection = false + self.searchingVenuesAround = false } } @@ -259,6 +261,8 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { private let statePromise: Promise private var geocodingDisposable = MetaDisposable() + private let searchVenuesPromise = Promise() + private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)? private var listOffset: CGFloat? @@ -277,7 +281,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { self.listNode.verticalScrollIndicatorColor = UIColor(white: 0.0, alpha: 0.3) self.listNode.verticalScrollIndicatorFollowsOverscroll = true - self.headerNode = LocationMapHeaderNode(presentationData: presentationData, toggleMapModeSelection: interaction.toggleMapModeSelection, goToUserLocation: interaction.goToUserLocation) + self.headerNode = LocationMapHeaderNode(presentationData: presentationData, toggleMapModeSelection: interaction.toggleMapModeSelection, goToUserLocation: interaction.goToUserLocation, showPlacesInThisArea: interaction.showPlacesInThisArea) self.headerNode.mapNode.isRotateEnabled = false self.optionsNode = LocationOptionsNode(presentationData: presentationData, updateMapMode: interaction.updateMapMode) @@ -408,13 +412,30 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { } ) + let foundVenues: Signal<[TelegramMediaMap]?, NoError> = .single(nil) + |> then( + self.searchVenuesPromise.get() + |> distinctUntilChanged + |> mapToSignal { coordinate -> Signal<[TelegramMediaMap]?, NoError> in + if let coordinate = coordinate { + return (.single(nil) + |> then( + nearbyVenues(account: context.account, latitude: coordinate.latitude, longitude: coordinate.longitude) + |> map (Optional.init) + )) + } else { + return .single(nil) + } + } + ) + let previousState = Atomic(value: self.state) let previousUserLocation = Atomic(value: nil) let previousAnnotations = Atomic<[LocationPinAnnotation]>(value: []) let previousEntries = Atomic<[LocationPickerEntry]?>(value: nil) - self.disposable = (combineLatest(self.presentationDataPromise.get(), self.statePromise.get(), userLocation, venues) - |> deliverOnMainQueue).start(next: { [weak self] presentationData, state, userLocation, venues in + self.disposable = (combineLatest(self.presentationDataPromise.get(), self.statePromise.get(), userLocation, venues, foundVenues) + |> deliverOnMainQueue).start(next: { [weak self] presentationData, state, userLocation, venues, foundVenues in if let strongSelf = self { var entries: [LocationPickerEntry] = [] switch state.selectedLocation { @@ -462,7 +483,8 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { entries.append(.header(presentationData.theme, presentationData.strings.Map_ChooseAPlace.uppercased())) - if let venues = venues { + var displayedVenues = state.searchingVenuesAround ? foundVenues : venues + if let venues = displayedVenues { var index: Int = 0 for venue in venues { entries.append(.venue(presentationData.theme, venue, index)) @@ -480,11 +502,10 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { crossFade = true } - let transition = preparedTransition(from: previousEntries ?? [], to: entries, isLoading: venues == nil, crossFade: crossFade, account: context.account, presentationData: presentationData, interaction: strongSelf.interaction) + let transition = preparedTransition(from: previousEntries ?? [], to: entries, isLoading: displayedVenues == nil, crossFade: crossFade, account: context.account, presentationData: presentationData, interaction: strongSelf.interaction) strongSelf.enqueueTransition(transition) - - strongSelf.headerNode.updateState(mapMode: state.mapMode, displayingMapModeOptions: state.displayingMapModeOptions) - + + var displayingPlacesButton = false let previousUserLocation = previousUserLocation.swap(userLocation) switch state.selectedLocation { case .none: @@ -492,9 +513,11 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { strongSelf.headerNode.mapNode.setMapCenter(coordinate: userLocation.coordinate, isUserLocation: true, animated: previousUserLocation != nil) } strongSelf.headerNode.mapNode.resetAnnotationSelection() + strongSelf.searchVenuesPromise.set(.single(nil)) case .selecting: strongSelf.headerNode.mapNode.resetAnnotationSelection() - case let .location(coordinate, _): + strongSelf.searchVenuesPromise.set(.single(nil)) + case let .location(coordinate, address): var updateMap = false switch previousState.selectedLocation { case .none, .venue: @@ -510,12 +533,19 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { strongSelf.headerNode.mapNode.setMapCenter(coordinate: coordinate, isUserLocation: false, animated: true) strongSelf.headerNode.mapNode.switchToPicking(animated: false) } + + if address != nil { + displayingPlacesButton = foundVenues == nil + } case let .venue(venue): strongSelf.headerNode.mapNode.setMapCenter(coordinate: venue.coordinate, animated: true) + strongSelf.searchVenuesPromise.set(.single(nil)) } + strongSelf.headerNode.updateState(mapMode: state.mapMode, displayingMapModeOptions: state.displayingMapModeOptions, displayingPlacesButton: displayingPlacesButton, animated: true) + let annotations: [LocationPinAnnotation] - if let venues = venues { + if let venues = displayedVenues { annotations = venues.compactMap { LocationPinAnnotation(context: context, theme: presentationData.theme, location: $0) } } else { annotations = [] @@ -533,6 +563,8 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { updateLayout = true } else if previousState.selectedLocation.isCustom != state.selectedLocation.isCustom { updateLayout = true + } else if previousState.searchingVenuesAround != state.searchingVenuesAround { + updateLayout = true } if updateLayout { @@ -597,6 +629,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { var state = state state.displayingMapModeOptions = false state.selectedLocation = .selecting + state.searchingVenuesAround = false return state } } @@ -609,6 +642,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { var state = state if case .selecting = state.selectedLocation { state.selectedLocation = .location(coordinate, nil) + state.searchingVenuesAround = false } return state } @@ -622,6 +656,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { var state = state state.displayingMapModeOptions = false state.selectedLocation = annotation?.location.flatMap { .venue($0) } ?? .none + state.searchingVenuesAround = false return state } } @@ -634,6 +669,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { var state = state state.displayingMapModeOptions = false state.selectedLocation = .none + state.searchingVenuesAround = false return state } } @@ -746,7 +782,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { let isFirstLayout = self.validLayout == nil self.validLayout = (layout, navigationHeight) - let isPickingLocation = self.state.selectedLocation.isCustom || self.state.forceSelection + let isPickingLocation = (self.state.selectedLocation.isCustom || self.state.forceSelection) && !self.state.searchingVenuesAround let optionsHeight: CGFloat = 38.0 var actionHeight: CGFloat? self.listNode.forEachItemNode { itemNode in @@ -819,4 +855,15 @@ final class LocationPickerControllerNode: ViewControllerTracingNode { self.headerNode.updateHighlight(highlighted) self.shadeNode.backgroundColor = highlighted ? self.presentationData.theme.list.itemHighlightedBackgroundColor : self.presentationData.theme.list.plainBackgroundColor } + + func requestPlacesAtSelectedLocation() { + if case let .location(coordinate, _) = self.state.selectedLocation { + self.searchVenuesPromise.set(.single(coordinate)) + self.updateState { state in + var state = state + state.searchingVenuesAround = true + return state + } + } + } } diff --git a/submodules/LocationUI/Sources/LocationViewControllerNode.swift b/submodules/LocationUI/Sources/LocationViewControllerNode.swift index e3c1d0620a..2dac87a12e 100644 --- a/submodules/LocationUI/Sources/LocationViewControllerNode.swift +++ b/submodules/LocationUI/Sources/LocationViewControllerNode.swift @@ -258,7 +258,7 @@ final class LocationViewControllerNode: ViewControllerTracingNode { let transition = preparedTransition(from: previousEntries ?? [], to: entries, account: context.account, presentationData: presentationData, interaction: strongSelf.interaction) strongSelf.enqueueTransition(transition) - strongSelf.headerNode.updateState(mapMode: state.mapMode, displayingMapModeOptions: state.displayingMapModeOptions) + strongSelf.headerNode.updateState(mapMode: state.mapMode, displayingMapModeOptions: state.displayingMapModeOptions, displayingPlacesButton: false, animated: false) switch state.selectedLocation { case .initial: