Skip to content

Commit 9386f2c

Browse files
authored
Resizable image support (#1269)
1 parent ec1d5ac commit 9386f2c

File tree

15 files changed

+212
-26
lines changed

15 files changed

+212
-26
lines changed

Apps/Examples/Examples.xcodeproj/project.pbxproj

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
58A3C0C925C4B93600CAE5F0 /* AnimateGeoJSONLineExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A3C0C825C4B93600CAE5F0 /* AnimateGeoJSONLineExample.swift */; };
4646
73694BD325D4B2CE0064F636 /* TrackingModeExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73694BD225D4B2CE0064F636 /* TrackingModeExample.swift */; };
4747
73E27C2627AF024C0067F277 /* DataJoinExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73E27C2527AF024C0067F277 /* DataJoinExample.swift */; };
48+
7406E3702806B66F002CC41F /* ResizableImageExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7406E36F2806B66F002CC41F /* ResizableImageExample.swift */; };
4849
7412CF6227E8DD1E00F03B1C /* AddOneMarkerSymbolExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7412CF6127E8DD1E00F03B1C /* AddOneMarkerSymbolExample.swift */; };
4950
7412CF6427E8EA2A00F03B1C /* CircleAnnotationExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7412CF6327E8EA2A00F03B1C /* CircleAnnotationExample.swift */; };
5051
7412CF6627E9A7FF00F03B1C /* CLLocationCoordinate2D+Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7412CF6527E9A7FF00F03B1C /* CLLocationCoordinate2D+Random.swift */; };
@@ -189,6 +190,7 @@
189190
666E0D4925664EE7000B8AF5 /* LayerPositionExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayerPositionExample.swift; sourceTree = "<group>"; };
190191
73694BD225D4B2CE0064F636 /* TrackingModeExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackingModeExample.swift; sourceTree = "<group>"; };
191192
73E27C2527AF024C0067F277 /* DataJoinExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataJoinExample.swift; sourceTree = "<group>"; };
193+
7406E36F2806B66F002CC41F /* ResizableImageExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizableImageExample.swift; sourceTree = "<group>"; };
192194
7412CF6127E8DD1E00F03B1C /* AddOneMarkerSymbolExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddOneMarkerSymbolExample.swift; sourceTree = "<group>"; };
193195
7412CF6327E8EA2A00F03B1C /* CircleAnnotationExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleAnnotationExample.swift; sourceTree = "<group>"; };
194196
7412CF6527E9A7FF00F03B1C /* CLLocationCoordinate2D+Random.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CLLocationCoordinate2D+Random.swift"; sourceTree = "<group>"; };
@@ -285,6 +287,8 @@
285287
isa = PBXGroup;
286288
children = (
287289
B58E470827BABE0E00D87FD6 /* AdvancedViewportGesturesExample.swift */,
290+
7406E36E2806B659002CC41F /* Lab */,
291+
7412CF6027E8DCFB00F03B1C /* Annotations */,
288292
58A3C0C825C4B93600CAE5F0 /* AnimateGeoJSONLineExample.swift */,
289293
17B480592684FB2300CF0D5E /* AnimateImageLayerExample.swift */,
290294
07DBDC91254C94C500F89304 /* AnimateLayerExample.swift */,
@@ -427,6 +431,14 @@
427431
path = Radar;
428432
sourceTree = "<group>";
429433
};
434+
7406E36E2806B659002CC41F /* Lab */ = {
435+
isa = PBXGroup;
436+
children = (
437+
7406E36F2806B66F002CC41F /* ResizableImageExample.swift */,
438+
);
439+
path = Lab;
440+
sourceTree = "<group>";
441+
};
430442
7412CF6027E8DCFB00F03B1C /* Annotations */ = {
431443
isa = PBXGroup;
432444
children = (
@@ -649,6 +661,7 @@
649661
17E28C5C2672A1160033DF0F /* SymbolClusteringExample.swift in Sources */,
650662
CADCF733258499130065C51B /* Examples.swift in Sources */,
651663
CADCF743258499570065C51B /* BasicMapExample.swift in Sources */,
664+
7406E3702806B66F002CC41F /* ResizableImageExample.swift in Sources */,
652665
17B40D2426A85500000887EF /* LiveDataExample.swift in Sources */,
653666
CADCF7242584990E0065C51B /* AnimateLayerExample.swift in Sources */,
654667
073475D725AFAE520049B0B8 /* CustomLayerExample.swift in Sources */,

Apps/Examples/Examples/All Examples/Annotations/AddMarkersSymbolExample.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ final class AddMarkersSymbolExample: UIViewController, ExampleProtocol {
4040

4141
private func prepareStyle() {
4242
let style = mapView.mapboxMap.style
43-
try? style.addImage(UIImage(named: "blue_marker_view")!, id: Constants.BLUE_ICON_ID, stretchX: [], stretchY: [])
44-
try? style.addImage(UIImage(named: "red_marker")!, id: Constants.RED_ICON_ID, stretchX: [], stretchY: [])
43+
try? style.addImage(UIImage(named: "blue_marker_view")!, id: Constants.BLUE_ICON_ID)
44+
try? style.addImage(UIImage(named: "red_marker")!, id: Constants.RED_ICON_ID)
4545

4646
var features = [Feature]()
4747
var feature = Feature(geometry: Point(LocationCoordinate2D(latitude: 55.608166, longitude: 12.65147)))

Apps/Examples/Examples/All Examples/Annotations/AddOneMarkerSymbolExample.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ final class AddOneMarkerSymbolExample: UIViewController, ExampleProtocol {
3232
}
3333

3434
private func addMarkerAnnotation(toStyle style: Style) {
35-
try? style.addImage(UIImage(named: "blue_marker_view")!, id: Constants.BLUE_ICON_ID, stretchX: [], stretchY: [])
35+
try? style.addImage(UIImage(named: "blue_marker_view")!, id: Constants.BLUE_ICON_ID)
3636

3737
var source = GeoJSONSource()
3838
source.data = .geometry(Geometry.point(Point(Constants.coordinate)))

Apps/Examples/Examples/All Examples/Annotations/AnimatedMarkerExample.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ final class AnimatedMarkerExample: UIViewController, ExampleProtocol {
6262
}
6363

6464
private func setupExample() {
65-
try? mapView.mapboxMap.style.addImage(UIImage(named: "red_marker")!, id: Constants.markerIconId, stretchX: [], stretchY: [])
65+
try? mapView.mapboxMap.style.addImage(UIImage(named: "red_marker")!, id: Constants.markerIconId)
6666

6767
// Create a GeoJSON data source.
6868
var source = GeoJSONSource()

Apps/Examples/Examples/All Examples/Annotations/IconSizeChangeExample.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ final class IconSizeChangeExample: UIViewController, ExampleProtocol {
4848
try? mapView.mapboxMap.style.addSource(markerSource, id: Constants.markerSourceId)
4949

5050
// Add marker image to the map
51-
try? mapView.mapboxMap.style.addImage(UIImage(named: "blue_marker_view")!, id: Constants.blueMarkerImageId, stretchX: [], stretchY: [])
51+
try? mapView.mapboxMap.style.addImage(UIImage(named: "blue_marker_view")!, id: Constants.blueMarkerImageId)
5252

5353
// Create a symbol layer for markers
5454
var markerLayer = SymbolLayer(id: Constants.markerLayerId)

Apps/Examples/Examples/All Examples/Annotations/SymbolClusteringExample.swift

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,7 @@ class SymbolClusteringExample: UIViewController, ExampleProtocol {
3737
// Add the image tp the map's style. Set `sdf` to `true`. This allows the icon images to be recolored.
3838
// For more information about `SDF`, or Signed Distance Fields, see
3939
// https://docs.mapbox.com/help/troubleshooting/using-recolorable-images-in-mapbox-maps/#what-are-signed-distance-fields-sdf
40-
try! style.addImage(image,
41-
id: "fire-station-icon",
42-
sdf: true,
43-
stretchX: [],
44-
stretchY: [])
40+
try! style.addImage(image, id: "fire-station-icon", sdf: true)
4541

4642
// Fire_Hydrants.geojson contains information about fire hydrants in the District of Columbia.
4743
// It was downloaded on 6/10/21 from https://opendata.dc.gov/datasets/DCGIS::fire-hydrants/about

Apps/Examples/Examples/All Examples/Annotations/ViewAnnotationMarkerExample.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ final class ViewAnnotationMarkerExample: UIViewController, ExampleProtocol {
110110

111111
private func prepareStyle() {
112112
let style = mapView.mapboxMap.style
113-
try? style.addImage(image, id: Constants.BLUE_ICON_ID, stretchX: [], stretchY: [])
113+
try? style.addImage(image, id: Constants.BLUE_ICON_ID)
114114

115115
var source = GeoJSONSource()
116116
source.data = .featureCollection(FeatureCollection(features: pointList))

Apps/Examples/Examples/All Examples/Annotations/ViewAnnotationWithPointAnnotationExample.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ final class ViewAnnotationWithPointAnnotationExample: UIViewController, ExampleP
3131
mapView.mapboxMap.onNext(.mapLoaded) { [weak self] _ in
3232
guard let self = self else { return }
3333

34-
try? self.mapView.mapboxMap.style.addImage(self.image, id: Constants.blueIconId, stretchX: [], stretchY: [])
34+
try? self.mapView.mapboxMap.style.addImage(self.image, id: Constants.blueIconId)
3535
self.addPointAndViewAnnotation(at: self.mapView.mapboxMap.coordinate(for: self.mapView.center))
3636

3737
// The below line is used for internal testing purposes only.

Apps/Examples/Examples/All Examples/DataDrivenSymbolsExample.swift

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,9 @@ public class DataDrivenSymbolsExample: UIViewController, ExampleProtocol {
3030

3131
// Add icons from the U.S. National Parks Service to the map's style.
3232
// Icons are located in the asset catalog
33-
try! mapView.mapboxMap.style.addImage(UIImage(named: "nps-restrooms")!,
34-
id: "restrooms",
35-
stretchX: [],
36-
stretchY: [])
37-
try! mapView.mapboxMap.style.addImage(UIImage(named: "nps-trailhead")!,
38-
id: "trailhead",
39-
stretchX: [],
40-
stretchY: [])
41-
try! mapView.mapboxMap.style.addImage(UIImage(named: "nps-picnic-area")!,
42-
id: "picnic-area",
43-
stretchX: [],
44-
stretchY: [])
33+
try! mapView.mapboxMap.style.addImage(UIImage(named: "nps-restrooms")!, id: "restrooms")
34+
try! mapView.mapboxMap.style.addImage(UIImage(named: "nps-trailhead")!, id: "trailhead")
35+
try! mapView.mapboxMap.style.addImage(UIImage(named: "nps-picnic-area")!, id: "picnic-area")
4536

4637
// Access a vector tileset that contains places of interest at Yosemite National Park.
4738
// This tileset was created by uploading NPS shapefiles to Mapbox Studio.
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import Foundation
2+
import UIKit
3+
import MapboxMaps
4+
5+
/// An example showcasing of adding a resizable image to the style
6+
/// and demonstrating how the image is stretched
7+
@objc(ResizableImageExample)
8+
final class ResizableImageExample: UIViewController, ExampleProtocol {
9+
private static let center = CLLocationCoordinate2DMake(55.70651, 12.554729)
10+
private static let layerId = "layer_id"
11+
private static let textBase = "Hi! "
12+
13+
private lazy var mapView: MapView = {
14+
let mapInitOptions = MapInitOptions(cameraOptions: CameraOptions(center: Self.center, zoom: 9))
15+
let mapView = MapView(frame: view.bounds, mapInitOptions: mapInitOptions)
16+
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
17+
return mapView
18+
}()
19+
private var style: Style { mapView.mapboxMap.style }
20+
21+
private var appendTextCounter = 1
22+
private var symbolLayer: SymbolLayer!
23+
private weak var timer: Timer? {
24+
didSet { oldValue?.invalidate() }
25+
}
26+
27+
// MARK: - Lifecycle
28+
29+
override func viewDidLoad() {
30+
super.viewDidLoad()
31+
32+
view.addSubview(mapView)
33+
34+
mapView.mapboxMap.onNext(.mapLoaded) { _ in
35+
self.setupExample()
36+
self.startUpdatingIconText()
37+
}
38+
}
39+
40+
override func viewDidAppear(_ animated: Bool) {
41+
super.viewDidAppear(animated)
42+
// The below line is used for internal testing purposes only.
43+
finish()
44+
}
45+
46+
override func didMove(toParent parent: UIViewController?) {
47+
super.didMove(toParent: parent)
48+
49+
if parent == nil {
50+
timer = nil
51+
}
52+
}
53+
54+
// MARK: - Private
55+
56+
private func setupExample() {
57+
let geoJSONSourceId = "source_id"
58+
59+
// create an image of a circle and specify the corners that should remain unchanged
60+
let image = UIImage(named: "circle")!
61+
.resizableImage(withCapInsets: UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12))
62+
try? style.addImage(image, id: "circle")
63+
64+
// add a GeoJSON source with a single point to the style
65+
var source = GeoJSONSource()
66+
source.data = .feature(Feature(geometry: Point(Self.center)))
67+
68+
try? style.addSource(source, id: geoJSONSourceId)
69+
70+
// add a symbol layer with the resizable icon image
71+
symbolLayer = SymbolLayer(id: Self.layerId)
72+
symbolLayer.source = geoJSONSourceId
73+
symbolLayer.iconImage = .constant(.name("circle"))
74+
// make sure the icon image is stretched both vertically and horizontally
75+
symbolLayer.iconTextFit = .constant(.both)
76+
symbolLayer.iconTextFitPadding = .constant([10, 10, 10, 10])
77+
symbolLayer.textField = .constant(Self.textBase)
78+
79+
try? style.addLayer(symbolLayer, layerPosition: .default)
80+
}
81+
82+
private func startUpdatingIconText() {
83+
Timer.scheduledTimer(timeInterval: 1.5, target: self, selector: #selector(updateIconText), userInfo: nil, repeats: true)
84+
}
85+
86+
// Append some text to the layer's textField, stretching the icon image in both X and Y axes
87+
@objc private func updateIconText() {
88+
guard style.isLoaded else {
89+
return
90+
}
91+
92+
let layer = try? style.layer(withId: Self.layerId, type: SymbolLayer.self)
93+
94+
guard case .expression(let expression) = layer?.textField else {
95+
return
96+
}
97+
98+
appendTextCounter += 1
99+
100+
guard let textLabel = expression.arguments.first?.description
101+
.appending(Self.textBase)
102+
.appending(appendTextCounter % 3 == 0 ? "\n" : "") else {
103+
return
104+
}
105+
106+
try? style.updateLayer(withId: Self.layerId, type: SymbolLayer.self) { layer in
107+
layer.textField = .constant(textLabel)
108+
}
109+
}
110+
}

0 commit comments

Comments
 (0)