mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-08 19:10:53 +00:00
Warp experiment
This commit is contained in:
parent
ddcd878d8a
commit
29917a97ac
@ -93,7 +93,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
case knockoutWallpaper(PresentationTheme, Bool)
|
||||
case experimentalCompatibility(Bool)
|
||||
case enableDebugDataDisplay(Bool)
|
||||
case acceleratedStickers(Bool)
|
||||
case rippleEffect(Bool)
|
||||
case browserExperiment(Bool)
|
||||
case localTranscription(Bool)
|
||||
case enableReactionOverrides(Bool)
|
||||
@ -127,7 +127,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return DebugControllerSection.web.rawValue
|
||||
case .keepChatNavigationStack, .skipReadHistory, .dustEffect, .crashOnSlowQueries, .crashOnMemoryPressure:
|
||||
return DebugControllerSection.experiments.rawValue
|
||||
case .clearTips, .resetNotifications, .crash, .fillLocalSavedMessageCache, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .resetTagHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .storiesExperiment, .storiesJpegExperiment, .playlistPlayback, .enableQuickReactionSwitch, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .browserExperiment, .localTranscription, .enableReactionOverrides, .restorePurchases, .disableReloginTokens, .liveStreamV2:
|
||||
case .clearTips, .resetNotifications, .crash, .fillLocalSavedMessageCache, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .resetTagHoles, .reindexUnread, .resetCacheIndex, .reindexCache, .resetBiometricsData, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .storiesExperiment, .storiesJpegExperiment, .playlistPlayback, .enableQuickReactionSwitch, .experimentalCompatibility, .enableDebugDataDisplay, .rippleEffect, .browserExperiment, .localTranscription, .enableReactionOverrides, .restorePurchases, .disableReloginTokens, .liveStreamV2:
|
||||
return DebugControllerSection.experiments.rawValue
|
||||
case .logTranslationRecognition, .resetTranslationStates:
|
||||
return DebugControllerSection.translation.rawValue
|
||||
@ -216,7 +216,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return 37
|
||||
case .enableDebugDataDisplay:
|
||||
return 38
|
||||
case .acceleratedStickers:
|
||||
case .rippleEffect:
|
||||
return 39
|
||||
case .browserExperiment:
|
||||
return 40
|
||||
@ -1228,12 +1228,12 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
})
|
||||
}).start()
|
||||
})
|
||||
case let .acceleratedStickers(value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: "Accelerated Stickers", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
case let .rippleEffect(value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: "Ripple", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
||||
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
|
||||
settings.acceleratedStickers = value
|
||||
settings.rippleEffect = value
|
||||
return PreferencesEntry(settings)
|
||||
})
|
||||
}).start()
|
||||
@ -1452,7 +1452,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
|
||||
entries.append(.knockoutWallpaper(presentationData.theme, experimentalSettings.knockoutWallpaper))
|
||||
entries.append(.experimentalCompatibility(experimentalSettings.experimentalCompatibility))
|
||||
entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay))
|
||||
entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers))
|
||||
entries.append(.rippleEffect(experimentalSettings.rippleEffect))
|
||||
#if DEBUG
|
||||
entries.append(.browserExperiment(experimentalSettings.browserExperiment))
|
||||
#else
|
||||
|
@ -402,7 +402,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
self.animationNode = animationNode
|
||||
}
|
||||
} else {
|
||||
let animationNode = DefaultAnimatedStickerNodeImpl(useMetalCache: item.context.sharedContext.immediateExperimentalUISettings.acceleratedStickers)
|
||||
let animationNode = DefaultAnimatedStickerNodeImpl(useMetalCache: false)
|
||||
animationNode.started = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.imageNode.alpha = 0.0
|
||||
|
@ -13,6 +13,7 @@ swift_library(
|
||||
"//submodules/Display",
|
||||
"//submodules/AsyncDisplayKit",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/TelegramUI/Components/SpaceWarpView/STCMeshView",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -0,0 +1,23 @@
|
||||
|
||||
objc_library(
|
||||
name = "STCMeshView",
|
||||
enable_modules = True,
|
||||
module_name = "STCMeshView",
|
||||
srcs = glob([
|
||||
"Sources/**/*.m",
|
||||
"Sources/**/*.h",
|
||||
]),
|
||||
hdrs = glob([
|
||||
"PublicHeaders/**/*.h",
|
||||
]),
|
||||
includes = [
|
||||
"PublicHeaders",
|
||||
],
|
||||
sdk_frameworks = [
|
||||
"Foundation",
|
||||
"UIKit",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
Copyright (c) 2014-present, Facebook, Inc.
|
||||
All rights reserved.
|
||||
|
||||
This source code is licensed under the BSD-style license found in the
|
||||
LICENSE file in the root directory of this source tree. An additional grant
|
||||
of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
/* A mesh layer allows individually transforming areas inside its subtree. */
|
||||
|
||||
@interface STCMeshLayer : CAReplicatorLayer
|
||||
|
||||
/* An array of bounds regions to use for each instance. The length
|
||||
* of this array is assumed to match `instanceCount'. Required. */
|
||||
|
||||
@property (atomic, assign) CGRect *instanceBounds;
|
||||
|
||||
/* An array of positions to use for each instance. The length
|
||||
* of this array is assumed to match `instanceCount'. Required. */
|
||||
|
||||
@property (atomic, assign) CGPoint *instancePositions;
|
||||
|
||||
/* An array of anchor points to use for each instance. The length
|
||||
* of this array is assumed to match `instanceCount'. Required. */
|
||||
|
||||
@property (atomic, assign) CGPoint *instanceAnchorPoints;
|
||||
|
||||
/* An array of transforms to apply to each instance. The length
|
||||
* of this array is assumed to match `instanceCount'. Required. */
|
||||
|
||||
@property (atomic, assign) CATransform3D *instanceTransforms;
|
||||
|
||||
/* Add content to this layer to transform it in the mesh. */
|
||||
|
||||
@property (atomic, strong) CALayer *contentLayer;
|
||||
|
||||
/* This CAReplicatorLayer property is used internally and is not
|
||||
* available for use by clients. Do not set it. */
|
||||
|
||||
@property (atomic, assign) CFTimeInterval instanceDelay NS_UNAVAILABLE;
|
||||
|
||||
/* This CAReplicatorLayer property is used internally and is not
|
||||
* available for use by clients. Do not set it. */
|
||||
|
||||
@property (atomic, assign) CATransform3D instanceTransform NS_UNAVAILABLE;
|
||||
|
||||
@end
|
||||
|
||||
@interface STCMeshLayer (UIViewSupport)
|
||||
|
||||
/* The wrapper replicator layer used to preserve a linear timespace. */
|
||||
|
||||
@property (atomic, strong) CAReplicatorLayer *wrapperLayer;
|
||||
|
||||
@end
|
@ -0,0 +1,26 @@
|
||||
/**
|
||||
Copyright (c) 2014-present, Facebook, Inc.
|
||||
All rights reserved.
|
||||
|
||||
This source code is licensed under the BSD-style license found in the
|
||||
LICENSE file in the root directory of this source tree. An additional grant
|
||||
of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <STCMeshView/STCMeshLayer.h>
|
||||
|
||||
// shows different part of its subviews with different transforms
|
||||
@interface STCMeshView : UIView
|
||||
|
||||
@property (nonatomic, retain, readonly) STCMeshLayer *layer;
|
||||
@property (nonatomic, retain, readwrite) UIView *contentView; // only subviews added to this are transformed
|
||||
|
||||
@property (nonatomic, assign, readwrite) NSInteger instanceCount; // defaults to 1
|
||||
@property (nonatomic, assign, readwrite) CATransform3D *instanceTransforms; // optional
|
||||
@property (nonatomic, assign, readwrite) CGRect *instanceBounds; // optional
|
||||
@property (nonatomic, assign, readwrite) CGPoint *instancePositions; // optional
|
||||
@property (nonatomic, assign, readwrite) CGPoint *instanceAnchorPoints; // optional
|
||||
|
||||
@end
|
@ -0,0 +1,337 @@
|
||||
/**
|
||||
Copyright (c) 2014-present, Facebook, Inc.
|
||||
All rights reserved.
|
||||
|
||||
This source code is licensed under the BSD-style license found in the
|
||||
LICENSE file in the root directory of this source tree. An additional grant
|
||||
of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <STCMeshView/STCMeshLayer.h>
|
||||
|
||||
static const CFTimeInterval STCMeshLayerTotalInstanceDelay = 10000000.0;
|
||||
static NSString *const STCMeshLayerBoundsAnimationKey = @"STCMeshLayerBoundsAnimation";
|
||||
static NSString *const STCMeshLayerTransformAnimationKey = @"STCMeshLayerTransformAnimation";
|
||||
static NSString *const STCMeshLayerPositionAnimationKey = @"STCMeshLayerPositionAnimation";
|
||||
static NSString *const STCMeshLayerAnchorPointAnimationKey = @"STCMeshLayerAnchorPointAnimation";
|
||||
static NSString *const STCMeshLayerInstanceDelayAnimationKey = @"STCMeshLayerInstanceDelayAnimation";
|
||||
|
||||
@implementation STCMeshLayer {
|
||||
CAReplicatorLayer *_wrapperLayer;
|
||||
CALayer *_contentLayer;
|
||||
|
||||
CGRect *_instanceBounds;
|
||||
CATransform3D *_instanceTransforms;
|
||||
CGPoint *_instancePositions;
|
||||
CGPoint *_instanceAnchorPoints;
|
||||
}
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
self.wrapperLayer = [[CAReplicatorLayer alloc] init];
|
||||
self.contentLayer = [[CALayer alloc] init];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
free(_instanceTransforms);
|
||||
_instanceTransforms = NULL;
|
||||
free(_instanceBounds);
|
||||
_instanceBounds = NULL;
|
||||
}
|
||||
|
||||
- (void)layoutSublayers
|
||||
{
|
||||
[super layoutSublayers];
|
||||
|
||||
_wrapperLayer.frame = self.bounds;
|
||||
_contentLayer.frame = _wrapperLayer.bounds;
|
||||
[self _updateMeshAnimations];
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
@dynamic instanceDelay;
|
||||
|
||||
@dynamic instanceTransform;
|
||||
|
||||
- (void)setInstanceCount:(NSInteger)instanceCount
|
||||
{
|
||||
if (instanceCount != self.instanceCount) {
|
||||
[super setInstanceCount:instanceCount];
|
||||
|
||||
free(_instanceTransforms);
|
||||
_instanceTransforms = NULL;
|
||||
free(_instanceBounds);
|
||||
_instanceBounds = NULL;
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
}
|
||||
|
||||
- (CATransform3D *)instanceTransforms
|
||||
{
|
||||
CATransform3D *instanceTransforms = _instanceTransforms;
|
||||
|
||||
return instanceTransforms;
|
||||
}
|
||||
|
||||
- (void)setInstanceTransforms:(CATransform3D *)instanceTransforms
|
||||
{
|
||||
free(_instanceTransforms);
|
||||
_instanceTransforms = NULL;
|
||||
|
||||
if (instanceTransforms != NULL) {
|
||||
_instanceTransforms = calloc(sizeof(CATransform3D), self.instanceCount);
|
||||
memcpy(_instanceTransforms, instanceTransforms, self.instanceCount * sizeof(CATransform3D));
|
||||
}
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (CGPoint *)instancePositions
|
||||
{
|
||||
CGPoint *instancePositions = _instancePositions;
|
||||
|
||||
return instancePositions;
|
||||
}
|
||||
|
||||
- (void)setInstancePositions:(CGPoint *)instancePositions
|
||||
{
|
||||
free(_instancePositions);
|
||||
_instancePositions = NULL;
|
||||
|
||||
if (instancePositions != NULL) {
|
||||
_instancePositions = calloc(sizeof(CGPoint), self.instanceCount);
|
||||
memcpy(_instancePositions, instancePositions, self.instanceCount * sizeof(CGPoint));
|
||||
}
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (CGPoint *)instanceAnchorPoints
|
||||
{
|
||||
CGPoint *instanceAnchorPoints = _instanceAnchorPoints;
|
||||
|
||||
return instanceAnchorPoints;
|
||||
}
|
||||
|
||||
- (void)setInstanceAnchorPoints:(CGPoint *)instanceAnchorPoints
|
||||
{
|
||||
free(_instanceAnchorPoints);
|
||||
_instanceAnchorPoints = NULL;
|
||||
|
||||
if (instanceAnchorPoints != NULL) {
|
||||
_instanceAnchorPoints = calloc(sizeof(CGPoint), self.instanceCount);
|
||||
memcpy(_instanceAnchorPoints, instanceAnchorPoints, self.instanceCount * sizeof(CGPoint));
|
||||
}
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (CGRect *)instanceBounds
|
||||
{
|
||||
CGRect *instanceBounds = _instanceBounds;
|
||||
|
||||
return instanceBounds;
|
||||
}
|
||||
|
||||
- (void)setInstanceBounds:(CGRect *)instanceBounds
|
||||
{
|
||||
free(_instanceBounds);
|
||||
_instanceBounds = NULL;
|
||||
|
||||
if (instanceBounds != NULL) {
|
||||
_instanceBounds = calloc(sizeof(CGRect), self.instanceCount);
|
||||
memcpy(_instanceBounds, instanceBounds, self.instanceCount * sizeof(CGRect));
|
||||
}
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (CALayer *)contentLayer
|
||||
{
|
||||
CALayer *contentLayer = _contentLayer;
|
||||
|
||||
return contentLayer;
|
||||
}
|
||||
|
||||
- (void)setContentLayer:(CALayer *)contentLayer
|
||||
{
|
||||
if (contentLayer != _contentLayer) {
|
||||
if (_contentLayer != nil) {
|
||||
[_contentLayer removeFromSuperlayer];
|
||||
}
|
||||
|
||||
_contentLayer = contentLayer;
|
||||
|
||||
if (_contentLayer != nil) {
|
||||
[_wrapperLayer addSublayer:_contentLayer];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (CAReplicatorLayer *)wrapperLayer
|
||||
{
|
||||
CAReplicatorLayer *wrapperLayer = _wrapperLayer;
|
||||
|
||||
return wrapperLayer;
|
||||
}
|
||||
|
||||
- (void)setWrapperLayer:(CAReplicatorLayer *)wrapperLayer
|
||||
{
|
||||
if (wrapperLayer != _wrapperLayer) {
|
||||
if (_contentLayer != nil) {
|
||||
[_contentLayer removeFromSuperlayer];
|
||||
}
|
||||
|
||||
if (_wrapperLayer != nil) {
|
||||
[_wrapperLayer removeFromSuperlayer];
|
||||
}
|
||||
|
||||
_wrapperLayer = wrapperLayer;
|
||||
|
||||
if (_wrapperLayer != nil) {
|
||||
_wrapperLayer.masksToBounds = YES;
|
||||
_wrapperLayer.instanceCount = 2;
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGColorRef hiddenColor = CGColorCreate(colorSpace, (CGFloat []){ 1.0, 1.0, 1.0, 0.0 });
|
||||
_wrapperLayer.instanceColor = hiddenColor;
|
||||
CGColorRelease(hiddenColor);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
_wrapperLayer.instanceAlphaOffset = 1.0;
|
||||
[self addSublayer:_wrapperLayer];
|
||||
}
|
||||
|
||||
if (_contentLayer != nil) {
|
||||
[_wrapperLayer addSublayer:_contentLayer];
|
||||
}
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Internal Methods
|
||||
|
||||
- (CGRect)_boundsAtIndex:(NSUInteger)index
|
||||
{
|
||||
CGRect bounds = CGRectZero;
|
||||
|
||||
if (_instanceBounds != NULL) {
|
||||
bounds = _instanceBounds[index];
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
- (CATransform3D)_transformAtIndex:(NSUInteger)index
|
||||
{
|
||||
CATransform3D transform = CATransform3DIdentity;
|
||||
|
||||
if (_instanceTransforms != NULL) {
|
||||
transform = _instanceTransforms[index];
|
||||
}
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
||||
- (CGPoint)_positionAtIndex:(NSUInteger)index
|
||||
{
|
||||
CGPoint position = CGPointZero;
|
||||
|
||||
if (_instancePositions != NULL) {
|
||||
position = _instancePositions[index];
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
- (CGPoint)_anchorPointAtIndex:(NSUInteger)index
|
||||
{
|
||||
CGPoint anchorPoint = CGPointMake(0.5, 0.5);
|
||||
|
||||
if (_instanceAnchorPoints != NULL) {
|
||||
anchorPoint = _instanceAnchorPoints[index];
|
||||
}
|
||||
|
||||
return anchorPoint;
|
||||
}
|
||||
|
||||
- (void)_updateMeshAnimations
|
||||
{
|
||||
[_wrapperLayer removeAllAnimations];
|
||||
|
||||
super.instanceDelay = -STCMeshLayerTotalInstanceDelay / self.instanceCount;
|
||||
|
||||
CAKeyframeAnimation *boundsAnimation = [CAKeyframeAnimation animationWithKeyPath:@"bounds"];
|
||||
boundsAnimation.calculationMode = kCAAnimationDiscrete;
|
||||
boundsAnimation.duration = STCMeshLayerTotalInstanceDelay;
|
||||
boundsAnimation.removedOnCompletion = NO;
|
||||
NSMutableArray *boundsValues = [NSMutableArray array];
|
||||
for (NSUInteger i = 0; i < self.instanceCount; i++) {
|
||||
CGRect bounds = [self _boundsAtIndex:i];
|
||||
NSValue *boundsValue = [NSValue valueWithBytes:&bounds objCType:@encode(CGRect)];
|
||||
[boundsValues addObject:boundsValue];
|
||||
}
|
||||
boundsAnimation.values = boundsValues;
|
||||
[_wrapperLayer addAnimation:boundsAnimation forKey:STCMeshLayerBoundsAnimationKey];
|
||||
|
||||
CAKeyframeAnimation *transformAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
|
||||
transformAnimation.calculationMode = kCAAnimationDiscrete;
|
||||
transformAnimation.duration = STCMeshLayerTotalInstanceDelay;
|
||||
transformAnimation.removedOnCompletion = NO;
|
||||
NSMutableArray *transformValues = [NSMutableArray array];
|
||||
for (NSUInteger i = 0; i < self.instanceCount; i++) {
|
||||
CATransform3D transform = [self _transformAtIndex:i];
|
||||
NSValue *transformValue = [NSValue valueWithCATransform3D:transform];
|
||||
[transformValues addObject:transformValue];
|
||||
}
|
||||
transformAnimation.values = transformValues;
|
||||
[_wrapperLayer addAnimation:transformAnimation forKey:STCMeshLayerTransformAnimationKey];
|
||||
|
||||
CAKeyframeAnimation *positionAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
|
||||
positionAnimation.calculationMode = kCAAnimationDiscrete;
|
||||
positionAnimation.duration = STCMeshLayerTotalInstanceDelay;
|
||||
positionAnimation.removedOnCompletion = NO;
|
||||
NSMutableArray *positionValues = [NSMutableArray array];
|
||||
for (NSUInteger i = 0; i < self.instanceCount; i++) {
|
||||
CGPoint position = [self _positionAtIndex:i];
|
||||
NSValue *positionValue = [NSValue valueWithBytes:&position objCType:@encode(CGPoint)];
|
||||
[positionValues addObject:positionValue];
|
||||
}
|
||||
positionAnimation.values = positionValues;
|
||||
[_wrapperLayer addAnimation:positionAnimation forKey:STCMeshLayerPositionAnimationKey];
|
||||
|
||||
CAKeyframeAnimation *anchorPointAnimation = [CAKeyframeAnimation animationWithKeyPath:@"anchorPoint"];
|
||||
anchorPointAnimation.calculationMode = kCAAnimationDiscrete;
|
||||
anchorPointAnimation.duration = STCMeshLayerTotalInstanceDelay;
|
||||
anchorPointAnimation.removedOnCompletion = NO;
|
||||
NSMutableArray *anchorPointValues = [NSMutableArray array];
|
||||
for (NSUInteger i = 0; i < self.instanceCount; i++) {
|
||||
CGPoint anchorPoint = [self _anchorPointAtIndex:i];
|
||||
NSValue *anchorPointValue = [NSValue valueWithBytes:&anchorPoint objCType:@encode(CGPoint)];
|
||||
[anchorPointValues addObject:anchorPointValue];
|
||||
}
|
||||
anchorPointAnimation.values = anchorPointValues;
|
||||
[_wrapperLayer addAnimation:anchorPointAnimation forKey:STCMeshLayerAnchorPointAnimationKey];
|
||||
|
||||
CAKeyframeAnimation *timeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"instanceDelay"];
|
||||
timeAnimation.calculationMode = kCAAnimationDiscrete;
|
||||
timeAnimation.duration = STCMeshLayerTotalInstanceDelay;
|
||||
timeAnimation.removedOnCompletion = NO;
|
||||
NSMutableArray *timeValues = [NSMutableArray array];
|
||||
for (NSUInteger i = 0; i < self.instanceCount; i++) {
|
||||
CFTimeInterval delay = -super.instanceDelay * i;
|
||||
[timeValues addObject:@(delay)];
|
||||
}
|
||||
timeAnimation.values = timeValues;
|
||||
[_wrapperLayer addAnimation:timeAnimation forKey:STCMeshLayerInstanceDelayAnimationKey];
|
||||
}
|
||||
|
||||
@end
|
@ -0,0 +1,126 @@
|
||||
/**
|
||||
Copyright (c) 2014-present, Facebook, Inc.
|
||||
All rights reserved.
|
||||
|
||||
This source code is licensed under the BSD-style license found in the
|
||||
LICENSE file in the root directory of this source tree. An additional grant
|
||||
of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <STCMeshView/STCMeshView.h>
|
||||
#import <STCMeshView/STCMeshLayer.h>
|
||||
|
||||
@interface _STCMeshViewReplicatorView : UIView
|
||||
|
||||
@property (nonatomic, readonly, retain) CAReplicatorLayer *layer;
|
||||
|
||||
@end
|
||||
|
||||
@implementation _STCMeshViewReplicatorView
|
||||
|
||||
- (CAReplicatorLayer *)layer
|
||||
{
|
||||
return (CAReplicatorLayer *)[super layer];
|
||||
}
|
||||
|
||||
+ (Class)layerClass
|
||||
{
|
||||
return [CAReplicatorLayer class];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation STCMeshView {
|
||||
_STCMeshViewReplicatorView *_wrapperView;
|
||||
}
|
||||
|
||||
- (STCMeshLayer *)layer
|
||||
{
|
||||
return (STCMeshLayer *)[super layer];
|
||||
}
|
||||
|
||||
+ (Class)layerClass
|
||||
{
|
||||
return [STCMeshLayer class];
|
||||
}
|
||||
|
||||
- (NSInteger)instanceCount
|
||||
{
|
||||
return self.layer.instanceCount;
|
||||
}
|
||||
|
||||
- (void)setInstanceCount:(NSInteger)instanceCount
|
||||
{
|
||||
self.layer.instanceCount = instanceCount;
|
||||
}
|
||||
|
||||
- (CATransform3D *)instanceTransforms
|
||||
{
|
||||
return self.layer.instanceTransforms;
|
||||
}
|
||||
|
||||
- (void)setInstanceTransforms:(CATransform3D *)instanceTransforms
|
||||
{
|
||||
self.layer.instanceTransforms = instanceTransforms;
|
||||
}
|
||||
|
||||
- (CGRect *)instanceBounds
|
||||
{
|
||||
return self.layer.instanceBounds;
|
||||
}
|
||||
|
||||
- (void)setInstanceBounds:(CGRect *)instanceBounds
|
||||
{
|
||||
self.layer.instanceBounds = instanceBounds;
|
||||
}
|
||||
|
||||
- (CGPoint *)instancePositions
|
||||
{
|
||||
return self.layer.instancePositions;
|
||||
}
|
||||
|
||||
- (void)setInstancePositions:(CGPoint *)instancePositions
|
||||
{
|
||||
self.layer.instancePositions = instancePositions;
|
||||
}
|
||||
|
||||
- (CGPoint *)instanceAnchorPoints
|
||||
{
|
||||
return self.layer.instanceAnchorPoints;
|
||||
}
|
||||
|
||||
- (void)setInstanceAnchorPoints:(CGPoint *)instanceAnchorPoints
|
||||
{
|
||||
self.layer.instanceAnchorPoints = instanceAnchorPoints;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if ((self = [super initWithFrame:frame])) {
|
||||
_wrapperView = [[_STCMeshViewReplicatorView alloc] init];
|
||||
[self addSubview:_wrapperView];
|
||||
self.layer.wrapperLayer = _wrapperView.layer;
|
||||
|
||||
self.contentView = [[UIView alloc] init];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setContentView:(UIView *)contentView
|
||||
{
|
||||
if (contentView != _contentView) {
|
||||
if (_contentView != nil) {
|
||||
[_contentView removeFromSuperview];
|
||||
}
|
||||
|
||||
if (contentView != nil) {
|
||||
[_wrapperView addSubview:contentView];
|
||||
}
|
||||
|
||||
_contentView = contentView;
|
||||
self.layer.contentLayer = _contentView.layer;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
@ -3,104 +3,57 @@ import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import ComponentFlow
|
||||
import STCMeshView
|
||||
|
||||
/*open class SpaceWarpView: UIView {
|
||||
private final class WarpPartView: UIView {
|
||||
let cloneView: PortalView
|
||||
|
||||
init?(contentView: PortalSourceView) {
|
||||
guard let cloneView = PortalView(matchPosition: false) else {
|
||||
return nil
|
||||
}
|
||||
self.cloneView = cloneView
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.layer.anchorPoint = CGPoint(x: 0.5, y: 0.0)
|
||||
|
||||
self.clipsToBounds = true
|
||||
self.addSubview(cloneView.view)
|
||||
contentView.addPortal(view: cloneView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(containerSize: CGSize, rect: CGRect, transition: ComponentTransition) {
|
||||
transition.setFrame(view: self.cloneView.view, frame: CGRect(origin: CGPoint(x: -rect.minX, y: -rect.minY), size: CGSize(width: containerSize.width, height: containerSize.height)))
|
||||
}
|
||||
}
|
||||
private final class FPSView: UIView {
|
||||
private var lastTimestamp: Double?
|
||||
private var counter: Int = 0
|
||||
private var fpsValue: Int?
|
||||
private var fpsString: NSAttributedString?
|
||||
|
||||
public var contentView: UIView {
|
||||
return self.contentViewImpl
|
||||
}
|
||||
|
||||
let contentViewImpl: PortalSourceView
|
||||
|
||||
private var warpViews: [WarpPartView] = []
|
||||
|
||||
override public init(frame: CGRect) {
|
||||
self.contentViewImpl = PortalSourceView()
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.contentView)
|
||||
self.contentView.alpha = 0.1
|
||||
|
||||
for _ in 0 ..< 8 {
|
||||
if let warpView = WarpPartView(contentView: self.contentViewImpl) {
|
||||
self.warpViews.append(warpView)
|
||||
self.addSubview(warpView)
|
||||
}
|
||||
}
|
||||
self.layer.anchorPoint = CGPoint()
|
||||
self.backgroundColor = .black
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public func update(size: CGSize, warpHeight: CGFloat, transition: ComponentTransition) {
|
||||
transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
let allItemsHeight = warpHeight * 0.5
|
||||
for i in 0 ..< self.warpViews.count {
|
||||
let itemHeight = warpHeight / CGFloat(self.warpViews.count)
|
||||
let itemFraction = CGFloat(i + 1) / CGFloat(self.warpViews.count)
|
||||
let _ = itemHeight
|
||||
|
||||
let da = CGFloat.pi * 0.5 / CGFloat(self.warpViews.count)
|
||||
let alpha = CGFloat.pi * 0.5 - itemFraction * CGFloat.pi * 0.5
|
||||
let endPoint = CGPoint(x: cos(alpha), y: sin(alpha))
|
||||
let prevAngle = alpha + da
|
||||
let prevPt = CGPoint(x: cos(prevAngle), y: sin(prevAngle))
|
||||
var angle: CGFloat
|
||||
angle = -atan2(endPoint.y - prevPt.y, endPoint.x - prevPt.x)
|
||||
|
||||
let itemLengthVector = CGPoint(x: endPoint.x - prevPt.x, y: endPoint.y - prevPt.y)
|
||||
let itemLength = sqrt(itemLengthVector.x * itemLengthVector.x + itemLengthVector.y * itemLengthVector.y) * warpHeight * 0.5
|
||||
let _ = itemLength
|
||||
|
||||
var transform: CATransform3D
|
||||
transform = CATransform3DIdentity
|
||||
transform.m34 = 1.0 / 240.0
|
||||
|
||||
transform = CATransform3DTranslate(transform, 0.0, prevPt.x * allItemsHeight, (1.0 - prevPt.y) * allItemsHeight)
|
||||
transform = CATransform3DRotate(transform, angle, 1.0, 0.0, 0.0)
|
||||
|
||||
let positionY = size.height - allItemsHeight + 4.0 + CGFloat(i) * itemLength
|
||||
let rect = CGRect(origin: CGPoint(x: 0.0, y: positionY), size: CGSize(width: size.width, height: itemLength))
|
||||
transition.setPosition(view: self.warpViews[i], position: CGPoint(x: rect.midX, y: 4.0))
|
||||
transition.setBounds(view: self.warpViews[i], bounds: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: itemLength)))
|
||||
transition.setTransform(view: self.warpViews[i], transform: transform)
|
||||
self.warpViews[i].update(containerSize: size, rect: rect, transition: transition)
|
||||
func update() {
|
||||
self.counter += 1
|
||||
let timestamp = CACurrentMediaTime()
|
||||
let deltaTime: Double
|
||||
if let lastTimestamp = self.lastTimestamp {
|
||||
deltaTime = timestamp - lastTimestamp
|
||||
} else {
|
||||
deltaTime = 1.0 / 60.0
|
||||
self.lastTimestamp = timestamp
|
||||
}
|
||||
if deltaTime >= 1.0 {
|
||||
let fpsValue = Int(Double(self.counter) / deltaTime)
|
||||
if self.fpsValue != fpsValue {
|
||||
self.fpsValue = fpsValue
|
||||
let fpsString = NSAttributedString(string: "\(fpsValue)", attributes: [.foregroundColor: UIColor.white])
|
||||
self.bounds = fpsString.boundingRect(with: CGSize(width: 100.0, height: 100.0), context: nil).integral
|
||||
self.fpsString = fpsString
|
||||
self.setNeedsDisplay()
|
||||
}
|
||||
self.counter = 0
|
||||
self.lastTimestamp = timestamp
|
||||
}
|
||||
}
|
||||
|
||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
return self.contentView.hitTest(point, with: event)
|
||||
|
||||
override func draw(_ rect: CGRect) {
|
||||
guard let fpsString = self.fpsString else {
|
||||
return
|
||||
}
|
||||
|
||||
fpsString.draw(at: CGPoint())
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
private extension CGPoint {
|
||||
static func -(lhs: CGPoint, rhs: CGPoint) -> CGPoint {
|
||||
@ -148,7 +101,7 @@ private func transformCoordinate(
|
||||
// The distance of the current pixel position from `origin`.
|
||||
let distance = length(position - origin)
|
||||
|
||||
if distance < 10.0 {
|
||||
if distance < 2.0 {
|
||||
return position
|
||||
}
|
||||
|
||||
@ -172,7 +125,7 @@ private func transformCoordinate(
|
||||
//
|
||||
// This new position moves toward or away from `origin` based on the
|
||||
// sign and magnitude of `rippleAmount`.
|
||||
let newPosition = position + n * rippleAmount
|
||||
let newPosition = position - n * rippleAmount
|
||||
return newPosition
|
||||
}
|
||||
|
||||
@ -222,12 +175,103 @@ private func rectToQuad(
|
||||
i = kEpsilon * (i > 0 ? 1.0 : -1.0)
|
||||
}
|
||||
|
||||
//CATransform3D transform = {a/i, d/i, 0, g/i, b/i, e/i, 0, h/i, 0, 0, 1, 0, c/i, f/i, 0, 1.0}
|
||||
let transform = CATransform3D(m11: a/i, m12: d/i, m13: 0, m14: g/i, m21: b/i, m22: e/i, m23: 0, m24: h/i, m31: 0, m32: 0, m33: 1, m34: 0, m41: c/i, m42: f/i, m43: 0, m44: 1.0)
|
||||
return transform
|
||||
}
|
||||
|
||||
open class SpaceWarpView: UIView {
|
||||
func transformToFitQuad(frame: CGRect, topLeft tl: CGPoint, topRight tr: CGPoint, bottomLeft bl: CGPoint, bottomRight br: CGPoint) -> CATransform3D {
|
||||
/*let boundingBox = UIView.boundingBox(forQuadWithTR: tr, tl: tl, bl: bl, br: br)
|
||||
self.layer.transform = CATransform3DIdentity // keeps current transform from interfering
|
||||
self.frame = boundingBox*/
|
||||
|
||||
let frameTopLeft = frame.origin
|
||||
let transform = rectToQuad2(
|
||||
rect: CGRect(origin: CGPoint(), size: frame.size),
|
||||
quadTL: CGPoint(x: tl.x - frameTopLeft.x, y: tl.y - frameTopLeft.y),
|
||||
quadTR: CGPoint(x: tr.x - frameTopLeft.x, y: tr.y - frameTopLeft.y),
|
||||
quadBL: CGPoint(x: bl.x - frameTopLeft.x, y: bl.y - frameTopLeft.y),
|
||||
quadBR: CGPoint(x: br.x - frameTopLeft.x, y: br.y - frameTopLeft.y)
|
||||
)
|
||||
|
||||
// To account for anchor point, we must translate, transform, translate
|
||||
let anchorPoint = frame.center
|
||||
let anchorOffset = CGPoint(x: anchorPoint.x - frame.origin.x, y: anchorPoint.y - frame.origin.y)
|
||||
let transPos = CATransform3DMakeTranslation(anchorOffset.x, anchorOffset.y, 0)
|
||||
let transNeg = CATransform3DMakeTranslation(-anchorOffset.x, -anchorOffset.y, 0)
|
||||
let fullTransform = CATransform3DConcat(CATransform3DConcat(transPos, transform), transNeg)
|
||||
|
||||
return fullTransform
|
||||
}
|
||||
|
||||
private func boundingBox(forQuadWithTR tr: CGPoint, tl: CGPoint, bl: CGPoint, br: CGPoint) -> CGRect {
|
||||
var boundingBox = CGRect.zero
|
||||
|
||||
let xmin = min(min(min(tr.x, tl.x), bl.x), br.x)
|
||||
let ymin = min(min(min(tr.y, tl.y), bl.y), br.y)
|
||||
let xmax = max(max(max(tr.x, tl.x), bl.x), br.x)
|
||||
let ymax = max(max(max(tr.y, tl.y), bl.y), br.y)
|
||||
|
||||
boundingBox.origin.x = xmin
|
||||
boundingBox.origin.y = ymin
|
||||
boundingBox.size.width = xmax - xmin
|
||||
boundingBox.size.height = ymax - ymin
|
||||
|
||||
return boundingBox
|
||||
}
|
||||
|
||||
func rectToQuad2(rect: CGRect, quadTL topLeft: CGPoint, quadTR topRight: CGPoint, quadBL bottomLeft: CGPoint, quadBR bottomRight: CGPoint) -> CATransform3D {
|
||||
return rectToQuad(rect: rect, quadTLX: topLeft.x, quadTLY: topLeft.y, quadTRX: topRight.x, quadTRY: topRight.y, quadBLX: bottomLeft.x, quadBLY: bottomLeft.y, quadBRX: bottomRight.x, quadBRY: bottomRight.y)
|
||||
}
|
||||
|
||||
private func rectToQuad(rect: CGRect, quadTLX x1a: CGFloat, quadTLY y1a: CGFloat, quadTRX x2a: CGFloat, quadTRY y2a: CGFloat, quadBLX x3a: CGFloat, quadBLY y3a: CGFloat, quadBRX x4a: CGFloat, quadBRY y4a: CGFloat) -> CATransform3D {
|
||||
let X = rect.origin.x
|
||||
let Y = rect.origin.y
|
||||
let W = rect.size.width
|
||||
let H = rect.size.height
|
||||
|
||||
let y21 = y2a - y1a
|
||||
let y32 = y3a - y2a
|
||||
let y43 = y4a - y3a
|
||||
let y14 = y1a - y4a
|
||||
let y31 = y3a - y1a
|
||||
let y42 = y4a - y2a
|
||||
|
||||
let a = -H * (x2a * x3a * y14 + x2a * x4a * y31 - x1a * x4a * y32 + x1a * x3a * y42)
|
||||
let b = W * (x2a * x3a * y14 + x3a * x4a * y21 + x1a * x4a * y32 + x1a * x2a * y43)
|
||||
let c = H * X * (x2a * x3a * y14 + x2a * x4a * y31 - x1a * x4a * y32 + x1a * x3a * y42) - H * W * x1a * (x4a * y32 - x3a * y42 + x2a * y43) - W * Y * (x2a * x3a * y14 + x3a * x4a * y21 + x1a * x4a * y32 + x1a * x2a * y43)
|
||||
|
||||
let d = H * (-x4a * y21 * y3a + x2a * y1a * y43 - x1a * y2a * y43 - x3a * y1a * y4a + x3a * y2a * y4a)
|
||||
let e = W * (x4a * y2a * y31 - x3a * y1a * y42 - x2a * y31 * y4a + x1a * y3a * y42)
|
||||
let f = -(W * (x4a * (Y * y2a * y31 + H * y1a * y32) - x3a * (H + Y) * y1a * y42 + H * x2a * y1a * y43 + x2a * Y * (y1a - y3a) * y4a + x1a * Y * y3a * (-y2a + y4a)) - H * X * (x4a * y21 * y3a - x2a * y1a * y43 + x3a * (y1a - y2a) * y4a + x1a * y2a * (-y3a + y4a)))
|
||||
|
||||
let g = H * (x3a * y21 - x4a * y21 + (-x1a + x2a) * y43)
|
||||
let h = W * (-x2a * y31 + x4a * y31 + (x1a - x3a) * y42)
|
||||
var i = W * Y * (x2a * y31 - x4a * y31 - x1a * y42 + x3a * y42) + H * (X * (-(x3a * y21) + x4a * y21 + x1a * y43 - x2a * y43) + W * (-(x3a * y2a) + x4a * y2a + x2a * y3a - x4a * y3a - x2a * y4a + x3a * y4a))
|
||||
|
||||
let kEpsilon = 0.0001
|
||||
|
||||
if abs(i) < kEpsilon {
|
||||
i = kEpsilon * (i > 0 ? 1.0 : -1.0)
|
||||
}
|
||||
|
||||
let transform = CATransform3D(
|
||||
m11: a / i, m12: d / i, m13: 0, m14: g / i,
|
||||
m21: b / i, m22: e / i, m23: 0, m24: h / i,
|
||||
m31: 0, m32: 0, m33: 1, m34: 0,
|
||||
m41: c / i, m42: f / i, m43: 0, m44: 1.0
|
||||
)
|
||||
|
||||
return transform
|
||||
}
|
||||
|
||||
public protocol SpaceWarpView: UIView {
|
||||
var contentView: UIView { get }
|
||||
|
||||
func trigger(at point: CGPoint)
|
||||
func update(size: CGSize, transition: ComponentTransition)
|
||||
}
|
||||
|
||||
open class SpaceWarpView1: UIView, SpaceWarpView {
|
||||
private final class GridView: UIView {
|
||||
let cloneView: PortalView
|
||||
let gridPosition: CGPoint
|
||||
@ -410,6 +454,598 @@ open class SpaceWarpView: UIView {
|
||||
}
|
||||
|
||||
|
||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if self.alpha.isZero || self.isHidden || !self.isUserInteractionEnabled {
|
||||
return nil
|
||||
}
|
||||
for view in self.contentView.subviews.reversed() {
|
||||
if let result = view.hitTest(self.convert(point, to: view), with: event), result.isUserInteractionEnabled {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
let result = super.hitTest(point, with: event)
|
||||
if result != self {
|
||||
return result
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class SpaceWarpView2: UIView, SpaceWarpView {
|
||||
public var contentView: UIView {
|
||||
return self.contentViewImpl
|
||||
}
|
||||
|
||||
private let contentViewImpl: UIView
|
||||
private var meshView: STCMeshView?
|
||||
|
||||
private var link: SharedDisplayLinkDriver.Link?
|
||||
private var startPoint: CGPoint?
|
||||
|
||||
private var timeValue: CGFloat = 0.0
|
||||
|
||||
private var resolution: (x: Int, y: Int)?
|
||||
private var size: CGSize?
|
||||
|
||||
override public init(frame: CGRect) {
|
||||
self.contentViewImpl = UIView()
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.contentView)
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public func trigger(at point: CGPoint) {
|
||||
self.startPoint = point
|
||||
self.timeValue = 0.0
|
||||
|
||||
if self.link == nil {
|
||||
self.link = SharedDisplayLinkDriver.shared.add(framesPerSecond: .max, { [weak self] deltaTime in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.timeValue += deltaTime * (1.0 / CGFloat(UIView.animationDurationFactor()))
|
||||
|
||||
if let size = self.size {
|
||||
self.update(size: size, transition: .immediate)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private func updateGrid(resolutionX: Int, resolutionY: Int) {
|
||||
if let resolution = self.resolution, resolution.x == resolutionX, resolution.y == resolutionY {
|
||||
return
|
||||
}
|
||||
self.resolution = (resolutionX, resolutionY)
|
||||
|
||||
if let meshView = self.meshView {
|
||||
self.meshView = nil
|
||||
meshView.removeFromSuperview()
|
||||
self.contentViewImpl.removeFromSuperview()
|
||||
}
|
||||
|
||||
let meshView = STCMeshView(frame: CGRect())
|
||||
self.meshView = meshView
|
||||
self.addSubview(meshView)
|
||||
|
||||
meshView.instanceCount = resolutionX * resolutionY
|
||||
|
||||
meshView.contentView.addSubview(self.contentViewImpl)
|
||||
|
||||
/*for gridView in self.gridViews {
|
||||
gridView.removeFromSuperview()
|
||||
}
|
||||
|
||||
var gridViews: [GridView] = []
|
||||
for y in 0 ..< resolutionY {
|
||||
for x in 0 ..< resolutionX {
|
||||
if let gridView = GridView(contentView: self.contentViewImpl, gridPosition: CGPoint(x: CGFloat(x) / CGFloat(resolutionX), y: CGFloat(y) / CGFloat(resolutionY))) {
|
||||
gridView.isUserInteractionEnabled = false
|
||||
gridViews.append(gridView)
|
||||
self.addSubview(gridView)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.gridViews = gridViews*/
|
||||
}
|
||||
|
||||
public func update(size: CGSize, transition: ComponentTransition) {
|
||||
self.size = size
|
||||
if size.width <= 0.0 || size.height <= 0.0 {
|
||||
return
|
||||
}
|
||||
|
||||
self.updateGrid(resolutionX: max(2, Int(size.width / 100.0)), resolutionY: max(2, Int(size.height / 100.0)))
|
||||
guard let resolution = self.resolution, let meshView = self.meshView else {
|
||||
return
|
||||
}
|
||||
|
||||
meshView.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
//let pixelStep = CGPoint(x: CGFloat(resolution.x) * 0.33, y: CGFloat(resolution.y) * 0.33)
|
||||
let pixelStep = CGPoint()
|
||||
let itemSize = CGSize(width: size.width / CGFloat(resolution.x), height: size.height / CGFloat(resolution.y))
|
||||
|
||||
let params = RippleParams(amplitude: 22.0, frequency: 15.0, decay: 8.0, speed: 1400.0)
|
||||
|
||||
var instanceBounds: [CGRect] = []
|
||||
var instancePositions: [CGPoint] = []
|
||||
var instanceTransforms: [CATransform3D] = []
|
||||
|
||||
for y in 0 ..< resolution.y {
|
||||
for x in 0 ..< resolution.x {
|
||||
let gridPosition = CGPoint(x: CGFloat(x) / CGFloat(resolution.x), y: CGFloat(y) / CGFloat(resolution.y))
|
||||
|
||||
let sourceRect = CGRect(origin: CGPoint(x: gridPosition.x * (size.width + pixelStep.x), y: gridPosition.y * (size.height + pixelStep.y)), size: itemSize)
|
||||
|
||||
instanceBounds.append(sourceRect)
|
||||
instancePositions.append(sourceRect.center)
|
||||
|
||||
//gridView.bounds = CGRect(origin: CGPoint(), size: sourceRect.size)
|
||||
//gridView.update(containerSize: size, rect: sourceRect, transition: transition)
|
||||
|
||||
let initialTopLeft = CGPoint(x: sourceRect.minX, y: sourceRect.minY)
|
||||
let initialTopRight = CGPoint(x: sourceRect.maxX, y: sourceRect.minY)
|
||||
let initialBottomLeft = CGPoint(x: sourceRect.minX, y: sourceRect.maxY)
|
||||
let initialBottomRight = CGPoint(x: sourceRect.maxX, y: sourceRect.maxY)
|
||||
|
||||
var topLeft = initialTopLeft
|
||||
var topRight = initialTopRight
|
||||
var bottomLeft = initialBottomLeft
|
||||
var bottomRight = initialBottomRight
|
||||
|
||||
if let startPoint = self.startPoint {
|
||||
topLeft = transformCoordinate(position: topLeft, origin: startPoint, time: self.timeValue, params: params)
|
||||
topRight = transformCoordinate(position: topRight, origin: startPoint, time: self.timeValue, params: params)
|
||||
bottomLeft = transformCoordinate(position: bottomLeft, origin: startPoint, time: self.timeValue, params: params)
|
||||
bottomRight = transformCoordinate(position: bottomRight, origin: startPoint, time: self.timeValue, params: params)
|
||||
}
|
||||
|
||||
let distanceTopLeft = length(topLeft - initialTopLeft)
|
||||
let distanceTopRight = length(topRight - initialTopRight)
|
||||
let distanceBottomLeft = length(bottomLeft - initialBottomLeft)
|
||||
let distanceBottomRight = length(bottomRight - initialBottomRight)
|
||||
var maxDistance = max(distanceTopLeft, distanceTopRight)
|
||||
maxDistance = max(maxDistance, distanceBottomLeft)
|
||||
maxDistance = max(maxDistance, distanceBottomRight)
|
||||
|
||||
let transform = rectToQuad(rect: CGRect(origin: CGPoint(), size: itemSize), quadTL: topLeft - initialTopLeft, quadTR: topRight - initialTopLeft, quadBL: bottomLeft - initialTopLeft, quadBR: bottomRight - initialTopLeft)
|
||||
instanceTransforms.append(transform)
|
||||
|
||||
let isActive: Bool
|
||||
if maxDistance <= 0.5 {
|
||||
//gridView.layer.transform = CATransform3DIdentity
|
||||
isActive = false
|
||||
} else {
|
||||
let _ = transform
|
||||
//gridView.layer.transform = transform
|
||||
isActive = true
|
||||
}
|
||||
let _ = isActive
|
||||
}
|
||||
}
|
||||
|
||||
instanceBounds.withUnsafeMutableBufferPointer { buffer in
|
||||
meshView.instanceBounds = buffer.baseAddress!
|
||||
}
|
||||
instancePositions.withUnsafeMutableBufferPointer { buffer in
|
||||
meshView.instancePositions = buffer.baseAddress!
|
||||
}
|
||||
instanceTransforms.withUnsafeMutableBufferPointer { buffer in
|
||||
meshView.instanceTransforms = buffer.baseAddress!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if self.alpha.isZero || self.isHidden || !self.isUserInteractionEnabled {
|
||||
return nil
|
||||
}
|
||||
for view in self.contentView.subviews.reversed() {
|
||||
if let result = view.hitTest(self.convert(point, to: view), with: event), result.isUserInteractionEnabled {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
let result = super.hitTest(point, with: event)
|
||||
if result != self {
|
||||
return result
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class SpaceWarpView3: UIView, SpaceWarpView {
|
||||
private final class GridView: UIView {
|
||||
let cloneView: PortalView
|
||||
let gridPosition: CGPoint
|
||||
|
||||
init?(contentView: PortalSourceView, gridPosition: CGPoint) {
|
||||
self.gridPosition = gridPosition
|
||||
|
||||
guard let cloneView = PortalView(matchPosition: false) else {
|
||||
return nil
|
||||
}
|
||||
self.cloneView = cloneView
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.layer.anchorPoint = CGPoint(x: 0.0, y: 0.0)
|
||||
|
||||
self.clipsToBounds = true
|
||||
self.isUserInteractionEnabled = false
|
||||
self.addSubview(cloneView.view)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func updateIsActive(contentView: PortalSourceView, isActive: Bool) {
|
||||
if isActive {
|
||||
contentView.addPortal(view: self.cloneView)
|
||||
} else {
|
||||
contentView.removePortal(view: self.cloneView)
|
||||
}
|
||||
}
|
||||
|
||||
func update(containerSize: CGSize, rect: CGRect, transition: ComponentTransition) {
|
||||
transition.setFrame(view: self.cloneView.view, frame: CGRect(origin: CGPoint(x: -rect.minX - containerSize.width * 0.5, y: -rect.minY - containerSize.height * 0.5), size: CGSize(width: containerSize.width, height: containerSize.height)))
|
||||
}
|
||||
}
|
||||
|
||||
private var gridViews: [GridView] = []
|
||||
|
||||
public var contentView: UIView {
|
||||
return self.contentViewSource
|
||||
}
|
||||
|
||||
private let contentViewSource: UIView
|
||||
private var currentCloneView: UIView?
|
||||
private let contentViewImpl: PortalSourceView
|
||||
|
||||
private var link: SharedDisplayLinkDriver.Link?
|
||||
private var startPoint: CGPoint?
|
||||
|
||||
private var timeValue: CGFloat = 0.0
|
||||
private var currentActiveViews: Int = 0
|
||||
|
||||
private var resolution: (x: Int, y: Int)?
|
||||
private var size: CGSize?
|
||||
|
||||
override public init(frame: CGRect) {
|
||||
self.contentViewSource = UIView()
|
||||
self.contentViewImpl = PortalSourceView()
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.contentViewSource)
|
||||
self.addSubview(self.contentViewImpl)
|
||||
|
||||
if self.link == nil {
|
||||
self.link = SharedDisplayLinkDriver.shared.add(framesPerSecond: .max, { [weak self] deltaTime in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.timeValue += deltaTime * (1.0 / CGFloat(UIView.animationDurationFactor()))
|
||||
|
||||
if let size = self.size {
|
||||
self.update(size: size, transition: .immediate)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public func trigger(at point: CGPoint) {
|
||||
self.startPoint = point
|
||||
self.timeValue = 0.0
|
||||
}
|
||||
|
||||
private func updateGrid(resolutionX: Int, resolutionY: Int) {
|
||||
if let resolution = self.resolution, resolution.x == resolutionX, resolution.y == resolutionY {
|
||||
return
|
||||
}
|
||||
self.resolution = (resolutionX, resolutionY)
|
||||
|
||||
for gridView in self.gridViews {
|
||||
gridView.removeFromSuperview()
|
||||
}
|
||||
|
||||
var gridViews: [GridView] = []
|
||||
for y in 0 ..< resolutionY {
|
||||
for x in 0 ..< resolutionX {
|
||||
if let gridView = GridView(contentView: self.contentViewImpl, gridPosition: CGPoint(x: CGFloat(x) / CGFloat(resolutionX), y: CGFloat(y) / CGFloat(resolutionY))) {
|
||||
gridView.isUserInteractionEnabled = false
|
||||
gridView.isHidden = true
|
||||
gridViews.append(gridView)
|
||||
self.addSubview(gridView)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.gridViews = gridViews
|
||||
}
|
||||
|
||||
public func update(size: CGSize, transition: ComponentTransition) {
|
||||
if let currentCloneView = self.currentCloneView {
|
||||
currentCloneView.removeFromSuperview()
|
||||
self.currentCloneView = nil
|
||||
}
|
||||
if let cloneView = self.contentViewSource.resizableSnapshotView(from: CGRect(origin: CGPoint(), size: size), afterScreenUpdates: false, withCapInsets: UIEdgeInsets()) {
|
||||
self.currentCloneView = cloneView
|
||||
self.contentViewImpl.addSubview(cloneView)
|
||||
}
|
||||
|
||||
self.size = size
|
||||
if size.width <= 0.0 || size.height <= 0.0 {
|
||||
return
|
||||
}
|
||||
|
||||
self.updateGrid(resolutionX: max(2, Int(size.width / 50.0)), resolutionY: max(2, Int(size.height / 50.0)))
|
||||
guard let resolution = self.resolution else {
|
||||
return
|
||||
}
|
||||
|
||||
if self.timeValue >= 3.0 {
|
||||
return
|
||||
}
|
||||
|
||||
let pixelStep = CGPoint()
|
||||
let itemSize = CGSize(width: size.width / CGFloat(resolution.x), height: size.height / CGFloat(resolution.y))
|
||||
|
||||
let params = RippleParams(amplitude: 22.0, frequency: 15.0, decay: 8.0, speed: 1400.0)
|
||||
|
||||
var activeViews = 0
|
||||
for gridView in self.gridViews {
|
||||
let sourceRect = CGRect(origin: CGPoint(x: gridView.gridPosition.x * (size.width + pixelStep.x), y: gridView.gridPosition.y * (size.height + pixelStep.y)), size: itemSize)
|
||||
|
||||
gridView.bounds = CGRect(origin: CGPoint(), size: sourceRect.size)
|
||||
gridView.update(containerSize: size, rect: sourceRect, transition: transition)
|
||||
|
||||
let initialTopLeft = CGPoint(x: sourceRect.minX, y: sourceRect.minY)
|
||||
let initialTopRight = CGPoint(x: sourceRect.maxX, y: sourceRect.minY)
|
||||
let initialBottomLeft = CGPoint(x: sourceRect.minX, y: sourceRect.maxY)
|
||||
let initialBottomRight = CGPoint(x: sourceRect.maxX, y: sourceRect.maxY)
|
||||
|
||||
var topLeft = initialTopLeft
|
||||
var topRight = initialTopRight
|
||||
var bottomLeft = initialBottomLeft
|
||||
var bottomRight = initialBottomRight
|
||||
|
||||
if let startPoint = self.startPoint {
|
||||
topLeft = transformCoordinate(position: topLeft, origin: startPoint, time: self.timeValue, params: params)
|
||||
topRight = transformCoordinate(position: topRight, origin: startPoint, time: self.timeValue, params: params)
|
||||
bottomLeft = transformCoordinate(position: bottomLeft, origin: startPoint, time: self.timeValue, params: params)
|
||||
bottomRight = transformCoordinate(position: bottomRight, origin: startPoint, time: self.timeValue, params: params)
|
||||
}
|
||||
|
||||
let distanceTopLeft = length(topLeft - initialTopLeft)
|
||||
let distanceTopRight = length(topRight - initialTopRight)
|
||||
let distanceBottomLeft = length(bottomLeft - initialBottomLeft)
|
||||
let distanceBottomRight = length(bottomRight - initialBottomRight)
|
||||
var maxDistance = max(distanceTopLeft, distanceTopRight)
|
||||
maxDistance = max(maxDistance, distanceBottomLeft)
|
||||
maxDistance = max(maxDistance, distanceBottomRight)
|
||||
|
||||
let isActive: Bool
|
||||
if maxDistance <= 0.5 {
|
||||
gridView.layer.transform = CATransform3DIdentity
|
||||
isActive = true
|
||||
activeViews += 1
|
||||
} else {
|
||||
let transform = rectToQuad(rect: CGRect(origin: CGPoint(), size: itemSize), quadTL: topLeft, quadTR: topRight, quadBL: bottomLeft, quadBR: bottomRight)
|
||||
gridView.layer.transform = transform
|
||||
isActive = true
|
||||
activeViews += 1
|
||||
}
|
||||
if gridView.isHidden != !isActive {
|
||||
gridView.isHidden = !isActive
|
||||
gridView.updateIsActive(contentView: self.contentViewImpl, isActive: isActive)
|
||||
}
|
||||
}
|
||||
|
||||
if self.currentActiveViews != activeViews {
|
||||
self.currentActiveViews = activeViews
|
||||
#if DEBUG
|
||||
print("SpaceWarpView: activeViews = \(activeViews)")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if self.alpha.isZero || self.isHidden || !self.isUserInteractionEnabled {
|
||||
return nil
|
||||
}
|
||||
for view in self.contentView.subviews.reversed() {
|
||||
if let result = view.hitTest(self.convert(point, to: view), with: event), result.isUserInteractionEnabled {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
let result = super.hitTest(point, with: event)
|
||||
if result != self {
|
||||
return result
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class SpaceWarpView4: UIView, SpaceWarpView {
|
||||
public var contentView: UIView {
|
||||
return self.contentViewSource
|
||||
}
|
||||
|
||||
private let contentViewSource: UIView
|
||||
private var currentCloneView: UIView?
|
||||
private var meshView: STCMeshView?
|
||||
private let fpsView: FPSView
|
||||
|
||||
private var link: SharedDisplayLinkDriver.Link?
|
||||
private var startPoint: CGPoint?
|
||||
|
||||
private var timeValue: CGFloat = 0.0
|
||||
|
||||
private var resolution: (x: Int, y: Int)?
|
||||
private var size: CGSize?
|
||||
|
||||
override public init(frame: CGRect) {
|
||||
self.contentViewSource = UIView()
|
||||
self.fpsView = FPSView(frame: CGRect(origin: CGPoint(x: 4.0, y: 40.0), size: CGSize()))
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.contentViewSource)
|
||||
self.addSubview(self.fpsView)
|
||||
|
||||
if self.link == nil {
|
||||
self.link = SharedDisplayLinkDriver.shared.add(framesPerSecond: .max, { [weak self] deltaTime in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.timeValue += deltaTime * (1.0 / CGFloat(UIView.animationDurationFactor()))
|
||||
|
||||
if let size = self.size {
|
||||
self.update(size: size, transition: .immediate)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public func trigger(at point: CGPoint) {
|
||||
self.startPoint = point
|
||||
self.timeValue = 0.0
|
||||
}
|
||||
|
||||
private func updateGrid(resolutionX: Int, resolutionY: Int) {
|
||||
if let resolution = self.resolution, resolution.x == resolutionX, resolution.y == resolutionY {
|
||||
return
|
||||
}
|
||||
self.resolution = (resolutionX, resolutionY)
|
||||
|
||||
if let meshView = self.meshView {
|
||||
self.meshView = nil
|
||||
meshView.removeFromSuperview()
|
||||
}
|
||||
|
||||
let meshView = STCMeshView(frame: CGRect())
|
||||
self.meshView = meshView
|
||||
self.insertSubview(meshView, aboveSubview: self.contentViewSource)
|
||||
|
||||
meshView.instanceCount = resolutionX * resolutionY
|
||||
}
|
||||
|
||||
public func update(size: CGSize, transition: ComponentTransition) {
|
||||
self.size = size
|
||||
if size.width <= 0.0 || size.height <= 0.0 {
|
||||
return
|
||||
}
|
||||
|
||||
self.fpsView.update()
|
||||
|
||||
self.updateGrid(resolutionX: max(2, Int(size.width / 40.0)), resolutionY: max(2, Int(size.height / 40.0)))
|
||||
guard let resolution = self.resolution, let meshView = self.meshView else {
|
||||
return
|
||||
}
|
||||
|
||||
if let currentCloneView = self.currentCloneView {
|
||||
currentCloneView.removeFromSuperview()
|
||||
self.currentCloneView = nil
|
||||
}
|
||||
if let cloneView = self.contentViewSource.resizableSnapshotView(from: CGRect(origin: CGPoint(), size: size), afterScreenUpdates: false, withCapInsets: UIEdgeInsets()) {
|
||||
self.currentCloneView = cloneView
|
||||
meshView.contentView.addSubview(cloneView)
|
||||
}
|
||||
|
||||
meshView.frame = CGRect(origin: CGPoint(), size: size)
|
||||
|
||||
let pixelStep = CGPoint()
|
||||
let itemSize = CGSize(width: size.width / CGFloat(resolution.x), height: size.height / CGFloat(resolution.y))
|
||||
|
||||
let params = RippleParams(amplitude: 26.0, frequency: 15.0, decay: 8.0, speed: 1400.0)
|
||||
|
||||
var instanceBounds: [CGRect] = []
|
||||
var instancePositions: [CGPoint] = []
|
||||
var instanceTransforms: [CATransform3D] = []
|
||||
|
||||
for y in 0 ..< resolution.y {
|
||||
for x in 0 ..< resolution.x {
|
||||
let gridPosition = CGPoint(x: CGFloat(x) / CGFloat(resolution.x), y: CGFloat(y) / CGFloat(resolution.y))
|
||||
|
||||
let sourceRect = CGRect(origin: CGPoint(x: gridPosition.x * (size.width + pixelStep.x), y: gridPosition.y * (size.height + pixelStep.y)), size: itemSize)
|
||||
|
||||
instanceBounds.append(sourceRect)
|
||||
instancePositions.append(sourceRect.center)
|
||||
|
||||
let initialTopLeft = CGPoint(x: sourceRect.minX, y: sourceRect.minY)
|
||||
let initialTopRight = CGPoint(x: sourceRect.maxX, y: sourceRect.minY)
|
||||
let initialBottomLeft = CGPoint(x: sourceRect.minX, y: sourceRect.maxY)
|
||||
let initialBottomRight = CGPoint(x: sourceRect.maxX, y: sourceRect.maxY)
|
||||
|
||||
var topLeft = initialTopLeft
|
||||
var topRight = initialTopRight
|
||||
var bottomLeft = initialBottomLeft
|
||||
var bottomRight = initialBottomRight
|
||||
|
||||
if let startPoint = self.startPoint {
|
||||
topLeft = transformCoordinate(position: topLeft, origin: startPoint, time: self.timeValue, params: params)
|
||||
topRight = transformCoordinate(position: topRight, origin: startPoint, time: self.timeValue, params: params)
|
||||
bottomLeft = transformCoordinate(position: bottomLeft, origin: startPoint, time: self.timeValue, params: params)
|
||||
bottomRight = transformCoordinate(position: bottomRight, origin: startPoint, time: self.timeValue, params: params)
|
||||
}
|
||||
|
||||
let distanceTopLeft = length(topLeft - initialTopLeft)
|
||||
let distanceTopRight = length(topRight - initialTopRight)
|
||||
let distanceBottomLeft = length(bottomLeft - initialBottomLeft)
|
||||
let distanceBottomRight = length(bottomRight - initialBottomRight)
|
||||
var maxDistance = max(distanceTopLeft, distanceTopRight)
|
||||
maxDistance = max(maxDistance, distanceBottomLeft)
|
||||
maxDistance = max(maxDistance, distanceBottomRight)
|
||||
|
||||
let transform = transformToFitQuad(frame: sourceRect, topLeft: topLeft, topRight: topRight, bottomLeft: bottomLeft, bottomRight: bottomRight)
|
||||
instanceTransforms.append(transform)
|
||||
|
||||
let isActive: Bool
|
||||
if maxDistance <= 0.5 {
|
||||
//gridView.layer.transform = CATransform3DIdentity
|
||||
isActive = false
|
||||
} else {
|
||||
let _ = transform
|
||||
//gridView.layer.transform = transform
|
||||
isActive = true
|
||||
}
|
||||
let _ = isActive
|
||||
}
|
||||
}
|
||||
|
||||
instanceBounds.withUnsafeMutableBufferPointer { buffer in
|
||||
meshView.instanceBounds = buffer.baseAddress!
|
||||
}
|
||||
instancePositions.withUnsafeMutableBufferPointer { buffer in
|
||||
meshView.instancePositions = buffer.baseAddress!
|
||||
}
|
||||
instanceTransforms.withUnsafeMutableBufferPointer { buffer in
|
||||
meshView.instanceTransforms = buffer.baseAddress!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
if self.alpha.isZero || self.isHidden || !self.isUserInteractionEnabled {
|
||||
return nil
|
||||
|
@ -98,16 +98,17 @@ class ChatNodeContainer: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
override init() {
|
||||
init(rippleEffect: Bool) {
|
||||
self.contentNodeImpl = ASDisplayNode()
|
||||
|
||||
super.init()
|
||||
|
||||
#if DEBUG && false
|
||||
self.setViewBlock({
|
||||
return SpaceWarpView(frame: CGRect())
|
||||
})
|
||||
#endif
|
||||
if rippleEffect {
|
||||
self.setViewBlock({
|
||||
return SpaceWarpView4(frame: CGRect())
|
||||
})
|
||||
self.contentNodeImpl.layer.allowsGroupOpacity = true
|
||||
}
|
||||
|
||||
(self.view as? SpaceWarpView)?.contentView.addSubnode(self.contentNodeImpl)
|
||||
}
|
||||
@ -154,7 +155,7 @@ class HistoryNodeContainer: ASDisplayNode {
|
||||
|
||||
#if DEBUG && false
|
||||
self.setViewBlock({
|
||||
return SpaceWarpView(frame: CGRect())
|
||||
return SpaceWarpView1(frame: CGRect())
|
||||
})
|
||||
#endif
|
||||
|
||||
@ -444,7 +445,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
|
||||
|
||||
self.backgroundNode = backgroundNode
|
||||
|
||||
self.contentContainerNode = ChatNodeContainer()
|
||||
self.contentContainerNode = ChatNodeContainer(rippleEffect: context.sharedContext.immediateExperimentalUISettings.rippleEffect)
|
||||
self.contentDimNode = ASDisplayNode()
|
||||
self.contentDimNode.isUserInteractionEnabled = false
|
||||
self.contentDimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.2)
|
||||
|
@ -38,7 +38,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
public var enableVoipTcp: Bool
|
||||
public var experimentalCompatibility: Bool
|
||||
public var enableDebugDataDisplay: Bool
|
||||
public var acceleratedStickers: Bool
|
||||
public var rippleEffect: Bool
|
||||
public var inlineStickers: Bool
|
||||
public var localTranscription: Bool
|
||||
public var enableReactionOverrides: Bool
|
||||
@ -74,7 +74,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
enableVoipTcp: false,
|
||||
experimentalCompatibility: false,
|
||||
enableDebugDataDisplay: false,
|
||||
acceleratedStickers: false,
|
||||
rippleEffect: false,
|
||||
inlineStickers: false,
|
||||
localTranscription: false,
|
||||
enableReactionOverrides: false,
|
||||
@ -111,7 +111,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
enableVoipTcp: Bool,
|
||||
experimentalCompatibility: Bool,
|
||||
enableDebugDataDisplay: Bool,
|
||||
acceleratedStickers: Bool,
|
||||
rippleEffect: Bool,
|
||||
inlineStickers: Bool,
|
||||
localTranscription: Bool,
|
||||
enableReactionOverrides: Bool,
|
||||
@ -145,7 +145,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
self.enableVoipTcp = enableVoipTcp
|
||||
self.experimentalCompatibility = experimentalCompatibility
|
||||
self.enableDebugDataDisplay = enableDebugDataDisplay
|
||||
self.acceleratedStickers = acceleratedStickers
|
||||
self.rippleEffect = rippleEffect
|
||||
self.inlineStickers = inlineStickers
|
||||
self.localTranscription = localTranscription
|
||||
self.enableReactionOverrides = enableReactionOverrides
|
||||
@ -183,7 +183,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
self.enableVoipTcp = (try container.decodeIfPresent(Int32.self, forKey: "enableVoipTcp") ?? 0) != 0
|
||||
self.experimentalCompatibility = (try container.decodeIfPresent(Int32.self, forKey: "experimentalCompatibility") ?? 0) != 0
|
||||
self.enableDebugDataDisplay = (try container.decodeIfPresent(Int32.self, forKey: "enableDebugDataDisplay") ?? 0) != 0
|
||||
self.acceleratedStickers = (try container.decodeIfPresent(Int32.self, forKey: "acceleratedStickers") ?? 0) != 0
|
||||
self.rippleEffect = (try container.decodeIfPresent(Int32.self, forKey: "rippleEffect") ?? 0) != 0
|
||||
self.inlineStickers = (try container.decodeIfPresent(Int32.self, forKey: "inlineStickers") ?? 0) != 0
|
||||
self.localTranscription = (try container.decodeIfPresent(Int32.self, forKey: "localTranscription") ?? 0) != 0
|
||||
self.enableReactionOverrides = try container.decodeIfPresent(Bool.self, forKey: "enableReactionOverrides") ?? false
|
||||
@ -221,7 +221,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
try container.encode((self.enableVoipTcp ? 1 : 0) as Int32, forKey: "enableVoipTcp")
|
||||
try container.encode((self.experimentalCompatibility ? 1 : 0) as Int32, forKey: "experimentalCompatibility")
|
||||
try container.encode((self.enableDebugDataDisplay ? 1 : 0) as Int32, forKey: "enableDebugDataDisplay")
|
||||
try container.encode((self.acceleratedStickers ? 1 : 0) as Int32, forKey: "acceleratedStickers")
|
||||
try container.encode((self.rippleEffect ? 1 : 0) as Int32, forKey: "rippleEffect")
|
||||
try container.encode((self.inlineStickers ? 1 : 0) as Int32, forKey: "inlineStickers")
|
||||
try container.encode((self.localTranscription ? 1 : 0) as Int32, forKey: "localTranscription")
|
||||
try container.encode(self.enableReactionOverrides, forKey: "enableReactionOverrides")
|
||||
|
Loading…
x
Reference in New Issue
Block a user