Search code examples
iosswiftuicollectionviewmkmapviewuicollectionviewcell

Preloading MKMapView in collection view


I'm trying to make "tab" experience. Each tab has width of screen and user can swipe between each of them. Everything is working just fine, except that on tab with index "3" MKMapView is implemented.

enter image description here

When user open app, collection view is initialized to index "1", also cellForIndexAtIndexPath is called for IndexPath(row: 1, section: 0)

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PageIndentifier, for: indexPath) as! TabCell

    if (indexPath.item == 0)
    {
        cell.SetupNearestView()
        NearestView = cell.nearestView
        NearestView?.Delegate = self
    }

    if (indexPath.item == 1)
    {
        cell.SetupStationsView()
        StationsView = cell.stationsView
        StationsView?.Delegate = self
    }

    if (indexPath.item == 2)
    {
        cell.SetupMapView()
        MapView = cell.stationsMapView
    }

    return cell
}

Issue is that UI is blocked for about second when user swipe from 1 -> 2 then collection view triggers cellForIndexAtIndexPath is called for IndexPath(row: 2, section: 0). I was trying "force" load cell at that index but unfortunatelly, map seems to be initialized just before rendering on screen. Is there any workaround for that?

edit:

TabCell.swift

class TabCell : UICollectionViewCell
{
var nearestView : NearestView?
var stationsView : StationsView?
var stationsMapView : MapView?

override init(frame: CGRect)
{
    super.init(frame: frame)

    let gradient = CAGradientLayer()
    gradient.frame = bounds
    gradient.colors = [UIColor.clear, RuntimeResources.Get(color: .VeryDarkBlue).cgColor]
    layer.insertSublayer(gradient, at: 0)
}

required init?(coder aDecoder: NSCoder)
{
    fatalError("init(coder:) has not been implemented")
}

func SetupNearestView()
{
    if (nearestView != nil)
    {
        return
    }

    nearestView = NearestView(frame: CGRect(x: 0, y: 0, width: frame.width, height: frame.height))
    addSubview(nearestView!)

    addConstraintsWithFormat(format: "H:|[v0]|", views: nearestView!)
    addConstraintsWithFormat(format: "V:|[v0]|", views: nearestView!)

}

func SetupMapView()
{
    if (stationsMapView != nil)
    {
        return
    }

    stationsMapView = MapView(frame: CGRect(x: 0, y: 0, width: frame.width, height: frame.height))
    addSubview(stationsMapView!)

    addConstraintsWithFormat(format: "H:|[v0]|", views: stationsMapView!)
    addConstraintsWithFormat(format: "V:|[v0]|", views: stationsMapView!)
}

func SetupStationsView()
{
    if (stationsView != nil)
    {
       return
    }

    stationsView = StationsView(frame: CGRect(x: 0, y: 0, width: frame.width, height: frame.height))
    addSubview(stationsView!)

    addConstraintsWithFormat(format: "H:|[v0]|", views: stationsView!)
    addConstraintsWithFormat(format: "V:|[v0]|", views: stationsView!)
}
}

MapView.swift

import UIKit
import MapKit

class MapView: MKMapView, MKMapViewDelegate
{
private let infoViewHeight = CGFloat(500)
private let FocusZoomLevel = 0.01
private let ZoomLevel = 0.04
private let centerOfSzczecin = CLLocationCoordinate2D(latitude: 53.433345, longitude: 14.544139)
private var infoViewTopConstraint : NSLayoutConstraint?

lazy var mainStationView : UIView = {

    let msv = UIView(frame: CGRect(x: 0, y: 0, width: self.frame.width, height: self.infoViewHeight))
    msv.translatesAutoresizingMaskIntoConstraints = false
    msv.backgroundColor = RuntimeResources.Get(color: .DarkBlue)
    return msv
}()

override init(frame: CGRect)
{
    super.init(frame: frame)
    delegate = self

    // Adding info view
    SetupInfoView()

    // Setting center of map
    let span = MKCoordinateSpanMake(ZoomLevel, ZoomLevel)
    let region = MKCoordinateRegion(center: centerOfSzczecin, span: span)
    setRegion(region, animated: false)
}

required init?(coder aDecoder: NSCoder)
{
    fatalError("init(coder:) has not been implemented")
}

private func SetupInfoView()
{
    addSubview(mainStationView)
    infoViewTopConstraint = mainStationView.topAnchor.constraint(equalTo: self.bottomAnchor)
    infoViewTopConstraint?.isActive = true
    mainStationView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
    mainStationView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
    mainStationView.heightAnchor.constraint(equalToConstant: infoViewHeight).isActive = true
}

}

Solution

  • Since you are not reusing the MapView cell.

    Try loading your mapView as a variable outside of your UICollectionViewCell (In your UIViewController) and add it as a subView to the cell in collectionView:willDisplayCell:forItemAtIndexPath:

    and in collectionView:didEndDisplayingCell:forItemAtIndexPath: you can remove it from the cell.

    Simply load all your mapView code outside of your cell and just use the Cell to display the map as a subview that is.