[ios, macos] Nine-part resizable images
The MGLSymbolStyleLayer.iconTextFit property now respects the cap insets of any nine-part stretchable image passed into the -[MGLStyle setImage:forName:] method. The “Manipulate Style” command in macosapp demonstrates the stretchable image feature by adding a single Ohio state route shield image, which has cap insets defined in the asset catalog, and labeling each Ohio state route with that image, stretched to fit the route number without widening the icon’s stroke.
This commit is contained in:
committed by
Minh Nguyễn
parent
33ee616396
commit
9c4b5ee557
@@ -57,6 +57,9 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"icon-text-fit": {
|
||||
"doc": "The directions in which the icon stretches to fit around the text. If the icon image is a resizable image, the resizable areas may be stretched, while the cap insets are always drawn at the original scale."
|
||||
},
|
||||
"icon-text-fit-padding": {
|
||||
"doc": "Size of the additional area added to dimensions determined by `icon-text-fit`."
|
||||
},
|
||||
|
||||
@@ -102,7 +102,9 @@ typedef NS_ENUM(NSUInteger, MGLIconRotationAlignment) {
|
||||
};
|
||||
|
||||
/**
|
||||
Scales the icon to fit around the associated text.
|
||||
The directions in which the icon stretches to fit around the text. If the icon
|
||||
image is a resizable image, the resizable areas may be stretched, while the cap
|
||||
insets are always drawn at the original scale.
|
||||
|
||||
Values of this type are used in the `MGLSymbolStyleLayer.iconTextFit`
|
||||
property.
|
||||
@@ -776,7 +778,9 @@ MGL_EXPORT
|
||||
@property (nonatomic, null_resettable) NSExpression *iconSize __attribute__((unavailable("Use iconScale instead.")));
|
||||
|
||||
/**
|
||||
Scales the icon to fit around the associated text.
|
||||
The directions in which the icon stretches to fit around the text. If the icon
|
||||
image is a resizable image, the resizable areas may be stretched, while the cap
|
||||
insets are always drawn at the original scale.
|
||||
|
||||
The default value of this property is an expression that evaluates to `none`.
|
||||
Set this property to `nil` to reset it to the default value.
|
||||
|
||||
@@ -9,6 +9,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
|
||||
* Added the `in` expression function for testing whether a value is included in an array expression or whether a string is a substring of another string. Use this function in expressions in style JSON or with the `MGL_FUNCTION()` syntax in an `NSExpression` format string. ([#16162](https://github.com/mapbox/mapbox-gl-native/pull/16162))
|
||||
* Added the `within` expression function for testing whether the evaluated feature lies within the given GeoJSON object. Use this function in expressions in style JSON or with the `MGL_FUNCTION()` syntax in an `NSExpression` format string. ([#16157](https://github.com/mapbox/mapbox-gl-native/pull/16157))
|
||||
* Added the `MGLLineStyleLayer.lineSortKey` and `MGLFillStyleLayer.fillSortKey` properties. ([#179](https://github.com/mapbox/mapbox-gl-native-ios/pull/179), [#16194](https://github.com/mapbox/mapbox-gl-native/pull/16194), [#16220](https://github.com/mapbox/mapbox-gl-native/pull/16220))
|
||||
* The `MGLSymbolStyleLayer.iconTextFit` property now respects the cap insets of any [nine-part stretchable image](https://developer.apple.com/documentation/uikit/uiimage#1658362) passed into the `-[MGLStyle setImage:forName:]` method. You can define the stretchable area in Xcode’s asset catalog or by calling the `-[UIImage resizableImageWithCapInsets:]` method. ([#182](https://github.com/mapbox/mapbox-gl-native-ios/pull/182))
|
||||
* The `-[MGLStyle localizeLabelsIntoLocale:]` and `-[NSExpression mgl_expressionLocalizedIntoLocale:]` methods can now localize text into Traditional Chinese and Vietnamese. ([#173](https://github.com/mapbox/mapbox-gl-native-ios/pull/173))
|
||||
* Fixed an issue where an `MGLSymbolStyleLayer.lineDashPattern` value of `{1, 0}` resulted in hairline gaps. ([#16202](https://github.com/mapbox/mapbox-gl-native/pull/16202))
|
||||
* Improved the performance of loading a style that has many style images. ([#16187](https://github.com/mapbox/mapbox-gl-native/pull/16187))
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
|
||||
const MGLExceptionName MGLResourceNotFoundException = @"MGLResourceNotFoundException";
|
||||
|
||||
BOOL MGLEdgeInsetsIsZero(UIEdgeInsets edgeInsets) {
|
||||
return edgeInsets.left == 0 && edgeInsets.top == 0 && edgeInsets.right == 0 && edgeInsets.bottom == 0;
|
||||
}
|
||||
|
||||
@implementation UIImage (MGLAdditions)
|
||||
|
||||
- (nullable instancetype)initWithMGLStyleImage:(const mbgl::style::Image &)styleImage
|
||||
@@ -39,10 +43,30 @@ const MGLExceptionName MGLResourceNotFoundException = @"MGLResourceNotFoundExcep
|
||||
}
|
||||
|
||||
- (std::unique_ptr<mbgl::style::Image>)mgl_styleImageWithIdentifier:(NSString *)identifier {
|
||||
mbgl::style::ImageStretches stretchX = {{
|
||||
self.capInsets.left / self.scale, (self.size.width - self.capInsets.right) / self.scale,
|
||||
}};
|
||||
mbgl::style::ImageStretches stretchY = {{
|
||||
self.capInsets.top / self.scale, (self.size.height - self.capInsets.bottom) / self.scale,
|
||||
}};
|
||||
|
||||
mbgl::optional<mbgl::style::ImageContent> imageContent;
|
||||
if (!MGLEdgeInsetsIsZero(self.capInsets)) {
|
||||
imageContent = (mbgl::style::ImageContent){
|
||||
.left = static_cast<float>(self.capInsets.left * self.scale),
|
||||
.top = static_cast<float>(self.capInsets.top * self.scale),
|
||||
.right = static_cast<float>((self.size.width - self.capInsets.right) * self.scale),
|
||||
.bottom = static_cast<float>((self.size.height - self.capInsets.bottom) * self.scale),
|
||||
};
|
||||
}
|
||||
|
||||
BOOL isTemplate = self.renderingMode == UIImageRenderingModeAlwaysTemplate;
|
||||
return std::make_unique<mbgl::style::Image>([identifier UTF8String],
|
||||
self.mgl_premultipliedImage,
|
||||
float(self.scale), isTemplate);
|
||||
static_cast<float>(self.scale),
|
||||
isTemplate,
|
||||
stretchX, stretchY,
|
||||
imageContent);
|
||||
}
|
||||
|
||||
- (mbgl::PremultipliedImage)mgl_premultipliedImage {
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
* Added the `within` expression function for testing whether the evaluated feature lies within the given GeoJSON object. Use this function in expressions in style JSON or with the `MGL_FUNCTION()` syntax in an `NSExpression` format string. ([#16157](https://github.com/mapbox/mapbox-gl-native/pull/16157), [#16194](https://github.com/mapbox/mapbox-gl-native/pull/16194), [#16220](https://github.com/mapbox/mapbox-gl-native/pull/16220))
|
||||
* Added the `MGLSymbolStyleLayer.textWritingModes` layout property. This property can be set to `MGLTextWritingModeHorizontal` or `MGLTextWritingModeVertical`. ([#14932](https://github.com/mapbox/mapbox-gl-native/pull/14932))
|
||||
* Added the `MGLLineStyleLayer.lineSortKey` and `MGLFillStyleLayer.fillSortKey` properties. ([#179](https://github.com/mapbox/mapbox-gl-native-ios/pull/179))
|
||||
* The `MGLIdeographicFontFamilyName` Info.plist key now also accepts an array of font family names, to customize font fallback behavior. It can also be set to a Boolean value of `NO` to force the SDK to typeset CJK characters in a remote font specified by `MGLSymbolStyleLayer.textFontNames`. ([#14862](https://github.com/mapbox/mapbox-gl-native/pull/14862))
|
||||
* The `MGLIdeographicFontFamilyName` Info.plist key now also accepts an array of font family names, to customize font fallback behavior. It can also be set to a Boolean value of `NO` to force the SDK to typeset CJK characters in a remote font specified by `MGLSymbolStyleLayer.textFontNames`. ([#14862](https://github.com/mapbox/mapbox-gl-native/pull/14862))
|
||||
* The `MGLSymbolStyleLayer.iconTextFit` property now respects the cap insets of any nine-part stretchable image passed into the `-[MGLStyle setImage:forName:]` method. You can define the stretchable area in Xcode’s asset catalog or by setting the `NSImage.capInsets` property. ([#182](https://github.com/mapbox/mapbox-gl-native-ios/pull/182))
|
||||
* The `-[MGLStyle localizeLabelsIntoLocale:]` and `-[NSExpression mgl_expressionLocalizedIntoLocale:]` methods can now localize text into Traditional Chinese and Vietnamese. ([#173](https://github.com/mapbox/mapbox-gl-native-ios/pull/173))
|
||||
* Fixed crashes triggered when `MGLSource` and `MGLStyleLayer` objects are accessed after having been invalidated after a style change. ([#15539](https://github.com/mapbox/mapbox-gl-native/pull/15539))
|
||||
* Fixed an issue where fill extrusion layers would be incorrectly rendered above other layers. ([#15065](https://github.com/mapbox/mapbox-gl-native/pull/15065))
|
||||
|
||||
26
platform/macos/app/Assets.xcassets/ohio.imageset/Contents.json
vendored
Normal file
26
platform/macos/app/Assets.xcassets/ohio.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ohio.pdf",
|
||||
"resizing" : {
|
||||
"mode" : "9-part",
|
||||
"center" : {
|
||||
"mode" : "stretch",
|
||||
"width" : 15,
|
||||
"height" : 18
|
||||
},
|
||||
"cap-insets" : {
|
||||
"bottom" : 3,
|
||||
"top" : 3,
|
||||
"right" : 4,
|
||||
"left" : 5
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
BIN
platform/macos/app/Assets.xcassets/ohio.imageset/ohio.pdf
vendored
Normal file
BIN
platform/macos/app/Assets.xcassets/ohio.imageset/ohio.pdf
vendored
Normal file
Binary file not shown.
@@ -1012,9 +1012,9 @@ NSArray<id <MGLAnnotation>> *MBXFlattenedShapes(NSArray<id <MGLAnnotation>> *sha
|
||||
|
||||
MGLSource *streetsSource = [self.mapView.style sourceWithIdentifier:@"composite"];
|
||||
if (streetsSource) {
|
||||
NSImage *image = [NSImage imageNamed:NSImageNameIChatTheaterTemplate];
|
||||
[self.mapView.style setImage:image forName:NSImageNameIChatTheaterTemplate];
|
||||
|
||||
NSImage *image = [NSImage imageNamed:NSImageNameIChatTheaterTemplate];
|
||||
[self.mapView.style setImage:image forName:NSImageNameIChatTheaterTemplate];
|
||||
|
||||
MGLSymbolStyleLayer *theaterLayer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"theaters" source:streetsSource];
|
||||
theaterLayer.sourceLayerIdentifier = @"poi_label";
|
||||
theaterLayer.predicate = [NSPredicate predicateWithFormat:@"maki == 'theatre'"];
|
||||
@@ -1026,6 +1026,23 @@ NSArray<id <MGLAnnotation>> *MBXFlattenedShapes(NSArray<id <MGLAnnotation>> *sha
|
||||
@20.0: [NSColor blackColor],
|
||||
}];
|
||||
[self.mapView.style addLayer:theaterLayer];
|
||||
|
||||
NSImage *ohio = [NSImage imageNamed:@"ohio"];
|
||||
[self.mapView.style setImage:ohio forName:@"ohio"];
|
||||
|
||||
MGLSymbolStyleLayer *ohioLayer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"ohio" source:streetsSource];
|
||||
ohioLayer.sourceLayerIdentifier = @"road";
|
||||
ohioLayer.predicate = [NSPredicate predicateWithFormat:@"shield = 'circle-white' and iso_3166_2 = 'US-OH'"];
|
||||
ohioLayer.symbolPlacement = [NSExpression expressionForConstantValue:@"line"];
|
||||
ohioLayer.text = [NSExpression expressionForKeyPath:@"ref"];
|
||||
ohioLayer.textFontNames = [NSExpression expressionWithFormat:@"{'DIN Offc Pro Bold', 'Arial Unicode MS Bold'}"];
|
||||
ohioLayer.textFontSize = [NSExpression expressionForConstantValue:@10];
|
||||
ohioLayer.textRotationAlignment = [NSExpression expressionForConstantValue:@"viewport"];
|
||||
ohioLayer.iconImageName = [NSExpression expressionForConstantValue:@"ohio"];
|
||||
ohioLayer.iconTextFit = [NSExpression expressionForConstantValue:@"both"];
|
||||
ohioLayer.iconTextFitPadding = [NSExpression expressionForConstantValue:[NSValue valueWithEdgeInsets:NSEdgeInsetsMake(1, 2, 1, 3)]];
|
||||
ohioLayer.iconRotationAlignment = [NSExpression expressionForConstantValue:@"viewport"];
|
||||
[self.mapView.style addLayer:ohioLayer];
|
||||
}
|
||||
|
||||
NSURL *imageURL = [NSURL URLWithString:@"https://www.mapbox.com/mapbox-gl-js/assets/radar.gif"];
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
#include <mbgl/util/image+MGLAdditions.hpp>
|
||||
|
||||
BOOL MGLEdgeInsetsIsZero(NSEdgeInsets edgeInsets) {
|
||||
return edgeInsets.left == 0 && edgeInsets.top == 0 && edgeInsets.right == 0 && edgeInsets.bottom == 0;
|
||||
}
|
||||
|
||||
@implementation NSImage (MGLAdditions)
|
||||
|
||||
- (nullable instancetype)initWithMGLPremultipliedImage:(mbgl::PremultipliedImage&&)src {
|
||||
@@ -35,10 +39,31 @@
|
||||
- (std::unique_ptr<mbgl::style::Image>)mgl_styleImageWithIdentifier:(NSString *)identifier {
|
||||
mbgl::PremultipliedImage cPremultipliedImage = self.mgl_premultipliedImage;
|
||||
auto imageWidth = cPremultipliedImage.size.width;
|
||||
|
||||
float scale = static_cast<float>(imageWidth) / self.size.width;
|
||||
mbgl::style::ImageStretches stretchX = {{
|
||||
self.capInsets.left * scale, (self.size.width - self.capInsets.right) * scale,
|
||||
}};
|
||||
mbgl::style::ImageStretches stretchY = {{
|
||||
self.capInsets.top * scale, (self.size.height - self.capInsets.bottom) * scale,
|
||||
}};
|
||||
|
||||
mbgl::optional<mbgl::style::ImageContent> imageContent;
|
||||
if (!MGLEdgeInsetsIsZero(self.capInsets)) {
|
||||
imageContent = (mbgl::style::ImageContent){
|
||||
.left = static_cast<float>(self.capInsets.left * scale),
|
||||
.top = static_cast<float>(self.capInsets.top * scale),
|
||||
.right = static_cast<float>((self.size.width - self.capInsets.right) * scale),
|
||||
.bottom = static_cast<float>((self.size.height - self.capInsets.bottom) * scale),
|
||||
};
|
||||
}
|
||||
|
||||
return std::make_unique<mbgl::style::Image>([identifier UTF8String],
|
||||
std::move(cPremultipliedImage),
|
||||
(float)(imageWidth / self.size.width),
|
||||
[self isTemplate]);
|
||||
scale,
|
||||
[self isTemplate],
|
||||
stretchX, stretchY,
|
||||
imageContent);
|
||||
}
|
||||
|
||||
- (mbgl::PremultipliedImage)mgl_premultipliedImage {
|
||||
|
||||
Reference in New Issue
Block a user