@tileflow/react-native 0.1.0-alpha.1 → 0.1.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/android/src/main/java/dev/tileflow/reactnative/NativeTheme.kt +75 -0
- package/android/src/main/java/dev/tileflow/reactnative/TileflowNativeSurfaceModule.kt +211 -3
- package/dist/{contract-DWs-lDPo.d.ts → contract-Bwl-gB-I.d.ts} +27 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +656 -193
- package/dist/internal/camera.d.ts +1 -1
- package/dist/internal/initial-view.d.ts +1 -1
- package/dist/internal/native-admission-bridge.d.ts +2 -2
- package/dist/internal/native-admission-bridge.js +2 -4
- package/dist/internal/native-admission.d.ts +2 -2
- package/dist/internal/native-admission.js +5 -4
- package/dist/internal/native-appearance.d.ts +1 -1
- package/dist/internal/source-state.d.ts +1 -1
- package/dist/{native-admission-contract-k7LKE1vW.d.ts → native-admission-contract-DFLfUP9X.d.ts} +1 -1
- package/ios/TFNativeTheme.h +28 -0
- package/ios/TFNativeTheme.mm +198 -0
- package/ios/TileflowNativeSurface.mm +253 -5
- package/package.json +2 -2
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
#import <MapLibreReactNative/MLRNMapView.h>
|
|
6
6
|
#import <MapLibreReactNative/MLRNCamera.h>
|
|
7
7
|
#import <QuartzCore/QuartzCore.h>
|
|
8
|
+
#import <algorithm>
|
|
8
9
|
#import "TFAdmissionEngine.h"
|
|
9
10
|
#import "TFNativeSurfaceState.h"
|
|
11
|
+
#import "TFNativeTheme.h"
|
|
10
12
|
#import <cmath>
|
|
11
13
|
|
|
12
14
|
static void TFSurfaceInvalid(void) {
|
|
@@ -67,6 +69,32 @@ static MLRNCamera *TFSurfaceCamera(UIView *root, MLRNMapView *map) {
|
|
|
67
69
|
return found;
|
|
68
70
|
}
|
|
69
71
|
|
|
72
|
+
// The view MapLibre draws into: the subview backed by a Metal (or OpenGL) layer.
|
|
73
|
+
static UIView *TFSurfaceRenderView(MLNMapView *map) {
|
|
74
|
+
for (UIView *view in map.subviews) {
|
|
75
|
+
NSString *name = NSStringFromClass(view.layer.class);
|
|
76
|
+
if ([name containsString:@"Metal"] || [name containsString:@"EAGL"]) return view;
|
|
77
|
+
}
|
|
78
|
+
return nil;
|
|
79
|
+
}
|
|
80
|
+
// A snapshot of one frame over the rendering view. It keeps four ground points of that frame and
|
|
81
|
+
// follows them with the ground plane's projective transform while the camera moves.
|
|
82
|
+
@interface TFSurfaceCover : NSObject
|
|
83
|
+
@property (nonatomic, strong) UIView *view;
|
|
84
|
+
@property (nonatomic, copy) NSArray<NSValue *> *corners;
|
|
85
|
+
@property (nonatomic, copy) NSArray<NSValue *> *anchors;
|
|
86
|
+
@property (nonatomic) BOOL fading;
|
|
87
|
+
@end
|
|
88
|
+
@implementation TFSurfaceCover
|
|
89
|
+
@end
|
|
90
|
+
@interface TFSurfaceReveal : NSObject
|
|
91
|
+
@property (nonatomic) double duration;
|
|
92
|
+
@property (nonatomic, strong) NSMutableArray<TFSurfaceCover *> *covers;
|
|
93
|
+
@property (nonatomic, copy) RCTPromiseResolveBlock resolve;
|
|
94
|
+
@end
|
|
95
|
+
@implementation TFSurfaceReveal
|
|
96
|
+
@end
|
|
97
|
+
|
|
70
98
|
@interface TFSurfaceLayoutProbe : UIView
|
|
71
99
|
@property (nonatomic, copy) dispatch_block_t changed;
|
|
72
100
|
@end
|
|
@@ -90,8 +118,19 @@ static MLRNCamera *TFSurfaceCamera(UIView *root, MLRNMapView *map) {
|
|
|
90
118
|
@property (nonatomic) BOOL retiring;
|
|
91
119
|
@property (nonatomic) BOOL foreground;
|
|
92
120
|
@property (nonatomic) BOOL observing;
|
|
121
|
+
@property (nonatomic, strong) NSMutableArray<TFSurfaceCover *> *covers;
|
|
122
|
+
@property (nonatomic, strong) NSMutableArray<TFSurfaceReveal *> *reveals;
|
|
123
|
+
@property (nonatomic, strong) NSMutableArray<TFSurfaceReveal *> *settling;
|
|
124
|
+
@property (nonatomic, copy, nullable) dispatch_block_t revealDeadline;
|
|
125
|
+
@property (nonatomic, copy, nullable) NSString *themedToken;
|
|
126
|
+
@property (nonatomic, strong) NSMutableDictionary<NSString *, MLNStyleLayer *> *themedLayers;
|
|
127
|
+
@property (nonatomic, strong) NSMutableDictionary<NSString *, UIImage *> *themedImages;
|
|
93
128
|
- (void)attach;
|
|
129
|
+
- (NSUInteger)applyThemeValues:(NSDictionary *)values;
|
|
94
130
|
- (void)retire;
|
|
131
|
+
- (BOOL)cover;
|
|
132
|
+
- (void)reveal:(double)duration resolve:(RCTPromiseResolveBlock)resolve;
|
|
133
|
+
- (void)discardCovers;
|
|
95
134
|
- (BOOL)owns;
|
|
96
135
|
- (MLNStyle *)style;
|
|
97
136
|
- (NSDictionary *)view;
|
|
@@ -114,6 +153,7 @@ static MLRNCamera *TFSurfaceCamera(UIView *root, MLRNMapView *map) {
|
|
|
114
153
|
}
|
|
115
154
|
- (void)attach {
|
|
116
155
|
self.token = @""; self.detached = [NSMutableArray array]; self.previous = self.map.delegate;
|
|
156
|
+
self.covers = [NSMutableArray array]; self.reveals = [NSMutableArray array]; self.settling = [NSMutableArray array];
|
|
117
157
|
if ([self.previous isKindOfClass:TFSurfaceAttachment.class]) TFSurfaceInvalid();
|
|
118
158
|
self.map.delegate = self;
|
|
119
159
|
[self.map addObserver:self forKeyPath:@"delegate" options:0 context:(__bridge void *)self]; self.observing = YES;
|
|
@@ -149,8 +189,182 @@ static MLRNCamera *TFSurfaceCamera(UIView *root, MLRNMapView *map) {
|
|
|
149
189
|
for (NSUInteger index = 0; index < 4; index++) visible = visible && [snapshot[index] doubleValue] > 0;
|
|
150
190
|
[self.state layout:visible];
|
|
151
191
|
}
|
|
192
|
+
- (BOOL)cover {
|
|
193
|
+
if (UIAccessibilityIsReduceMotionEnabled() || !self.foreground || !self.root.window || !self.style) return NO;
|
|
194
|
+
for (TFSurfaceCover *existing in self.covers) if (!existing.fading) return YES;
|
|
195
|
+
UIView *render = TFSurfaceRenderView(self.map);
|
|
196
|
+
CGRect frame = render.frame;
|
|
197
|
+
if (!render || frame.size.width < 1 || frame.size.height < 1 || self.covers.count >= 4) return NO;
|
|
198
|
+
UIView *snapshot = [render snapshotViewAfterScreenUpdates:NO];
|
|
199
|
+
if (!snapshot) return NO;
|
|
200
|
+
snapshot.userInteractionEnabled = NO; snapshot.accessibilityElementsHidden = YES;
|
|
201
|
+
snapshot.layer.anchorPoint = CGPointZero; snapshot.frame = frame;
|
|
202
|
+
// Ground points below the horizon at any supported pitch, in map view coordinates.
|
|
203
|
+
CGFloat width = frame.size.width, height = frame.size.height;
|
|
204
|
+
NSMutableArray<NSValue *> *corners = [NSMutableArray array], *anchors = [NSMutableArray array];
|
|
205
|
+
for (NSValue *value in @[[NSValue valueWithCGPoint:CGPointMake(width * 0.1, height * 0.45)], [NSValue valueWithCGPoint:CGPointMake(width * 0.9, height * 0.45)],
|
|
206
|
+
[NSValue valueWithCGPoint:CGPointMake(width * 0.9, height * 0.9)], [NSValue valueWithCGPoint:CGPointMake(width * 0.1, height * 0.9)]]) {
|
|
207
|
+
CGPoint point = value.CGPointValue;
|
|
208
|
+
CLLocationCoordinate2D coordinate = [self.map convertPoint:CGPointMake(point.x + frame.origin.x, point.y + frame.origin.y) toCoordinateFromView:self.map];
|
|
209
|
+
if (!std::isfinite(coordinate.latitude) || !std::isfinite(coordinate.longitude) || std::fabs(coordinate.latitude) > 90) return NO;
|
|
210
|
+
[corners addObject:value];
|
|
211
|
+
[anchors addObject:[NSValue valueWithBytes:&coordinate objCType:@encode(CLLocationCoordinate2D)]];
|
|
212
|
+
}
|
|
213
|
+
TFSurfaceCover *cover = [TFSurfaceCover new];
|
|
214
|
+
cover.view = snapshot; cover.corners = corners; cover.anchors = anchors;
|
|
215
|
+
// Newer covers go below older ones, which keep fading on top.
|
|
216
|
+
[self.map insertSubview:snapshot aboveSubview:render];
|
|
217
|
+
[self.covers addObject:cover];
|
|
218
|
+
return YES;
|
|
219
|
+
}
|
|
220
|
+
- (void)followCovers {
|
|
221
|
+
if (!self.covers.count) return;
|
|
222
|
+
UIView *render = TFSurfaceRenderView(self.map);
|
|
223
|
+
if (!render) return;
|
|
224
|
+
CGPoint origin = render.frame.origin;
|
|
225
|
+
for (TFSurfaceCover *cover in self.covers) {
|
|
226
|
+
CGPoint from[4], to[4];
|
|
227
|
+
for (int index = 0; index < 4; index++) {
|
|
228
|
+
from[index] = cover.corners[index].CGPointValue;
|
|
229
|
+
CLLocationCoordinate2D coordinate;
|
|
230
|
+
[cover.anchors[index] getValue:&coordinate size:sizeof(coordinate)];
|
|
231
|
+
CGPoint point = [self.map convertCoordinate:coordinate toPointToView:self.map];
|
|
232
|
+
if (!std::isfinite(point.x) || !std::isfinite(point.y)) return;
|
|
233
|
+
to[index] = CGPointMake(point.x - origin.x, point.y - origin.y);
|
|
234
|
+
}
|
|
235
|
+
double h[9];
|
|
236
|
+
if (!TFThemeHomography(from, to, h)) continue;
|
|
237
|
+
[CATransaction begin]; [CATransaction setDisableActions:YES];
|
|
238
|
+
cover.view.layer.position = origin; cover.view.layer.transform = TFThemeTransform(h);
|
|
239
|
+
[CATransaction commit];
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
- (void)reveal:(double)duration resolve:(RCTPromiseResolveBlock)resolve {
|
|
243
|
+
TFSurfaceReveal *reveal = [TFSurfaceReveal new];
|
|
244
|
+
reveal.duration = duration; reveal.resolve = resolve; reveal.covers = [self.covers mutableCopy];
|
|
245
|
+
if (!reveal.covers.count) { resolve(@{@"revealed": @YES}); return; }
|
|
246
|
+
[self.reveals addObject:reveal];
|
|
247
|
+
// Fades start on the next complete frame, or after ten seconds at most.
|
|
248
|
+
if (!self.revealDeadline) {
|
|
249
|
+
__weak TFSurfaceAttachment *weakSelf = self;
|
|
250
|
+
dispatch_block_t deadline = dispatch_block_create((dispatch_block_flags_t)0, ^{ weakSelf.revealDeadline = nil; [weakSelf startReveals]; });
|
|
251
|
+
self.revealDeadline = deadline;
|
|
252
|
+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC), dispatch_get_main_queue(), deadline);
|
|
253
|
+
}
|
|
254
|
+
[self.map triggerRepaint];
|
|
255
|
+
}
|
|
256
|
+
- (void)startReveals {
|
|
257
|
+
if (self.revealDeadline) { dispatch_block_cancel(self.revealDeadline); self.revealDeadline = nil; }
|
|
258
|
+
NSArray<TFSurfaceReveal *> *reveals = [self.reveals copy];
|
|
259
|
+
[self.reveals removeAllObjects];
|
|
260
|
+
for (TFSurfaceReveal *reveal in reveals) {
|
|
261
|
+
for (TFSurfaceCover *cover in reveal.covers) {
|
|
262
|
+
if (cover.fading) continue;
|
|
263
|
+
cover.fading = YES;
|
|
264
|
+
__weak TFSurfaceAttachment *weakSelf = self;
|
|
265
|
+
[UIView animateWithDuration:reveal.duration / 1000.0 delay:0 options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction
|
|
266
|
+
animations:^{ cover.view.alpha = 0; } completion:^(__unused BOOL finished) { [weakSelf removeCover:cover]; }];
|
|
267
|
+
}
|
|
268
|
+
[self settle:reveal];
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
- (void)removeCover:(TFSurfaceCover *)cover {
|
|
272
|
+
[cover.view.layer removeAllAnimations]; [cover.view removeFromSuperview];
|
|
273
|
+
[self.covers removeObject:cover];
|
|
274
|
+
for (TFSurfaceReveal *reveal in [self.settling copy]) [self settle:reveal];
|
|
275
|
+
}
|
|
276
|
+
// A reveal resolves once every cover that existed when it was requested is gone.
|
|
277
|
+
- (void)settle:(TFSurfaceReveal *)reveal {
|
|
278
|
+
for (TFSurfaceCover *cover in reveal.covers) if ([self.covers containsObject:cover]) {
|
|
279
|
+
if (![self.settling containsObject:reveal]) [self.settling addObject:reveal];
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
[self.settling removeObject:reveal];
|
|
283
|
+
if (reveal.resolve) { RCTPromiseResolveBlock resolve = reveal.resolve; reveal.resolve = nil; resolve(@{@"revealed": @YES}); }
|
|
284
|
+
}
|
|
285
|
+
- (void)discardCovers {
|
|
286
|
+
if (self.revealDeadline) { dispatch_block_cancel(self.revealDeadline); self.revealDeadline = nil; }
|
|
287
|
+
for (TFSurfaceCover *cover in [self.covers copy]) { [cover.view.layer removeAllAnimations]; [cover.view removeFromSuperview]; }
|
|
288
|
+
[self.covers removeAllObjects];
|
|
289
|
+
NSArray *reveals = [self.reveals arrayByAddingObjectsFromArray:self.settling];
|
|
290
|
+
[self.reveals removeAllObjects]; [self.settling removeAllObjects];
|
|
291
|
+
for (TFSurfaceReveal *reveal in reveals) if (reveal.resolve) { RCTPromiseResolveBlock resolve = reveal.resolve; reveal.resolve = nil; resolve(@{@"revealed": @YES}); }
|
|
292
|
+
}
|
|
293
|
+
- (NSUInteger)applyThemeValues:(NSDictionary *)values {
|
|
294
|
+
MLNStyle *style = self.style;
|
|
295
|
+
if (!style) TFSurfaceInvalid();
|
|
296
|
+
if (![self.themedToken isEqual:self.token]) {
|
|
297
|
+
// Layers and original artwork belong to one style.
|
|
298
|
+
self.themedToken = self.token; self.themedLayers = [NSMutableDictionary dictionary]; self.themedImages = [NSMutableDictionary dictionary];
|
|
299
|
+
}
|
|
300
|
+
NSUInteger applied = 0;
|
|
301
|
+
for (NSString *group in @[@"paint", @"layout"]) {
|
|
302
|
+
id entries = values[group];
|
|
303
|
+
if (!entries) continue;
|
|
304
|
+
if (![entries isKindOfClass:NSArray.class] || [entries count] > 8192) TFSurfaceInvalid();
|
|
305
|
+
for (id entry in entries) {
|
|
306
|
+
if (![entry isKindOfClass:NSArray.class] || [entry count] != 3 || ![entry[0] isKindOfClass:NSString.class] || ![entry[1] isKindOfClass:NSString.class]) TFSurfaceInvalid();
|
|
307
|
+
NSString *identifier = entry[0];
|
|
308
|
+
MLNStyleLayer *layer = self.themedLayers[identifier];
|
|
309
|
+
if (!layer) {
|
|
310
|
+
layer = [style layerWithIdentifier:identifier];
|
|
311
|
+
if (!layer) continue;
|
|
312
|
+
self.themedLayers[identifier] = layer;
|
|
313
|
+
}
|
|
314
|
+
if ([entry[1] isEqual:@"visibility"]) {
|
|
315
|
+
// Per-theme copies of a layer switch by visibility, a plain flag.
|
|
316
|
+
layer.visible = ![entry[2] isEqual:@"none"];
|
|
317
|
+
applied++;
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
NSString *key = TFThemeStyleKey(entry[1]);
|
|
321
|
+
if (![layer respondsToSelector:NSSelectorFromString(key)] || !TFThemeCameraValue(entry[2], 0)) continue;
|
|
322
|
+
// A value MapLibre rejects keeps its previous value; the rest of the batch still applies.
|
|
323
|
+
BOOL supported;
|
|
324
|
+
NSExpression *expression = TFThemeExpression(entry[2], key, &supported);
|
|
325
|
+
if (!supported) continue;
|
|
326
|
+
@try { [layer setValue:expression forKey:key]; applied++; }
|
|
327
|
+
@catch (__unused NSException *exception) {}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
id images = values[@"images"];
|
|
331
|
+
if (images) {
|
|
332
|
+
if (![images isKindOfClass:NSArray.class] || [images count] > 64) TFSurfaceInvalid();
|
|
333
|
+
// Originals are read before any artwork is replaced, so tuples can share names.
|
|
334
|
+
for (NSArray *entry in images) {
|
|
335
|
+
if (![entry isKindOfClass:NSArray.class] || entry.count != 4) TFSurfaceInvalid();
|
|
336
|
+
for (NSUInteger index = 0; index < 3; index++) {
|
|
337
|
+
if (![entry[index] isKindOfClass:NSString.class]) TFSurfaceInvalid();
|
|
338
|
+
if (!self.themedImages[entry[index]]) { UIImage *image = [style imageForName:entry[index]]; if (image) self.themedImages[entry[index]] = image; }
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
for (NSArray *entry in images) {
|
|
342
|
+
UIImage *from = self.themedImages[entry[1]], *to = self.themedImages[entry[2]];
|
|
343
|
+
double t = TFSurfaceNumber(entry[3], 0, 1);
|
|
344
|
+
if (!from || !to) continue;
|
|
345
|
+
[style setImage:TFThemeMixImage(from, to, t) forName:entry[0]];
|
|
346
|
+
applied++;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
id light = values[@"light"];
|
|
350
|
+
if (light) {
|
|
351
|
+
if (![light isKindOfClass:NSDictionary.class]) TFSurfaceInvalid();
|
|
352
|
+
MLNLight *next = style.light ?: [MLNLight new];
|
|
353
|
+
id colour = light[@"color"], intensity = light[@"intensity"], position = light[@"position"];
|
|
354
|
+
if ([colour isKindOfClass:NSString.class]) { UIColor *parsed = TFThemeColor(colour); if (parsed) next.color = [NSExpression expressionForConstantValue:parsed]; }
|
|
355
|
+
if ([intensity isKindOfClass:NSNumber.class]) next.intensity = [NSExpression expressionForConstantValue:intensity];
|
|
356
|
+
if ([position isKindOfClass:NSArray.class] && [position count] == 3 && TFThemeNumbers(position)) {
|
|
357
|
+
MLNSphericalPosition spherical = MLNSphericalPositionMake([position[0] doubleValue], [position[1] doubleValue], [position[2] doubleValue]);
|
|
358
|
+
next.position = [NSExpression expressionForConstantValue:[NSValue valueWithMLNSphericalPosition:spherical]];
|
|
359
|
+
}
|
|
360
|
+
style.light = next;
|
|
361
|
+
applied++;
|
|
362
|
+
}
|
|
363
|
+
return applied;
|
|
364
|
+
}
|
|
152
365
|
- (void)retire {
|
|
153
366
|
if (self.retiring) return;
|
|
367
|
+
[self discardCovers];
|
|
154
368
|
self.retiring = YES; [self.state close];
|
|
155
369
|
if (self.deadline) dispatch_block_cancel(self.deadline); self.deadline = nil;
|
|
156
370
|
if (self.layoutCancel) self.layoutCancel(); self.layoutCancel = nil;
|
|
@@ -165,23 +379,23 @@ static MLRNCamera *TFSurfaceCamera(UIView *root, MLRNMapView *map) {
|
|
|
165
379
|
if ([self.previous respondsToSelector:_cmd]) [self.previous mapView:mapView didFinishLoadingStyle:style];
|
|
166
380
|
}
|
|
167
381
|
- (void)mapViewWillStartRenderingFrame:(MLNMapView *)mapView {
|
|
168
|
-
if ([self owns]) { [self sampleLayout]; [self.state frameStart:self.style]; }
|
|
382
|
+
if ([self owns]) { [self sampleLayout]; [self.state frameStart:self.style]; [self followCovers]; }
|
|
169
383
|
if ([self.previous respondsToSelector:_cmd]) [self.previous mapViewWillStartRenderingFrame:mapView];
|
|
170
384
|
}
|
|
171
385
|
- (void)mapViewDidFinishRenderingMap:(MLNMapView *)mapView fullyRendered:(BOOL)fully {
|
|
172
386
|
if ([self.previous respondsToSelector:_cmd]) [self.previous mapViewDidFinishRenderingMap:mapView fullyRendered:fully];
|
|
173
387
|
}
|
|
174
388
|
- (void)mapViewDidFinishRenderingFrame:(MLNMapView *)mapView fullyRendered:(BOOL)fully {
|
|
175
|
-
if ([self owns]) [self.state frameEnd:self.style fully:fully];
|
|
389
|
+
if ([self owns]) { [self.state frameEnd:self.style fully:fully]; if (fully && self.reveals.count) [self startReveals]; }
|
|
176
390
|
if ([self.previous respondsToSelector:_cmd]) [self.previous mapViewDidFinishRenderingFrame:mapView fullyRendered:fully];
|
|
177
391
|
}
|
|
178
392
|
- (void)mapViewDidFinishRenderingFrame:(MLNMapView *)mapView fullyRendered:(BOOL)fully frameEncodingTime:(double)encoding frameRenderingTime:(double)rendering {
|
|
179
|
-
if ([self owns]) [self.state frameEnd:self.style fully:fully];
|
|
393
|
+
if ([self owns]) { [self.state frameEnd:self.style fully:fully]; if (fully && self.reveals.count) [self startReveals]; }
|
|
180
394
|
if ([self.previous respondsToSelector:_cmd]) [self.previous mapViewDidFinishRenderingFrame:mapView fullyRendered:fully frameEncodingTime:encoding frameRenderingTime:rendering];
|
|
181
395
|
else if ([self.previous respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:)]) [self.previous mapViewDidFinishRenderingFrame:mapView fullyRendered:fully];
|
|
182
396
|
}
|
|
183
397
|
- (void)mapViewDidFinishRenderingFrame:(MLNMapView *)mapView fullyRendered:(BOOL)fully renderingStats:(MLNRenderingStats *)stats {
|
|
184
|
-
if ([self owns]) [self.state frameEnd:self.style fully:fully];
|
|
398
|
+
if ([self owns]) { [self.state frameEnd:self.style fully:fully]; if (fully && self.reveals.count) [self startReveals]; }
|
|
185
399
|
if ([self.previous respondsToSelector:_cmd]) [self.previous mapViewDidFinishRenderingFrame:mapView fullyRendered:fully renderingStats:stats];
|
|
186
400
|
else if ([self.previous respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:)]) [self.previous mapViewDidFinishRenderingFrame:mapView fullyRendered:fully];
|
|
187
401
|
}
|
|
@@ -194,7 +408,7 @@ static MLRNCamera *TFSurfaceCamera(UIView *root, MLRNMapView *map) {
|
|
|
194
408
|
else if ([self.previous respondsToSelector:@selector(mapView:regionWillChangeAnimated:)]) [self.previous mapView:mapView regionWillChangeAnimated:animated];
|
|
195
409
|
}
|
|
196
410
|
- (void)mapView:(MLNMapView *)mapView regionIsChangingWithReason:(MLNCameraChangeReason)reason {
|
|
197
|
-
if ([self owns]) [self.state gestureChange:self.view];
|
|
411
|
+
if ([self owns]) { [self.state gestureChange:self.view]; [self followCovers]; }
|
|
198
412
|
if ([self.previous respondsToSelector:_cmd]) [self.previous mapView:mapView regionIsChangingWithReason:reason];
|
|
199
413
|
else if ([self.previous respondsToSelector:@selector(mapViewRegionIsChanging:)]) [self.previous mapViewRegionIsChanging:mapView];
|
|
200
414
|
}
|
|
@@ -338,6 +552,40 @@ RCT_REMAP_METHOD(cancelCamera, cancelCameraForSurface:(NSString *)identifier seq
|
|
|
338
552
|
@try { if (!TFAdmissionValidToken(identifier)) TFSurfaceInvalid(); [self.surfaces[identifier].state cancelCommand:TFSurfaceInteger(sequence)]; resolve(@{@"cancelled": @YES}); }
|
|
339
553
|
@catch (NSException *exception) { TFSurfaceReject(reject); }
|
|
340
554
|
}
|
|
555
|
+
static double TFSurfaceDuration(id value) {
|
|
556
|
+
if (![value isKindOfClass:NSNumber.class] || CFGetTypeID((__bridge CFTypeRef)value) == CFBooleanGetTypeID()) TFSurfaceInvalid();
|
|
557
|
+
double number = [value doubleValue];
|
|
558
|
+
if (!std::isfinite(number) || number < 0 || number > 5000 || std::floor(number) != number) TFSurfaceInvalid();
|
|
559
|
+
return number;
|
|
560
|
+
}
|
|
561
|
+
RCT_REMAP_METHOD(coverSurface, coverSurface:(NSString *)identifier duration:(NSNumber *)duration resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
|
562
|
+
@try {
|
|
563
|
+
TFSurfaceAttachment *surface = [self current:identifier allowFailed:NO];
|
|
564
|
+
double milliseconds = TFSurfaceDuration(duration);
|
|
565
|
+
resolve(@{@"covered": @(milliseconds > 0 && [surface cover])});
|
|
566
|
+
} @catch (NSException *exception) { TFSurfaceReject(reject); }
|
|
567
|
+
}
|
|
568
|
+
RCT_REMAP_METHOD(revealSurface, revealSurface:(NSString *)identifier duration:(NSNumber *)duration resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
|
569
|
+
@try {
|
|
570
|
+
TFSurfaceAttachment *surface = [self current:identifier allowFailed:YES];
|
|
571
|
+
[surface reveal:TFSurfaceDuration(duration) resolve:resolve];
|
|
572
|
+
} @catch (NSException *exception) { TFSurfaceReject(reject); }
|
|
573
|
+
}
|
|
574
|
+
RCT_REMAP_METHOD(applyThemeValues, applyThemeValuesForSurface:(NSString *)identifier style:(NSString *)token values:(NSDictionary *)values resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
|
575
|
+
@try {
|
|
576
|
+
TFSurfaceAttachment *surface = [self current:identifier allowFailed:NO];
|
|
577
|
+
if (![values isKindOfClass:NSDictionary.class] || ![surface.token isEqual:token]) TFSurfaceInvalid();
|
|
578
|
+
NSUInteger applied = [surface applyThemeValues:values];
|
|
579
|
+
resolve(@{@"applied": @(applied)});
|
|
580
|
+
} @catch (NSException *exception) { TFSurfaceReject(reject); }
|
|
581
|
+
}
|
|
582
|
+
RCT_REMAP_METHOD(discardSurfaceCover, discardSurfaceCover:(NSString *)identifier resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
|
|
583
|
+
@try {
|
|
584
|
+
if (!TFAdmissionValidToken(identifier)) TFSurfaceInvalid();
|
|
585
|
+
[self.surfaces[identifier] discardCovers];
|
|
586
|
+
resolve(@{@"discarded": @YES});
|
|
587
|
+
} @catch (NSException *exception) { TFSurfaceReject(reject); }
|
|
588
|
+
}
|
|
341
589
|
- (void)retire:(TFSurfaceAttachment *)surface resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
|
|
342
590
|
[surface retire];
|
|
343
591
|
if (!surface.root.window) { [self.surfaces removeObjectForKey:surface.identifier]; resolve(@{@"detached": @YES}); return; }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tileflow/react-native",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.3",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Tileflow maps, camera control and semantic interactions for React Native.",
|
|
6
6
|
"type": "module",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@tileflow/core": ">=0.1.0-alpha.
|
|
39
|
+
"@tileflow/core": ">=0.1.0-alpha.32 <0.1.0-beta.0",
|
|
40
40
|
"@tileflow/interactions": ">=0.1.0-alpha.4 <0.1.0-beta.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|