SwiftUI Button interact with Map
Actually I think in your case, as you have a reference to map, you can try to interact with it directly (aka imperatively, because it is such by nature, so no need to make simple thing complex)
Like
...
let myMapHolder = MapView()
var body: some View {
ZStack {
myMapHolder
.userTrackingMode(.follow)
.edgesIgnoringSafeArea(.all)
...
VStack() {
Button(action: {
self.myMapHolder.mapView.userTrackingMode = _your_mode_
}) {
Image(systemName: "location.fill")
Want to show zoom in zoom out on map through buttons in swiftUI
You have to operate with span
parameter of MKCoordinateRegion
for that (the less value the more zoomed area), like (demo zoom on 10%):
Button("Zoom In") {
region.span.latitudeDelta *= 0.9
region.span.longitudeDelta *= 0.9
}
Tested with Xcode 13.2 / iOS 15.2
How do I make a clickable pin in SwiftUI's new Map view?
As of XCode 12.3 this appears to be functioning with buttons. One key gotcha that I noticed though was that if you use offsets then it's possible your buttons will be placed out of the tappable area of the annotation.
In order to counter that you can add additional padding to account for the offset and keep it within tappable bounds:
MapAnnotation(coordinate: item.placemark.coordinate) {
ZStack {
MapPinView() // My view for showing a precise pin
VStack { // A prompt with interactive option, offset by 60pt
Text("Use this location?")
HStack {
Button("Yes", action: {
print("yes")
})
Button("No", action: {
print("no")
})
}
}
.offset(x: 0, y: 60) // offset prompt so it's below the marker pin
}
.padding(.vertical, 60) // compensate for offset in tappable area of annotation. pad both the top AND the bottom to keep contents centered
}
How to display MKCompassButton with swiftui
Actually compass is showing, but only when you try to turn your map. If you want to see compass button for all the time, you can add your own button in makeUIView
func:
struct RootMapView: View {
var body: some View {
MapView()
}
}
struct MapView: UIViewRepresentable {
func makeUIView(context: Context) -> MKMapView {
let map = MKMapView()
map.showsCompass = false // hides current compass, which shows only on map turning
let compassBtn = MKCompassButton(mapView: map)
compassBtn.frame.origin = CGPoint(x: 20, y: 20) // you may use GeometryReader to replace it's position
compassBtn.compassVisibility = .visible // compass will always be on map
map.addSubview(compassBtn)
return map
}
func updateUIView(_ uiView: MKMapView, context: Context) {
}
}
SwiftUI - Mapkit - Binding mapkit and show view on annotation callout buttons
import SwiftUI
import MapKit
class Store: Identifiable, ObservableObject {
let id: UUID = UUID()
@Published var title: String?
@Published var latitude: Double
@Published var longitude: Double
@Published var displayStoreImage: Bool
@Published var displayRedMarker: Bool
@Published var redStore: Bool
init(title: String, latitude: Double , longitude: Double, displayStoreImage: Bool, displayRedMarker: Bool, redStore: Bool ) {
self.title = title
self.latitude = latitude
self.longitude = longitude
self.displayRedMarker = displayRedMarker
self.displayStoreImage = displayStoreImage
self.redStore = redStore
}
}
extension Store{
var coordinate: CLLocationCoordinate2D{
get{
return CLLocationCoordinate2D(latitude: self.latitude, longitude: self.longitude)
}
}
}
struct MapViewParent: View {
@State var selectedListing: Store?
@State var results: [Store] = [Store(title: "Point 1 title", latitude: 38.3 , longitude: 123.2, displayStoreImage: true, displayRedMarker: true, redStore: false ), Store(title: "Point 2 title", latitude: 38.4 , longitude: 123.1, displayStoreImage: true, displayRedMarker: false, redStore: true )
]
var body: some View {
VStack{
Button("clear-storepage", action: {
selectedListing = nil
})
if selectedListing != nil{
TopMapMenu(results: $results, selectedListing: $selectedListing)
}else{
MapViewAnnotations(selectedListing: $selectedListing, results: $results)
}
}
}
}
struct MapViewAnnotations: View {
@State var region: MKCoordinateRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 38.3 , longitude: 123.2 ), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
@Binding var selectedListing: Store?
@Binding var results: [Store]
var body: some View {
Map(coordinateRegion: $region, annotationItems: results, annotationContent: {
listing in
MapAnnotation(coordinate: listing.coordinate, content: {
Button(action: {
self.selectedListing = listing as Store?
self.region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: listing.coordinate.latitude , longitude: listing.coordinate.longitude ), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
}, label: {
if listing.displayStoreImage{
if listing.displayRedMarker{
Image(systemName: "mappin.circle.fill").foregroundColor(.red).contentShape(Circle())
}else{
Image(systemName: "mappin").foregroundColor(.green).contentShape(Circle())
}
}
else{
Image(systemName: "mappin.circle.fill").foregroundColor(.red).contentShape(Circle())
}
}).buttonStyle(PlainButtonStyle())
})
})
}
}
struct TopMapMenu: View {
@Binding var results: [Store]
@Binding var selectedListing: Store?
var body: some View {
List{
ForEach(results, id: \.id){store in
StoreView(store: store, selectedListing: $selectedListing, results: $results)
}
}
}
}
struct StoreView: View {
@ObservedObject var store: Store
@Binding var selectedListing: Store?
@Binding var results: [Store]
var body: some View{
HStack{
VStack(alignment: .leading, spacing: 10){
Button(action: {
store.displayRedMarker.toggle()
store.redStore = store.displayRedMarker
}, label: {
HStack {
if store.redStore {
Image(systemName: "heart.fill")
}
else { Image(systemName: "heart") }
Text("Restaurant")
}
Spacer()
.background(Color(.white))
}
)}
MapViewAnnotations(selectedListing: $selectedListing, results: Binding(get: {
return [store]
}, set: {
let idx = results.firstIndex(where: {
store.id == $0.id
})
if idx != nil && $0.first != nil{
results[idx!] = $0.first!
}
}))
}
}
}
struct MapViewParent_Previews: PreviewProvider {
static var previews: some View {
MapViewParent()
}
}
SwiftUI: Interaction between Views
Nevermind, i solved it by myself after @vadian's comment (thank you mate).
ContentView:
import SwiftUI
import MapKit
struct ContentView: View
{
@State private var expandedInfoView: Bool = false
@State private var buttonDecision: Int = 0
var body: some View
{
ZStack(alignment: Alignment(horizontal: .center, vertical: .bottom))
{
GeometryReader
{
proxy in
VStack(alignment: .center)
{
MapView(mapCenter: $buttonDecision)
VStack
{
HStack
{
Spacer()
MapViewControls(mapCenter: $buttonDecision)
.padding(.top, -700)
}
}
LogoView()
.onTapGesture
{
withAnimation
{
expandedInfoView.toggle()
}
}
.padding(.top, -150)
}
.sheet(isPresented: $expandedInfoView)
{
InfoView()
}
}
}
}
}
struct ContentView_Previews: PreviewProvider
{
static var previews: some View
{
ContentView()
}
}
MapView:
import SwiftUI
import MapKit
import CoreLocation
struct MapView: UIViewRepresentable
{
@Binding var mapCenter: Int
let fszCoordinates = CLLocation(latitude: XXX, longitude: YYY)
var locationManager = CLLocationManager()
func setupManager()
{
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.requestAlwaysAuthorization()
}
func makeUIView(context: Context) -> MKMapView
{
setupManager()
let fszAnnotation = MKPointAnnotation()
fszAnnotation.coordinate = CLLocationCoordinate2D(latitude: fszCoordinates.coordinate.latitude, longitude: fszCoordinates.coordinate.longitude)
fszAnnotation.title = "TITLE"
fszAnnotation.subtitle = "SUBTITLE"
let mapView = MKMapView(frame: UIScreen.main.bounds)
mapView.userTrackingMode = .follow
mapView.showsScale = true
mapView.showsCompass = true
mapView.setCenter(CLLocationCoordinate2D(latitude: fszCoordinates.coordinate.latitude, longitude: fszCoordinates.coordinate.longitude), animated: true)
mapView.showAnnotations([fszAnnotation], animated: false)
let buttonLocateUser = MKUserTrackingButton(mapView: mapView)
buttonLocateUser.layer.backgroundColor = UIColor(white: 1, alpha: 0.8).cgColor
buttonLocateUser.layer.borderColor = UIColor.white.cgColor
buttonLocateUser.layer.borderWidth = 1
buttonLocateUser.layer.cornerRadius = 5
buttonLocateUser.translatesAutoresizingMaskIntoConstraints = false
mapView.addSubview(buttonLocateUser)
let scale = MKScaleView(mapView: mapView)
scale.legendAlignment = .trailing
scale.translatesAutoresizingMaskIntoConstraints = false
mapView.addSubview(scale)
NSLayoutConstraint.activate([buttonLocateUser.bottomAnchor.constraint(equalTo: mapView.bottomAnchor, constant: -25),
buttonLocateUser.trailingAnchor.constraint(equalTo: mapView.trailingAnchor, constant: -10),
scale.trailingAnchor.constraint(equalTo: buttonLocateUser.leadingAnchor, constant: -10),
scale.centerYAnchor.constraint(equalTo: buttonLocateUser.centerYAnchor)])
return mapView
}
func updateUIView(_ uiView: MKMapView, context: Context)
{
if ($mapCenter.wrappedValue == 0)
{
uiView.setCenter(CLLocationCoordinate2D(latitude: fszCoordinates.coordinate.latitude, longitude: fszCoordinates.coordinate.longitude), animated: true)
}
else if ($mapCenter.wrappedValue == 1)
{
uiView.setCenter(CLLocationCoordinate2D(latitude: uiView.userLocation.coordinate.latitude, longitude: uiView.userLocation.coordinate.longitude), animated: true)
}
}
}
struct MapView_Previews: PreviewProvider
{
@State static var fszCoordinates = 0
static var previews: some View
{
MapView(mapCenter: $fszCoordinates)
}
}
MapViewControls:
import SwiftUI
struct MapViewControls: View
{
@Binding var mapCenter: Int
var body: some View
{
VStack(spacing: 6)
{
VStack(spacing: 12)
{
Button
{
buttonActionZoomToFSZIT()
}
label:
{
Image(systemName: "house.circle")
}
Divider()
Button
{
buttonActionZoomToUser()
}
label:
{
Image(systemName: "location.circle")
}
}
.frame(width: 40)
.padding(.vertical, 12)
.background(Color(UIColor.secondarySystemGroupedBackground))
.cornerRadius(8)
}
.font(.system(size: 20))
.foregroundColor(.blue)
.padding()
.shadow(color: Color(UIColor.black.withAlphaComponent(0.1)), radius: 4)
}
func buttonActionZoomToFSZIT()
{
self.mapCenter = 0
}
func buttonActionZoomToUser()
{
self.mapCenter = 1
}
}
struct MapViewControls_Previews: PreviewProvider
{
@State static var fszCoordinates = 0
static var previews: some View
{
MapViewControls(mapCenter: $fszCoordinates)
}
}
SwiftUI Views blocking touches to MapKit but not other views
Yes, even transparent images do not allow hit-through so far. In your case, simple enough, the possible approach is to create custom shape, like below. Shapes do pass hits.
Here is simple demo of shape to show the direction.
struct Cross: Shape {
func path(in rect: CGRect) -> Path {
return Path { path in
path.move(to: CGPoint(x: rect.midX, y: 0))
path.addLine(to: CGPoint(x: rect.midX, y: rect.maxY))
path.move(to: CGPoint(x: 0, y: rect.midY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.midY))
path.move(to: CGPoint(x: rect.midX, y: rect.midY))
path.addArc(center: CGPoint(x: rect.midX, y: rect.midY), radius: 10, startAngle: Angle(degrees: 0), endAngle: Angle(degrees: 360), clockwise: false)
}
}
}
struct ContentView: View {
var body: some View {
ZStack {
MapView()
Cross().stroke(Color.red)
.frame(width: 90, height: 90)
}
}
}
Dismiss a map view (MKMapView) in SwiftUI
I assume you meant this...
struct DemoView: View {
@State private var showMap = true
var body: some View {
ZStack {
if showMap {
MapView(centerCoordinate:
Binding(get: {CLLocationCoordinate2D(
latitude: self.refSpot.userLatitude,
longitude: self.refSpot.userLongitude)},
set: {newValue in}))
.edgesIgnoringSafeArea(.all)
}
Button(action: {
self.showMap.toggle()
}) {
VStack {
HStack {
Image(uiImage: UIImage(named: "Back.png")!)
.font(.title)
.foregroundColor(.gray)
Spacer()
}
Spacer()
}
}
}
}
}
Map and Scrollview in SwiftUI
ScrollView
takes all available space and doesn't lend size constraints to child views. Because Map
acts similarly, with no size constraints from the parent, within a ScrollView
, it can just collapse into nothing (eg a frame of 0,0
).
To fix this, you can add an explicit frame
modifier to the map to tell it what size to be:
Map(...) { ... }.frame(width: 300, height: 300)
Related Topics
How to Increase the Scope of Variables in Switch-Case/Loops in Swift
How to Disable "Save to Files" in iOS 11
Metal Kernels Not Behaving Properly on the New MACbook Pro (Late 2016) Gpus
Navigationview Bar Material Invisible on iOS 15
Ios15 Uttype Deprecations for Url-Extension
Google Sign-In via Firebase: Gidsignindelegate Does Not Conform to Viewcontroller
Saving a Codable Struct to Userdefaults with Swift
Why Are Uiscreen.Bounds Incorrect in iOS11
Argument of '#Selector' Does Not Refer to an '@Objc' Method, Property or Initializer
How to Get Frame Data in Apprtc iOS App for Video Modifications
Swift: Specialize Method of Generic Class for Function Types
Change the Font of a Datepicker
Generic and (Early) Binding in Swift 1.2
Swift: Print Name of a Function Stored in a Variable
Swift: Nstextfield Allow Only Specified Characters
Error "No Such Module" When Installed Framework with Pod in Swift 3