1
votes

I am trying to make a SwiftUI View expand its available space. I have a MapView which uses the MapKit to display a map on the screen. I would like this map to expand the available space in the VStack. You can see it is above a view colored red, and below a search bar. If i make the red colored view have a height of 100, then the MapView shrinks down. If I do not set the height on the red colored view, then the MapView is bigger however the red view does not look as I want.

I want the red view to have a height of 100, and the MapView to fill all available height underneath the search bar, and above the red view.

ContentView:

struct ContentView: View {

@ObservedObject
var viewModel: HomeViewModel

var body: some View {
    NavigationView {
        ZStack {
            ColorTheme.brandBlue.value.edgesIgnoringSafeArea(.all)
            VStack {
                CardView {
                    EditText(hint: "Search", text: self.$viewModel.searchText, textContentType: UITextContentType.organizationName)
                }
                MapView(annotations: self.$viewModel.restaurantAnnotations)
                    .cornerRadius(8)
                CardView(height: 100) {
                    HStack {
                        Color.red

                    }
                }
            }
        }
        .navigationBarTitle("", displayMode: .inline)
        .navigationBarHidden(true)
        .navigationBarBackButtonHidden(true)
    }
}
}

CardView

struct CardView<Content>: View where Content : View {
var height: CGFloat = .infinity
var content: () -> Content
var body: some View {
    content()
        .padding(EdgeInsets.init(top: 0, leading: 8, bottom: 8, trailing: 8))
    .background(Color.white.cornerRadius(8))
        .shadow(radius: 2, x: 0, y: 1)
        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: height)
}
}

EditText

struct EditText: View {

var hint: String
@Binding
var text: String
var label: String = ""
var textContentType: UITextContentType? = .none
var keyboardType: UIKeyboardType = .default
var textSize: CGFloat = 16

var body: some View {
    return VStack(alignment: .leading) {
        Text(label).font(.system(size: 12)).bold()
            .foregroundColor(ColorTheme.text.value)
        HStack {
            TextField(hint, text: $text)
                .lineLimit(1)
                .font(.system(size: textSize))
                .textContentType(textContentType)
                .keyboardType(keyboardType)
                .foregroundColor(ColorTheme.text.value)
        }
        Divider().background(ColorTheme.brandBlue.value)
    }
}
}

MapView

struct MapView: UIViewRepresentable {

@Binding
var annotations: [MKAnnotation]

func makeUIView(context: Context) -> MKMapView {
    let mapView = MKMapView()
    mapView.delegate = context.coordinator
    return mapView
}


func updateUIView(_ view: MKMapView, context: Context) {
    view.delegate = context.coordinator
    view.addAnnotations(annotations)
    if annotations.count == 1 {
        let coords = annotations.first!.coordinate
        let region = MKCoordinateRegion(center: coords, span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1))
        view.setRegion(region, animated: true)
    }

}

func makeCoordinator() -> MapViewCoordinator {
    MapViewCoordinator(self)
}
}

MapViewCoordinator

class MapViewCoordinator: NSObject, MKMapViewDelegate {

var mapViewController: MapView

init(_ control: MapView) {
    self.mapViewController = control
}


func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?{
    //Custom View for Annotation
    let identifier = "Placemark"
    if  let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) {
        annotationView.annotation = annotation
        return annotationView
    } else {
        let annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
        annotationView.isEnabled = true
        annotationView.canShowCallout = true
        let button = UIButton(type: .infoDark)
        annotationView.rightCalloutAccessoryView = button
        return annotationView
    }

    return nil
}
}

As you can see the MapView does not fill the available space Map View not filling available space

Now all the available space is filled but the red view height is not 100 All space filled but red view height is not 100

1

1 Answers

1
votes

Here is a solution (tested with Xcode 11.4):

struct CardView<Content>: View where Content : View {
    var height: CGFloat? = nil // << here !!

Note: defined height, even with .infinity, made your upper card equivalent by requesting height to map view, so they divided free space; when height is not specified, the component tights to content