Migrate to v10 | Maps SDK | iOS | Mapbox

文章推薦指數: 80 %
投票人數:10人

Upgrade your application from the Mapbox Maps SDK for iOS v6 to v10. ... The OfflineManager API can be used to create offline style packs that contain style ... Youareusinganoutdatedbrowserandwillencountersomeproblemswithourwebsite.Pleaseconsiderupgrading.UpgradeNowMapsSDKforiOSSearchGuidesExamplesAPIReferenceTutorialsTroubleshootingAlldocsMapsSDKforiOSGuidesMigratetov10Migratetov10OnthispageRequirementsNewMapViewStructuredAPIsurfaceResourceOptionsconfiguration(includingaccesstoken)DisplayamapStyleloadingfunctionsListeningtothemap'slifecycleMapoptionsConfigurethemapModulararchitectureExample:ReplaceableHTTPstackCameraManipulatethecameraSetthemap’scameraFitthecameratoagivenshapeConfigurethedefaultbehaviorofthecameraMapstylesStyleSpecificationType-safeAPILocalizationExpressionsAnnotationsPointannotationsLineannotationsPolygonannotationsSelectingannotationsUserlocationRequestingpermissionsShowingthedevice'slocationFollowingthedevice'slocationCustomLocationProviderHandlingprivacyNewfeaturesandimprovementsPlatform-drivencameraanimationsystem3DterrainandskylayersCustomrenderedlayersNewOfflineManagerDeprecationsandremovalsShareyourfeedbackTheMapboxMapsSDKv10introducesimprovementstohowMapboxworksoniOSplatforms,aswellaschangestohowdevelopersusetheSDK.ThisdocumentsummarizesthemostimportantchangesandwalksyouthroughhowtoupgradeanapplicationusingapreviousversionoftheMapboxMapsSDKtov10.RequirementsXcode12.2+Swiftversion5.3+iOS11orgreaterNoteStartingwithv10.0.0-beta.15,buildingwithAppleSiliconissupported.NewMapViewStructuredAPIsurfaceTheMapsSDKv10aimstoprovideasinglepointofentrytoallmap-relatedtasks.TheAPIinterfaceofthenewMapViewisnowhierarchicalinnature.Methodsarestrictlyseparatedbasedontheircomponents(forexampleMapboxMap,Style,CameraandLocation).AllcapabilitiesrelatedtothemapstylewillbepartofthestylepropertyoftheMapView.Othercapabilitieslikeannotationsoruserlocationhavetheirowncorrespondingpropertiesthatencapsulatetheirfeatureset.ResourceOptionsconfiguration(includingaccesstoken)Inpre-v10theaccesstokenwasconfiguredeitherusingtheInfo.plistkeyMGLMapboxAccessTokenorbyprogrammaticallysettingMGLAccountManager.accessToken.Inv10,anewResourceOptionsManagerhasbeenintroducedtomanagetheapplication-scopedResourceOptions,whichincludesanaccesstoken.ThesharedResourceOptionsManager.defaultisanapplicationwideconvenience.It'susedbydefaultbyMapInitOptions(andinturnbyMapView.init())ifyoudonotprovideacustomResourceOptions.Youshouldusethisconveniencetosetanapplicationwideaccesstoken:funcapplication(_application:UIApplication,didFinishLaunchingWithOptionslaunchOptions:[UIApplication.LaunchOptionsKey:Any]?)->Bool{ //Overridepointforcustomizationafterapplicationlaunch. ResourceOptionsManager.default.resourceOptions.accessToken="my-application-access-token" returntrue } Ifyouchoosenottosetthetokenprogrammatically,thesharedinstancewilllookintheapplication'sInfo.plistforthevalueassociatedwiththeMBXAccessTokenkey.YoucancreateResourceOptionsManagersasyouneedthem.ThiscanbeusefulifyouneedamechanismtoshareResourceOptionsbetweenpartsofyourapplication.NoteIfyouareworkingwithStoryboardsorXibs,ResourceOptionsManager.defaultwillbeusedwhencreatingtherequiredMapInitOptions.IfyouwishtoprovideacustomMapInitOptions(whichincludestheResourceOptions),youcanconnectacustomtypeconformingtotheMapInitOptionsProviderprotocoltothemapInitOptionsProviderIBOutlet.ThisgivesyouamechanismtooverridetheaccesstokenusedbyaMapView.DisplayamapTheprimaryinterfaceinv10istheMapView,which,likeinearlierversionsoftheSDK,isaUIView.MapboxMaphasalsobeenintroduced,whichgrantsaccesstomapproperties(asopposedtoUIViewassociatedproperties).TheMapView.mapboxMappropertyistheequivalentofAndroid'sgetMapboxMap()function.Theexamplebelowdisplaysafull-screenmapwithadefaultstyle.InitializingaMapViewcreatesadefaultsetofmapoptions,providingastrongfoundation.pre-v10:importUIKit importMapbox classBasicMapViewController:UIViewController{ varmapView:MGLMapView! overridefuncviewDidLoad(){ super.viewDidLoad() mapView=MGLMapView(frame:view.bounds) view.addSubview(mapView) } } v10:importUIKit importMapboxMaps classBasicMapViewController:UIViewController{ varmapView:MapView! varaccessToken:String! overridefuncviewDidLoad(){ super.viewDidLoad() ResourceOptionsManager.default.resourceOptions.accessToken=self.accessToken mapView=MapView(frame:view.bounds) view.addSubview(mapView) } } Theresultwilllooklikethis:StyleloadingfunctionsYoucanseparatestyleloadingfrommapviewcreationusingthenewloadStyleURIandloadStyleJSONfunctions.ThesehaveconvenientclosurestoreturntheloadedStyleandanymaploadingerror.FunctionDescriptionloadStyleURI(_:completion:)LoadanewmapstyleasynchronousfromthespecifiedURI.loadStyleJSON(_:completion:)LoadstylefromaJSONstring.Here'sanexampleillustratinghowtodelayloadingamapstyleusingastyleURL://PassanilStyleURItostopthemapfromloadingadefaultstyle mapView=MapView(frame:bounds,mapInitOptions:MapInitOptions(styleURI:nil)) ... mapView.mapboxMap.loadStyleURI(.streets){resultin switchresult{ case.success(letstyle): print("Themaphasfinishedloadingthestyle") //Dosomethingwith`style` caselet.failure(error): print("Themapfailedtoloadthestyle:\(error)") } } Listeningtothemap'slifecycleTheMaphasseveralstagestoitslifecycleandcommunicatestransitionsviaacallbackmechanism.ThiscallbackmechanismreplacestheMGLMapViewDelegateprotocolseeninpre-v10versionsoftheMapsSDK.Thecontainerviewcontrollerinthelastexamplecanlistenforwhenthemaphasfinishedloadingusingthefollowingcode:classBasicMapViewController:UIViewController{ varmapView:MapView! varaccessToken:String! varhandler:((Event)->Void)? overridefuncviewDidLoad(){ super.viewDidLoad() ResourceOptionsManager.default.resourceOptions.accessToken=self.accessToken mapView=MapView(frame:view.bounds) view.addSubview(mapView) /** Theclosureiscalledwhenstyledatahasbeenloaded.Thisiscalled multipletimes.Usetheeventdatatodeterminewhatkindofstyledata hasbeenloaded. Whenthetypeis`style`thiseventmostcloselymatches `-[MGLMapViewDelegatemapView:didFinishLoadingStyle:]`inSDKversions priortov10. */ mapView.mapboxMap.onEvery(.styleDataLoaded){[weakself](event)in guardletdata=event.dataas?[String:Any], lettype=data["type"], lethandler=self?.handlerelse{ return } print("Themaphasfinishedloadingstyledataoftype=\(type)") handler(event) } /** Theclosureiscalledduringtheinitializationofthemapviewand afterany`styleDataLoaded`events;itiscalledwhentherequested stylehasbeenfullyloaded,includingthestyle,specifiedspriteand sourcemetadata. Thiseventisthelastopportunitytomodifythelayoutorappearance ofthecurrentstylebeforethemapviewisdisplayedtotheuser. Addingalayeratthistimetothemapmayresultinthelayerbeing presentedBEFOREtherestofthemaphasfinishedrendering. Changestosourcesorlayersofthecurrentstyledonotcausethis eventtobeemitted. */ mapView.mapboxMap.onNext(.styleLoaded){(event)in print("Themaphasfinishedloadingstyle...Event=\(event)") self.handler?(event) } /** Theclosureiscalledwheneverthemapfinishesloadingandthemaphas renderedallvisibletiles,eitheraftertheinitialloadORaftera stylechangehasforcedareload. Thisisanidealtimetoaddanyruntimestylingorannotationstothe mapandensuresthattheselayerswouldonlybeshownafterthemaphas beenfullyrendered. */ mapView.mapboxMap.onNext(.mapLoaded){(event)in print("Themaphasfinishedloading...Event=\(event)") self.handler?(event) } /** Theclosureiscalledwheneverthemapviewisenteringanidlestate, andnomoredrawingwillbenecessaryuntilnewdataisloadedorthere issomeinteractionwiththemap. -Allcurrentlyrequestedtileshavebeenrendered -Allfade/transitionanimationshavecompleted */ mapView.mapboxMap.onNext(.mapIdle){(event)in print("Themapisidle...Event=\(event)") self.handler?(event) } /** Theclosureiscalledwheneverthemaphasfailedtoload.Thiscould bebecauseofavarietyofreasons,includinganetworkconnection failureorafailuretofetchthestylefromtheserver. Youcanusetheassociatederrormessagetonotifytheuserthatmap dataisunavailable. */ mapView.mapboxMap.onNext(.mapLoadingError){(event)in guardletdata=event.dataas?[String:Any], lettype=data["type"], letmessage=data["message"]else{ return } print("Themapfailedtoload..\(type)=\(message)") } } } It'sworthnotingthattheclosestequivalentofpre-v10's-[MGLMapViewDelegatemapView:didFinishLoadingStyle:]isthe.styleDataLoadedeventwhenitsassociatedtypeisstyle,thoughwerecommendedusingtheMapboxMap.loadStyleURI()functioninstead.loadStyleURI()listensforthe.styleLoadedevent.Thefollowingsimplifieddiagramhelpsexplaintheeventlifecycle: ┌─────────────┐┌─────────┐┌──────────────┐ │Application││Map││ResourceLoader│ └──────┬──────┘└────┬────┘└───────┬──────┘ │││ ├─────SetstyleURL──────▶││ │├───────────getstyle───────────▶│ │││ ││◀─────────styledata────────────┤ │││ │├─parsestyle─┐│ ││││ │styleDataLoaded◀─────────────┘│ │◀────{"type":"style"}─────┤│ │├─────────getsprite────────────▶│ │││ ││◀────────spritedata────────────┤ │││ │├──────parsesprite───────┐│ ││││ │styleDataLoaded◀─────────────────────────┘│ │◀───{"type":"sprite"}─────┤│ │├─────getsourceTileJSON(s)────▶│ │││ │sourceDataLoaded│◀─────parseTileJSONdata───────┤ │◀──{"type":"metadata"}────┤│ │││ │││ │styleDataLoaded││ │◀───{"type":"sources"}────┤│ │├──────────gettiles────────────▶│ │││ │◀───────styleLoaded────────┤│ │││ │sourceDataLoaded│◀─────────tiledata─────────────┤ │◀────{"type":"tile"}──────┤│ │││ │││ │◀────renderFrameStarted────┤│ │├─────render─────┐│ ││││ │◀────────────────┘│ │◀───renderFrameFinished────┤│ │├──render,alltilesloaded──┐│ ││││ │◀────────────────────────────┘│ │◀────────mapLoaded─────────┤│ │││ │││ │◀─────────mapIdle──────────┤│ │┌────┴────┐│ ││offline││ │└────┬────┘│ │││ ├────────Setcamera──────▶││ │├───────────gettiles───────────▶│ │││ ││┌──────────────│ │◀─────────mapIdle──────────┤waitingforconnectivity││ │││Maprenderscacheddata│ ││──────────────┘│ │││ MapoptionsConfigurethemapThemapcreatedintheexampleaboveisinitializedwithdefaultconfigurationsformapelementssuchasgestures,ornaments,andthemap'scamera.Themap'sconfigurationscanbeupdatedviatheMapViewanditsmapboxMapproperty.pre-v10:Inpre-v10versionsoftheMapsSDK,optionsweresetontheMGLMapVieworusinganMGLMapViewDelegatemethod.privatevarrestrictedBounds:MGLCoordinateBounds! overridefuncviewDidLoad(){ super.viewDidLoad() letmapView=MGLMapView(frame:view.bounds) mapView.delegate=self mapView.showsScale=YES; view.addSubview(mapView) } funcmapView(_mapView:MGLMapView,shouldChangeFromoldCamera:MGLMapCamera,tonewCamera:MGLMapCamera)->Bool{ letcurrentCamera=mapView.camera letnewCameraCenter=newCamera.centerCoordinate mapView.camera=newCamera letnewVisibleCoordinates=mapView.visibleCoordinateBounds mapView.camera=currentCamera //TestifthenewCameraCenterandnewVisibleCoordinatesareinsideself.restrictedBounds. letinside=MGLCoordinateInCoordinateBounds(newCameraCenter,self.restrictedBounds) letintersects=MGLCoordinateInCoordinateBounds(newVisibleCoordinates.ne,self.colorado)&&MGLCoordinateInCoordinateBounds(newVisibleCoordinates.sw,self.restrictedBounds) returninside&&intersects } v10:Intheexamplebelow,therestrictedboundsaresetdirectlyviatheMapView.mapboxMap.letrestrictedBounds=CoordinateBounds(southwest:CLLocationCoordinate2D(latitude:10,longitude:10), northeast:CLLocationCoordinate2D(latitude:11,longitude:11)) letcameraBoundsOptions=CameraBoundsOptions(bounds:restrictedBounds) //Configuremaptoshowascalebar mapView.ornaments.options.scaleBar.visibility=.visible trymapView.mapboxMap.setCameraBounds(with:cameraBoundsOptions) ModulararchitectureTheMapsSDKv10isbeingdevelopedwithagoalofprovidingamodulararchitecture.Thissetsthefoundationforafutureplugin-basedarchitecturethatcanbeminimizedorextended.Example:ReplaceableHTTPstackYoucansupplycustomimplementationsforsomecomponentsoftheSDK.TheexamplebelowshowsyouhowtoreplacetheHTTPstack.Youmustreplacethestackearlyintheapplicationlifecycle,beforeanyAPIrequestsaremade,andyoushouldcallsetUserDefinedForCustomonceonly.importUIKit importMapboxMaps @UIApplicationMain classAppDelegate:UIResponder,UIApplicationDelegate{ varwindow:UIWindow? letcustomHTTPService=CustomHttpService() funcapplication(_application:UIApplication,didFinishLaunchingWithOptionslaunchOptions:[UIApplication.LaunchOptionsKey:Any]?)->Bool{ HttpServiceFactory.setUserDefinedForCustom(customHTTPService) returntrue } ... CustomHttpServicereferencedinthecodesnippetaboveimplementsHttpServiceInterface.Thefollowingisanexampleofapartialimplementation:classCustomHttpService:HttpServiceInterface{ funcrequest(forrequest:HttpRequest,callback:@escapingHttpResponseCallback)->UInt64{ //MakeanAPIrequest varurlRequest=URLRequest(url:URL(string:request.url)!) letmethodMap:[HttpMethod:String]=[ .get:"GET", .head:"HEAD", .post:"POST" ] urlRequest.httpMethod=methodMap[request.method]! urlRequest.httpBody=request.body urlRequest.allHTTPHeaderFields=request.headers lettask=URLSession.shared.dataTask(with:urlRequest){(data,response,error)in letresult:Result ifleterror=error{ //MapNSURLErrortoHttpRequestErrorType letrequestError=HttpRequestError(type:.otherError,message:error.localizedDescription) result=.failure(requestError) }elseifletresponse=responseas?HTTPURLResponse, letdata=data{ //Keysareexpectedtobelowercase varheaders:[String:String]=[:] for(key,value)inresponse.allHeaderFields{ guardletkey=keyas?String, letvalue=valueas?Stringelse{ continue } headers[key.lowercased()]=value } letresponseData=HttpResponseData(headers:headers,code:Int64(response.statusCode),data:data) result=.success(responseData) }else{ //Error letrequestError=HttpRequestError(type:.otherError,message:"Invalidresponse") result=.failure(requestError) } letresponse=HttpResponse(request:request,result:result) callback(response) } task.resume() //Handleusedtocancelrequests returnUInt64(task.taskIdentifier) } ... CameraManipulatethecameraInv10,themap’scameraishandledbytheCameraAnimationsManager.TheMapViewhasareferencetotheCameraAnimationsManagercalledcamera.CameraOptionsisthev10equivalenttopre-v10'sMGLMapCamera.Themap’sCameraOptionscontainthecentercoordinate,padding,anchor,zoom,bearing,andpitchforamap.Thecameraoptionscanbeconfiguredeitherasawholeorindividually.Themap’scameracanbeconfiguredviaprogrammaticorgesture-basedevents.ThecurrentcamerastateofthemapcanbeaccessedviathemapView'scameraStateproperty.Setthemap’scameraThecameramanagercanbeconfiguredwithaninitialcameraview.Tosetthemap’scamera,firstcreateaCameraOptionsobject,thendirectthemaptouseitviatheMapInitOptions.CameraOptionsparametersareoptional,soonlytherequiredpropertiesneedtobeset.pre-v10:letcenterCoordinate=CLLocationCoordinate2D(latitude:21.3069, longitude:-157.8583) mapView.setCenter(centerCoordinate,zoomLevel:14,direction:0,animated:false) v10://SetthecentercoordinateofthemaptoHonolulu,Hawaii letcenterCoordinate=CLLocationCoordinate2D(latitude:21.3069, longitude:-157.8583) //Createacamera letcamera=CameraOptions(center:centerCoordinate,zoom:14) letoptions=MapInitOptions(cameraOptions:camera) letmapView=MapView(frame:frame,mapInitOptions:options) Youcansetthemap’szoomlevel,pitch,andcentercoordinateprogrammaticallybyinitializingaCameraOptionsobjectwiththosevalues.YoucanthesetthecamerausingmapView.mapboxMap.setCamera()CameravaluesshouldbesetusingtheMapboxMap.setCamera()method,althoughtheycanbeaccessedasread-onlypropertiesonthemapview'scameraState(forexample,MapView.cameraState.centerCoordinate).FitthecameratoagivenshapeIntheMapsSDKv10,theapproachtofittingthecameratoagivenshapelikethatofpre-v10versions.Inv10,callcamera(for:)functionsontheMapboxMaptocreate:acameraforagivengeometry,acamerathatfitsasetofrectangularcoordinateboundsoracamerabasedoffofacollectionofcoordinates.Then,callease(to:)onMapView.cameratovisiblytransitiontothenewcamera.Belowisanexampleofsettingthecameratoasetofcoordinates:pre-v10://Fittingacameratoasetofcoordinatebounds letsw=CLLocationCoordinate2D(latitude:24,longitude:-89) letne=CLLocationCoordinate2D(latitude:26,longitude:-88) letcoordinateBounds=MGLCoordinateBounds(sw:sw,ne:ne) letcamera=mapView.cameraThatFitsCoordinateBounds(coordinateBounds) mapView.setCamera(camera,animated:true) v10:letcoordinates=[ CLLocationCoordinate2DMake(24,-89), CLLocationCoordinate2DMake(24,-88), CLLocationCoordinate2DMake(26,-88), CLLocationCoordinate2DMake(26,-89), CLLocationCoordinate2DMake(24,-89) ] letcamera=mapView.mapboxMap.camera(for:coordinates, padding:.zero, bearing:nil, pitch:nil) mapView.camera.ease(to:camera,duration:10.0) Configurethedefaultbehaviorofthecamerapre-v10Inpre-v10versionsoftheMapsSDK,defaultcamerabehaviorwasconfigureddirectlyonthemapviewusingMGLMapViewpropertieslikeminimumZoomLevelandminimumPitch.TolimitthecameratoaspecificareayouwouldusethemapView:shouldChangeFromCamera:toCamera:methodonMGLMapViewDelegateasshownintheRestrictmappanningtoanareaexample.v10:Inv10,therearenewCameraBoundsandCameraBoundsOptionsstructthatallowsconfigurationofdefaultbehaviorsforthecamerapositions,withtheoptionsoutlinedbelow:publicstructCameraBoundsOptions:Hashable{ ///Thelatitudeandlongitudeboundstowhichthecameracenterareconstrained. publicvarbounds:CoordinateBounds? ///Themaximumzoomlevel,inmapboxzoomlevels0-25.5.Atlowzoomlevels, ///asmallsetofmaptilescoversalargegeographicalarea.Athigher ///zoomlevels,alargernumberoftilescoverasmallergeographicalarea. publicvarmaxZoom:CGFloat? ///Theminimumzoomlevel,inmapboxzoomlevels0-25.5. publicvarminZoom:CGFloat? ///Themaximumallowedpitchvalueindegrees. publicvarmaxPitch:CGFloat? ///Theminimumallowedpitchvaluedegrees. publicvarminPitch:CGFloat? ... TheabovepropertiescanbesetviathesetCameraBounds(with:)functiononMapView.mapboxMap:letsw=CLLocationCoordinate2DMake(-12,-46) letne=CLLocationCoordinate2DMake(2,43) letrestrictedBounds=CoordinateBounds(southwest:sw,northeast:ne) trymapView.mapboxMap.setCameraBounds(with:CameraBoundsOptions(bounds:restrictedBounds, maxZoom:15.0, minZoom:8.0)) Thebiggestchangetothisapproachishowyourestrictthecameratoagivensetofcoordinatebounds,whichyoucannowdousingasimplerfunction-basedapproachinsteadofusingthedelegatemethodoutlinedearlier.MapstylesStyleSpecificationInv10thestyleAPIisalignedwiththeMapboxStyleSpecification.Sources,Layers,andLightallworkintheexactsamemannerasintheStyleSpecification.TheStyleobjectintheMapView.mapboxMapisresponsibleforallfunctionalityrelatedtoruntimestyling.NoteAddsourcesandlayerstothemaponlyafterithasfinishedloadingitsinitialstyle.Usethelifecyclecallbackstobeinformedofwhenthestylehasfinishedloading.Type-safeAPIEverySourceandLayerdeclaredintheStyleSpecificationexistsinv10asanexactly-typedSwiftstruct.EverypropertywithinthosesourcesandlayersismodeledusingfamiliarSwifttypes.ThismeansthatcreatingnewsourcesandlayersshouldfeelfamiliartoiOSdeveloperswhowriteinSwift.Example:GeoJSONSourceForexample,aminimalGeoJSONsourcecanbecreatedusingthefollowingcode:varmyGeoJSONSource=GeoJSONSource() myGeoJSONSource.maxzoom=14 GeoJSONsourceshaveadatapropertythatyoucansettoeitheraURLorinlineGeoJSON.TheSwiftstructrepresentingaGeoJSONsourceismodeled1:1withthisexpectation.TheSDKusesturf-swifttomodelGeoJSON.ThismeansthatcraftingGeoJSONatruntimeisbothstraightforwardandbackedbythetype-safetyandcodableconformancethatTurfprovides.//Settingthe`data`propertywithaurlpointingtoaGeoJSONdocument myGeoJSONSource.data=.url(someGeoJSONDocumentURL) //SettingaTurffeaturetothe`data`property myGeoJSONSource.data=.featureCollection(someTurfFeatureCollection) TheMapView.mapboxMapholdsastyleobjectthatcanbeusedtoadd,remove,orupdatesourcesandlayers.trymapView.mapboxMap.style.addSource(myGeoJSONSource,id:"my-geojson-source") pre-v10:ThisfunctionalitypreviouslyrequiredcreationofanMGLShapeSource:letshapeSource=MGLShapeSource(identifier:"my-geojson",url:URL(string:""),options:nil) self.mapView.addSource(shapeSource) NoteUpdatingGeoJSONsourceshappensdifferentlyinv10.You’llneedtocallstyle.updateGeoJSONSource(withId:geoJSON:)toupdatethedatabelongingtoaGeoJSONSource.Example:BackgroundlayerAddingabackgroundlayerfurtherdemonstratesthetype-safetyprovidedinv10.Asmentionedearlier,allLayersarealsoSwiftstructs.Thefollowingcodesetsupabackgroundlayerandsetsitsbackgroundcolortored:varmyBackgroundLayer=BackgroundLayer(id:"my-background-layer") myBackgroundLayer.backgroundColor=.constant(StyleColor(.red)) Oncealayeriscreated,addittothemap:trymapView.mapboxMap.style.addLayer(myBackgroundLayer) Localizationv10allowsdeveloperstochangethelanguagedisplayedbyamapstylebasedonagivenlocale.Thiscapabilitycanbeenabledwiththefollowingcode://ChangeslocaletoSpanish mapView.mapboxMap.style.localizeLabels(into:Locale(identifier:"en")) Thereisalsoanopportunitytoprovideanarrayoflayeridssothatonlyasubsetoflayerswillbelocalized.Thiscanbeachievedwiththefollowingcode://ChangesallcountrylabelstoJapanese mapView.mapboxMap.style.localizeLabels(into:Locale(identifier:"ja"),forLayerIds:["country-label"]) ExpressionsInthebackgroundlayerexampleabove,thebackgroundColorpropertyofthelayerissettoaconstantvalue.However,thebackgroundColorproperty(andalllayoutandpaintproperties)alsosupportexpressions.Inv10,expressionshavebeenredesignedfromthegroundup.ThenewExpressiondomain-specificlanguage(DSL)directlymodelsexpressionsbasedontheMapboxStyleSpecificationandremovesthedependencyonNSExpression.Inv10,expressionsexistasfamiliarandtype-safeSwiftstructs.TheyarealsobackedbySwiftfunctionbuilderstomaketheexperienceofwritinganexpressionlikethewaySwiftUIworks.ConsideraninterpolateexpressionwritteninrawJSON:[ "interpolate", ["linear"], ["zoom"], 0, "hsl(0,79%,53%)", 14, "hsl(233,80%,47%)" ] IfthisexpressionisappliedtothebackgroundColorpaintpropertyofalayer(the"land"layer,intheexamplescreenshotabove),thenthebackgroundColorwouldbeinterpolatedfromredtobluebasedonthezoomlevelofthemap'scamera.pre-v10:Inpre-v10versionsoftheMapsSDK,theexpressionwouldbewrittenlikethis:letcolorStops:[NSNumber:UIColor]=[ 0:.red, 14:.blue ]; layer.backgroundColor=NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel,'linear',nil,%@)", colorStops]; v10:IntheMapsSDKv10,aJSONexpressionfromMapboxStudiocanbeuseddirectlyorcanbetranslatedtoSwift.TousetheJSONexpressiondirectly:letexpressionString= """ [ "interpolate", ["linear"], ["zoom"], 0, "hsl(0,79%,53%)", 14, "hsl(233,80%,47%)" ] """ ifletexpressionData=expressionString.data(using:.utf8){ letexpJSONObject=tryJSONSerialization.jsonObject(with:expressionData,options:[]) trymapView.mapboxMap.style.setLayerProperty(for:"land", property:"background-color", value:expJSONObject) } TotranslatethesameJSONexpressiontoSwift:Exp(.interpolate){ Exp(.linear) Exp(.zoom) 0 UIColor.red 14 UIColor.blue } UsethefullpowerofSwiftandtheiOSruntimewhendefiningthisexpression.Forexample,toadjustthisexpressionbasedonwhethertheuserhasdarkmodeenabled:varisDarkModeEnabled=traitCollection.userInterfaceStyle==.dark?true:false letlowZoomColor=isDarkModeEnabled?UIColor.black.expression:UIColor.red lethighZoomColor=isDarkModeEnabled?UIColor.grey.expression:UIColor.blue Exp(.interpolate){ Exp(.linear) Exp(.zoom) 0 lowZoomColor 14 highZoomColor } ComposeexpressionsInv10,expressionscanalsobecomposedandbuiltinamodularway.Theycanbeinfinitelynestedordefinedseparatelyandaddedtothestack.Exp(.interpolate){ Exp(.linear) Exp(.zoom) Exp(.subtract){ 10 3 } UIColor.red Exp(.sum){ 7 7 } UIColor.blue } AnnotationsYoucanadd"annotations"tothemapusingpoint,line,polygonandcircleshapeswiththeMapView’sAnnotationOrchestrator.UsetheAnnotationOrchestratortocreateannotationmanagersbasedonthetypeofannotationthatyou'reinterestedin.Everyannotationmanagerisresponsibleforacollectionofannotations.Onceamanagerhasbeencreated,youcancreateandaddindividuallystyledinstancesofthecorrespondingannotationtype.Annotationswerealsosupportedpre-v10bywayofMGLShapeobjectsthatcouldconformtotheMGLAnnotationprotocol.Forexample,addingasinglepointannotationthatmarksacoordinatewithanimagepreviouslylookedlikethis:pre-v10classViewController:UIViewController,MGLMapViewDelegate{ overridefuncviewDidLoad(){ super.viewDidLoad() ... mapView.delegate=self letpointAnnotation=MGLPointAnnotation() pointAnnotation.coordinate=CLLocationCoordinate2D(latitude:43,longitude:10) mapView.addAnnotation(pisa) } funcmapView(_mapView:MGLMapView,imageForannotation:MGLAnnotation)->MGLAnnotationImage?{ varcustomImage=UIImage(named:"star")! returnMGLAnnotationImage(image:customImage,reuseIdentifier:"star") } } IntheMapsSDKv10,thedelegate-basedapproachisnolongerused,movinginfavorofcustomSwiftstructstorepresentannotationsthatcanbecustomizedwithaproperty-basedapproach.Thelargestchangetoannotationsisthattheyareallpoweredinternallybystylelayers.Viewannotationsavailableinv10.2.0andhigherInv6,viewannotationswerepossiblewiththeMGLAnnotationViewclass.Inv10,viewannotationsareonlyavailableinv10.2.0andhigher.FormoredetailsseetheViewannotationsguide.Seecodeexamplesforimplementingpoint,line,polygonandcircleannotationsinMapsSDKv10below.PointannotationsApointannotationisamarkerthatisplacedatadeveloper-specifiedcoordinate://Makea`PointAnnotationManager`whichwillberesponsibleformanagingacollectionof`PointAnnotion`s. letpointAnnotationManager=mapView.annotations.makePointAnnotationManager() //Initializeapointannotationwithageometry("coordinate"inthiscase) //andconfigureitwithacustomimage(sourcedfromtheassetcatalogue) varcustomPointAnnotation=PointAnnotation(coordinate:customCoordinate) //Maketheannotationshowaredpin.See/ios/maps/examples/point-annotation/foracompleteexample. customPointAnnotation.image=.init(image:UIImage(named:"red_pin")!,name:"red_pin") //Addtheannotationtothemanagerinordertorenderitonthemao. pointAnnotationManager.annotations=[customPointAnnotation] ThisresultsinamarkeratthecustomCoordinatespecified.ThePointAnnotationManagercreatedabovealsocontrolsthelifecycleoftheannotationsinitspurview.AnnotationmanagersarekeptalivebytheAnnotationOrchestratoruntiltheyareremovedexplicitlyviaacalltoremoveAnnotationManager(withId:)orimplicitlybycreatinganotherannotationmanagerwiththesameID.LineannotationsAlineannotationconnectsalistofcoordinatesonthemapwithaline.SimilartothePointAnnotationexample,youmustcreateinstancesofLineAnnotationswithsomegeometryandthenaddittoaLineAnnotationManager://LinefromNewYorkCity,NYtoWashington,D.C. letlineCoordinates=[ CLLocationCoordinate2DMake(40.7128,-74.0060), CLLocationCoordinate2DMake(38.9072,-77.0369) ] //Createthelineannotation. varlineAnnotation=PolylineAnnotation(lineCoordinates:lineCoordinates) //Customizethestyleofthelineannotation lineAnnotation.lineColor=StyleColor(.red) lineAnnotation.lineOpacity=0.8 lineAnnotation.lineWidth=10.0 //CreatethePolylineAnnotationManagerresponsibleformanaging //thislineannotations(andothersifyousochoose) letlineAnnnotationManager=mapView.annotations.makePolylineAnnotationManager() //Addtheannotationtothemanager. lineAnnnotationManager.annotations=[lineAnnotation] PolygonannotationsTheAnnotationManagercanalsoaddapolygontothemapbytakingalistofcoordinatesthatitwillthenattempttoconnect.Youcanalsocreateapolygonwithanemptyspaceinthemiddle(likeadoughnut)bytakingaseparatelistofcoordinates.TheorderofthecoordinatesinthelistmatterandshouldconformtotheGeoJSONspecification.//Describethepolygon'sgeometry letouterRingCoords=[ CLLocationCoordinate2DMake(24.51713945052515,-89.857177734375), CLLocationCoordinate2DMake(24.51713945052515,-87.967529296875), CLLocationCoordinate2DMake(26.244156283890756,-87.967529296875), CLLocationCoordinate2DMake(26.244156283890756,-89.857177734375), CLLocationCoordinate2DMake(24.51713945052515,-89.857177734375) ] //Thispolygonhasanintereriorpolygonwhichrepresentsaholeintheshape. letinnerRingCoords=[ CLLocationCoordinate2DMake(25.085598897064752,-89.20898437499999), CLLocationCoordinate2DMake(25.085598897064752,-88.61572265625), CLLocationCoordinate2DMake(25.720735134412106,-88.61572265625), CLLocationCoordinate2DMake(25.720735134412106,-89.20898437499999), CLLocationCoordinate2DMake(25.085598897064752,-89.20898437499999) ] ///CreatethePolygonwiththeouterringandinnerring letouterRing=Turf.Ring(coordinates:outerRingCoords) letinnerRing=Turf.Ring(coordinates:innerRingCoords) letpolygon=Turf.Polygon(outerRing:outerRing,innerRings:[innerRing]) //CreatethePolygonAnnotationManager letpolygonAnnotationManager=mapView.annotations.makePolygonAnnotationManager() //Createthepolygonannotation varpolygonAnnotation=PolygonAnnotation(polygon:makePolygon()) //Stylethepolygonannotation polygonAnnotation.fillColor=StyleColor(.red) polygonAnnotation.fillOpacity=0.8 //Addthepolygonannotationtothemanager polygonAnnotationManager.annotations=[polygonAnnotation] SelectingannotationsAnnotationscanalsobeinteractedwithviaatapgesture.EnsuringthatadelegateconformstoAnnotationInteractionDelegateenablestapevents:classMyViewController:UIViewController,AnnotationInteractionDelegate{ @IBOutletvarmapView:MapView! overridefuncviewDidLoad(){ super.viewDidLoad() //Createthepointannotation,whichwillberenderedwitharedpin. letcoordinate=mapView.cameraState.center varpointAnnotation=PointAnnotation(coordinate:coordinate) pointAnnotation.image=.init(image:UIImage(named:"red_pin")!,name:"red_pin") //Createthepointannotationmanager letpointAnnotationManager=mapView.annotations.makePointAnnotationManager() //Allowtheviewcontrollertoacceptannotationselectionevents. pointAnnotationManager.delegate=self //Addtheannotationtothemap. pointAnnotationManager.annotations=[pointAnnotation] } //MARK:-AnnotationInteractionDelegate publicfuncannotationManager(_manager:AnnotationManager, didDetectTappedAnnotationsannotations:[Annotation]){ print("Annotationstapped:\(annotations)") } } View-basedannotationsarenotsupported.Allpointannotationsmustbeconfiguredwithastaticimage.Draganddropsupportisnotsupported.However,thiscanbeimplementedwithanadvancedusageofvariousstyleAPIstoupdatethedatasourceofaGeoJSONSourceafterrespondingtoaUIGestureRecognizer.UserlocationTheUserLocationcomponentprovidesamechanismbywhichthedevice’scurrentlocationcanbeobservedandrespondedto.Italsohandlespermissionsandprivacy,includingfullvs.reducedaccuracy(whichwasaddediniOS14).RequestingpermissionsUsersmustgrantpermissionbeforeaniOSappcanaccessinformationabouttheirlocation.Duringthispermissionprompt,acustomstringmaybepresentedexplaininghowlocationwillbeused.ThisisspecifiedbyaddingthekeyNSLocationWhenInUseUsageDescriptiontotheInfo.plistfilewithavaluethatdescribeswhytheapplicationisrequestingthesepermissions.NSLocationWhenInUseUsageDescription Yourlocationisusedtoimprovethemapexperience. Showingthedevice'slocationDisplaythedevice'slocationwiththefollowingcode:mapView.location.options.puckType=.puck2D() Theabovecodewillenablerenderingofapuckthatrepresentsthedevice'scurrentlocation.Itwillalsohandlerequestingpermissionsandallthenuancesthatcomewithdifferentpermissionlevels.Followingthedevice'slocationv6trackingmodeshavebeenreplacedbytheViewportAPI(availablestartinginv10.3).Besidesfollowingobjectsonamap,itcanalsobeextendedwithcustomstatesandtransitions.Formoredetails,seetheUserlocationguide.CustomLocationProviderThelocationframeworkhasitsowndefaultlocationproviderwhichwillhandlelocationupdates,permissions,status,andmore.But,acustomlocationprovidercanbeusedinstead.LocationProviderisaprotocol,encapsulatingfunctionsandpropertiesthatwillhandlelocationupdates,permissionchanges,andmore.Itnotifiesitsdelegatewhenchangesinlocation,heading,authorization,orerrorstatusoccur.ThisislikeMGLLocationManagerinpre-v10versionsoftheMapsSDK.pre-v10Inpre-v10versionsoftheMapsSDK,acustomlocationprovidercouldbeprovidedbymakingacustomMGLLocationManagerobject:classCustomLocationManager:NSObject,MGLLocationManager{ //Implementprotocolrequiredfunctions } self.mapView.locationManager=CustomLocationManager() v10:AcustomlocationprovidermustconformtotheLocationProviderprotocolandimplementtherequiredfunctionsfromtheprotocol.ThecustomprovidercanbepassedtotheLocationManagerwiththefollowingcode:mapView.location.overrideLocationProvider(with:customLocationProvider) HandlingprivacyIniOS14,Appleintroducedanewlayerofprivacyprotectiontolocation.ThereisnowanewpropertycalledaccuracyAuthorization.Thisallowstheusertoselectbetweenfullaccuracyandreducedaccuracymodesfortheirlocationtracking.Whenfullaccuracymodeisenabled,thecodewillfunctionasitdidinpreviousiOSversions.But,inreducedaccuracymode,thedevicewillsendlocationupdateslessoftenandwillspecifyalargeradiusinwhichthedeviceislocatedratherthanapreciselocation.Ifauserdecidestoturnofffullaccuracylocationanddenytemporaryfullaccuracy,thenthe2Dpuckwillberenderedasanapproximatering,whichlookslikethis:Tohandlechangesinlocationprivacy,therearetwooptions:lettheMapsSDKhandlethechangesorimplementthedelegate.LettheSDKhandlechangesinlocationprivacyTheMapsSDK'sdefaultimplementationwilllistenforachangeinaccuracyauthorization.Iftheuserhasdecidedtoturnofffullaccuracylocation,theninthenextappsession,theuserwillbepromptedwithanalerttorequestatemporaryfullaccuracyauthorization.Youcansetthisfunctionalityandthecontentsofthedialogueintheapp’sInfo.plistfile.AddthekeyNSLocationTemporaryUsageDescriptionDictionaryandaddthefollowingkey-valuepair:Key:LocationAccuracyAuthorizationDescriptionValue:AsentencedescribingwhytheapplicationneedsfullaccuracylocationsNSLocationTemporaryUsageDescriptionDictionary LocationAccuracyAuthorizationDescription Thisapplicationtemporarilyrequiresyourpreciselocation. ImplementthelocationpermissionsdelegateTheLocationPermissionsDelegateprotocolhasasetofdelegatemethodsthatwillbecalledwhentheusermakeschangestotheapp'saccuracyauthorization.classViewController:UIViewController{ //Thiscontroller'sinitializationhasbeenomittedinthiscodesnippet varmapView:MapView! overridefuncviewDidLoad(){ super.viewDidLoad() mapView.location.delegate=self } //Selectorthatwillbecalledasaresultofthedelegatebelow funcrequestPermissionsButtonTapped(){ mapView.location.requestTemporaryFullAccuracyPermissions(withPurposeKey:"CustomKey") } } extensionViewController:LocationPermissionsDelegate{ funclocationManager(_locationManager:LocationManager,didChangeAccuracyAuthorizationaccuracyAuthorization:CLAccuracyAuthorization){ ifaccuracyAuthorization==.reducedAccuracy{ //Presentabuttononscreenthatasksforfullaccuracy //Thisbuttoncanhaveaselectorasdefinedabove } } } Thiscodesnippetalsohasourrecommendationforhowtohandlealocationpermissionchange.AwithPurposeKeyparameterisspecified.Thisvaluemustcorrespondtothekeyofakey-valuepairthatwasspecifiedinInfo.plist.NSLocationTemporaryUsageDescriptionDictionary CustomKey Wetemporarilyrequireyourpreciselocationforanoptimalexperience. NewfeaturesandimprovementsPlatform-drivencameraanimationsystemTheMapsSDKv10introducesanewcameraanimationsystemwhichleveragesCoreAnimation.Alongwithprovidingfamiliarhigh-levelanimationssuchaseaseToorflyTo,theSDKprovidesexpressivenewwaysofcontrollinganimationswithmoregranularity.SupportforavarietyofanimationcurvesthatatransitioncouldfollowisincludedintheMapsSDKv10.Forexample,considerasituationthatnecessitateszoomingintothemapwhilealsorotatingthemap.ThemapView.cameraexposesasetofpowerfulnewmakeAnimator*functionsthatallowforbuildingthisinanexpressivemanner:lazyvarmyCustomAnimator:BasicCameraAnimator={ mapView.camera.makeAnimator(duration:5,curve:.easeInOut){(transition)in //Transitionthezoomlevelofthemap'scamerato`7` transition.zoom.toValue=7 //Transitionthebearingofthemap'scamerato`180`degrees transition.bearing.toValue=180 } }() .... .... //Call`startAnimation()`tostarttheanimation(atsomepointafterthe`.mapLoaded`event) myCustomAnimator.startAnimation() Thisanimationwillresultinthefollowingchangestothemap'scamera:Themap'szoomwillanimatetoazoomlevelof7fromthevalueitwasbeforestartAnimation()wascalled.Themap'sbearingwillanimateto180degreesfromthevalueitwasbeforestartAnimation()iscalled.Theentireanimationwilllastfor5secondsandwillinterpolatewitha.easeInOutcurve.ControlcameratransitionswithmoregranularityThetransitionconstructpassedintoevery"animation"blockallowsyoutocontrolbothwheretheanimationcompletes(inotherwords,thefinalmapcamera)anditsstartingposition.Thesnippetbelowillustratesthis:lazyvarmyCustomAnimator:BasicCameraAnimator={ mapView.camera.makeAnimator(duration:5,curve:.easeInOut){(transition)in //Transitionthezoomlevelofthemap'scamerafrom`10`to`7` transition.zoom.fromValue=10 transition.zoom.toValue=7 //Transitionthebearingofthemap'scamerafrom`45`degreesto`180`degrees transition.bearing.fromValue=45 transition.bearing.toValue=180 } }() .... .... //Call`startAnimation()`tostarttheanimation(atsomepointafterthe`.mapLoaded`event) myCustomAnimator.startAnimation() Thisanimationwillresultinthefollowingchangestothemap'scamera:First,themap'szoomwillchangeto10andthebearingwillchangeto45withoutananimatedtransition.Themap'szoomwillthenanimatetoazoomlevelof7fromaninitialvalueof10.Themap'sbearingwillanimateto180degreesfromaninitialvalueof45degrees.Theentireanimationwilllastfor5secondsandwillinterpolatewitha.easeInOutcurve.ChainanimationsBasicCameraAnimatoralsosupportscompletionblockswiththefollowingAPI:myCustomAnimator.addCompletion{positionin print("Animationcompleteatposition:\(position)") } Thesecompletionblockscanbeusedtochainanimationstoexecuteoneafteranother,asinthisexample://StoretheCameraAnimatorssotheydon'tfalloutofscope lazyvarzoomAnimator:BasicCameraAnimator={ letanimator=mapView.camera.makeAnimator(duration:4,curve:.easeInOut){(transition)in transition.zoom.toValue=14 } animator.addCompletion{[unownedself](_)in print("Animatingcamerapitchfrom0degrees->55degrees") self.pitchAnimator.startAnimation() } returnanimator }() lazyvarpitchAnimator:BasicCameraAnimator={ letanimator=mapView.camera.makeAnimator(duration:2,curve:.easeInOut){(transition)in transition.pitch.toValue=55 } animator.addCompletion{[unownedself](_)in print("Animatingcamerabearingfrom0degrees->45degrees") self.bearingAnimator.startAnimation() } returnanimator }() lazyvarbearingAnimator:BasicCameraAnimator={ letanimator=mapView.camera.makeAnimator(duration:4,curve:.easeInOut){(transition)in transition.bearing.toValue=-45 } animator.addCompletion{(_)in print("Allanimationscomplete!") } returnanimator }() //StartthezoomAnimatoratsomepointafterthe`.mapLoaded`event zoomAnimation.startAnimation() AnimationOwnerAnimationOwnerisastructusedtokeeptrackthesource(orowner)foreachanimation.AnimationOwnerhasthefollowingpre-definedvalues:gestures:supportforanimationsrunbygestures.unspecified:supportforanon-specificanimation.CustomvaluesmaybecreatedviaAnimationOwner(rawValue:).NotesoncameraanimationABasicCameraAnimator'slifecycleisimportant.IfaBasicCameraAnimatorisdestroyedbeforeananimationiscomplete,theanimationwillimmediatelystop.CameraAnimatorsarecreatedwithadefaultownerof.unspecified.TheCameraManagerwillholdalistofallactiveanimators.3DterrainandskylayersIntheMapsSDKv10,youcanshowdramaticelevationchangesagainstanatmosphericbackdropbyenabling3Dterrainandusingthenewskylayer.Toconfigurethis,firstaddaRasterDemSourcetoamapstyle,withaURLsourcepointedtoMapbox'sglobaldigitalelevationmodel(DEM),MapboxTerrainRGB.Then,createaTerrainobjectanddefinetheexaggerationfactor,whichactsasascaletorepresentextrusion.Thehigherthevalue,themoreexaggeratedlandfeaturesathigherelevationswillappear.Addinganewskylayergivestheappearanceofthesunilluminatingland,especiallywhenthecamera’spitchischanged.Toenable3Dterrainandaddanewskylayer://Addterrain vardemSource=RasterDemSource() demSource.url="mapbox://mapbox.mapbox-terrain-dem-v1" demSource.tileSize=512 demSource.maxzoom=14.0 trymapView.mapboxMap.style.addSource(demSource,id:"mapbox-dem") varterrain=Terrain(sourceId:"mapbox-dem") terrain.exaggeration=.constant(1.5) //Addskylayer trymapView.mapboxMap.style.setTerrain(terrain) varskyLayer=SkyLayer(id:"sky-layer") skyLayer.skyType=.constant(.atmosphere) skyLayer.skyAtmosphereSun=.constant([0.0,0.0]) skyLayer.skyAtmosphereSunIntensity=.constant(15.0) trymapView.mapboxMap.style.addLayer(skyLayer) Note3Dterrainandskylayersarestillinanexperimentalstate.Itmightnotworkasexpectedwiththemapcameraanimationsystem.CustomrenderedlayersFormoreadvancedrenderingoperations,theMapsSDKv10supportsinsertingcustomMetallayerswithinthemap’sstyle.Thisunlocksmanynewopportunitiesforcustomizationandisspecificallytailoredtothoselookingtoaddtheirowncustomrenderingcode.Foranimplementationdemo,seethe“Addacustomrenderedlayer”examplewithintheExamplesapplicationformoredetails.NewOfflineManagerv10introducesanewOfflineManagerAPIthatmanagesstylepacksandproducestilesetdescriptorsforthetilestore.ReadmoreaboutOfflineManagerintheOfflineguide.TheOfflineManagerAPIcanbeusedtocreateofflinestylepacksthatcontainstyledata,suchasstyledefinition,sprites,fontsandotherresources.TilesetdescriptorscreatedbytheOfflineManagerAPIareusedtocreatetilepacksviaTileStoreAPI.MobilemapsSDKsusetilepacksforrenderingmapcontent.DeprecationsandremovalsExpressionsupportusingNSExpressionhasbeenremoved.OpenGLmaprenderinghasbeenremovedinfavorofMetal.Storingyouraccesstokenintheapplication'sInfo.plistisdiscouraged.ThelegacyOfflineManagerhasbeendeprecatedandrenamedtoOfflineRegionManager.Shareyourfeedback



請為這篇文章評分?